resolve conflicts
Change-Id: I87f854430f7083cd2f2e28b4ec4a7112ef6fe4f1
This commit is contained in:
@ -12,7 +12,7 @@ LOCAL_SRC_FILES := \
|
||||
$(call all-java-files-under, EnabledTestApp/src)
|
||||
|
||||
LOCAL_DX_FLAGS := --core-library
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := core-tests-supportlib android-common
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := core-tests-supportlib android-common frameworks-core-util-lib
|
||||
LOCAL_JAVA_LIBRARIES := android.test.runner
|
||||
LOCAL_PACKAGE_NAME := FrameworksCoreTests
|
||||
|
||||
|
@ -36,12 +36,16 @@
|
||||
android:description="@string/permdesc_testDenied" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
|
||||
<uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
|
||||
<uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
|
||||
<uses-permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE" />
|
||||
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
|
@ -0,0 +1,888 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.database.Cursor;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.DownloadManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.DownloadManager.Query;
|
||||
import android.net.DownloadManager.Request;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Environment;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.SystemClock;
|
||||
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
|
||||
import android.provider.Settings;
|
||||
import android.test.InstrumentationTestCase;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Vector;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
|
||||
import coretestutils.http.MockResponse;
|
||||
import coretestutils.http.MockWebServer;
|
||||
|
||||
/**
|
||||
* Base class for Instrumented tests for the Download Manager.
|
||||
*/
|
||||
public class DownloadManagerBaseTest extends InstrumentationTestCase {
|
||||
|
||||
protected DownloadManager mDownloadManager = null;
|
||||
protected MockWebServer mServer = null;
|
||||
protected String mFileType = "text/plain";
|
||||
protected Context mContext = null;
|
||||
protected static final int DEFAULT_FILE_SIZE = 130 * 1024; // 130kb
|
||||
protected static final int FILE_BLOCK_READ_SIZE = 1024 * 1024;
|
||||
|
||||
protected static final String LOG_TAG = "android.net.DownloadManagerBaseTest";
|
||||
protected static final int HTTP_OK = 200;
|
||||
protected static final int HTTP_PARTIAL_CONTENT = 206;
|
||||
protected static final int HTTP_NOT_FOUND = 404;
|
||||
protected static final int HTTP_SERVICE_UNAVAILABLE = 503;
|
||||
protected String DEFAULT_FILENAME = "somefile.txt";
|
||||
|
||||
protected static final int DEFAULT_MAX_WAIT_TIME = 2 * 60 * 1000; // 2 minutes
|
||||
protected static final int DEFAULT_WAIT_POLL_TIME = 5 * 1000; // 5 seconds
|
||||
|
||||
protected static final int WAIT_FOR_DOWNLOAD_POLL_TIME = 1 * 1000; // 1 second
|
||||
protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 5 * 60 * 1000; // 5 minutes
|
||||
|
||||
// Just a few popular file types used to return from a download
|
||||
protected enum DownloadFileType {
|
||||
PLAINTEXT,
|
||||
APK,
|
||||
GIF,
|
||||
GARBAGE,
|
||||
UNRECOGNIZED,
|
||||
ZIP
|
||||
}
|
||||
|
||||
protected enum DataType {
|
||||
TEXT,
|
||||
BINARY
|
||||
}
|
||||
|
||||
public static class LoggingRng extends Random {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Creates RNG with self-generated seed value.
|
||||
*/
|
||||
public LoggingRng() {
|
||||
this(SystemClock.uptimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Creats RNG with given initial seed value
|
||||
|
||||
* @param seed The initial seed value
|
||||
*/
|
||||
public LoggingRng(long seed) {
|
||||
super(seed);
|
||||
Log.i(LOG_TAG, "Seeding RNG with value: " + seed);
|
||||
}
|
||||
}
|
||||
|
||||
public static class MultipleDownloadsCompletedReceiver extends BroadcastReceiver {
|
||||
private volatile int mNumDownloadsCompleted = 0;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equalsIgnoreCase(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
|
||||
++mNumDownloadsCompleted;
|
||||
Log.i(LOG_TAG, "MultipleDownloadsCompletedReceiver got intent: " +
|
||||
intent.getAction() + " --> total count: " + mNumDownloadsCompleted);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of times the {@link #onReceive} callback has been called for the
|
||||
* {@link DownloadManager.ACTION_DOWNLOAD_COMPLETED} action, indicating the number of
|
||||
* downloads completed thus far.
|
||||
*
|
||||
* @return the number of downloads completed so far.
|
||||
*/
|
||||
public int numDownloadsCompleted() {
|
||||
return mNumDownloadsCompleted;
|
||||
}
|
||||
}
|
||||
|
||||
public static class WiFiChangedReceiver extends BroadcastReceiver {
|
||||
private Context mContext = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Sets the current state of WiFi.
|
||||
*
|
||||
* @param context The current app {@link Context}.
|
||||
*/
|
||||
public WiFiChangedReceiver(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equalsIgnoreCase(ConnectivityManager.CONNECTIVITY_ACTION)) {
|
||||
Log.i(LOG_TAG, "ConnectivityManager state change: " + intent.getAction());
|
||||
synchronized (this) {
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current state of WiFi.
|
||||
*
|
||||
* @return Returns true if WiFi is on, false otherwise.
|
||||
*/
|
||||
public boolean getWiFiIsOn() {
|
||||
ConnectivityManager connManager = (ConnectivityManager)mContext.getSystemService(
|
||||
Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo info = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
|
||||
return info.isConnected();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
mContext = getInstrumentation().getContext();
|
||||
mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
mServer = new MockWebServer();
|
||||
// Note: callers overriding this should call mServer.play() with the desired port #
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to enqueue a response from the MockWebServer.
|
||||
*
|
||||
* @param status The HTTP status code to return for this response
|
||||
* @param body The body to return in this response
|
||||
* @return Returns the mock web server response that was queued (which can be modified)
|
||||
*/
|
||||
protected MockResponse enqueueResponse(int status, byte[] body) {
|
||||
return doEnqueueResponse(status).setBody(body);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to enqueue a response from the MockWebServer.
|
||||
*
|
||||
* @param status The HTTP status code to return for this response
|
||||
* @param bodyFile The body to return in this response
|
||||
* @return Returns the mock web server response that was queued (which can be modified)
|
||||
*/
|
||||
protected MockResponse enqueueResponse(int status, File bodyFile) {
|
||||
return doEnqueueResponse(status).setBody(bodyFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for enqueue'ing a response from the MockWebServer.
|
||||
*
|
||||
* @param status The HTTP status code to return for this response
|
||||
* @return Returns the mock web server response that was queued (which can be modified)
|
||||
*/
|
||||
protected MockResponse doEnqueueResponse(int status) {
|
||||
MockResponse response = new MockResponse().setResponseCode(status);
|
||||
response.addHeader("Content-type", mFileType);
|
||||
mServer.enqueue(response);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to generate a random blob of bytes.
|
||||
*
|
||||
* @param size The size of the data to generate
|
||||
* @param type The type of data to generate: currently, one of {@link DataType.TEXT} or
|
||||
* {@link DataType.BINARY}.
|
||||
* @return The random data that is generated.
|
||||
*/
|
||||
protected byte[] generateData(int size, DataType type) {
|
||||
return generateData(size, type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to generate a random blob of bytes using a given RNG.
|
||||
*
|
||||
* @param size The size of the data to generate
|
||||
* @param type The type of data to generate: currently, one of {@link DataType.TEXT} or
|
||||
* {@link DataType.BINARY}.
|
||||
* @param rng (optional) The RNG to use; pass null to use
|
||||
* @return The random data that is generated.
|
||||
*/
|
||||
protected byte[] generateData(int size, DataType type, Random rng) {
|
||||
int min = Byte.MIN_VALUE;
|
||||
int max = Byte.MAX_VALUE;
|
||||
|
||||
// Only use chars in the HTTP ASCII printable character range for Text
|
||||
if (type == DataType.TEXT) {
|
||||
min = 32;
|
||||
max = 126;
|
||||
}
|
||||
byte[] result = new byte[size];
|
||||
Log.i(LOG_TAG, "Generating data of size: " + size);
|
||||
|
||||
if (rng == null) {
|
||||
rng = new LoggingRng();
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
result[i] = (byte) (min + rng.nextInt(max - min + 1));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to verify the size of a file.
|
||||
*
|
||||
* @param pfd The input file to compare the size of
|
||||
* @param size The expected size of the file
|
||||
*/
|
||||
protected void verifyFileSize(ParcelFileDescriptor pfd, long size) {
|
||||
assertEquals(pfd.getStatSize(), size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to verify the contents of a downloaded file versus a byte[].
|
||||
*
|
||||
* @param actual The file of whose contents to verify
|
||||
* @param expected The data we expect to find in the aforementioned file
|
||||
* @throws IOException if there was a problem reading from the file
|
||||
*/
|
||||
protected void verifyFileContents(ParcelFileDescriptor actual, byte[] expected)
|
||||
throws IOException {
|
||||
AutoCloseInputStream input = new ParcelFileDescriptor.AutoCloseInputStream(actual);
|
||||
long fileSize = actual.getStatSize();
|
||||
|
||||
assertTrue(fileSize <= Integer.MAX_VALUE);
|
||||
assertEquals(expected.length, fileSize);
|
||||
|
||||
byte[] actualData = new byte[expected.length];
|
||||
assertEquals(input.read(actualData), fileSize);
|
||||
compareByteArrays(actualData, expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to compare 2 byte arrays.
|
||||
*
|
||||
* @param actual The array whose data we want to verify
|
||||
* @param expected The array of data we expect to see
|
||||
*/
|
||||
protected void compareByteArrays(byte[] actual, byte[] expected) {
|
||||
assertEquals(actual.length, expected.length);
|
||||
int length = actual.length;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
// assert has a bit of overhead, so only do the assert when the values are not the same
|
||||
if (actual[i] != expected[i]) {
|
||||
fail("Byte arrays are not equal.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the contents of a downloaded file versus the contents of a File.
|
||||
*
|
||||
* @param pfd The file whose data we want to verify
|
||||
* @param file The file containing the data we expect to see in the aforementioned file
|
||||
* @throws IOException If there was a problem reading either of the two files
|
||||
*/
|
||||
protected void verifyFileContents(ParcelFileDescriptor pfd, File file) throws IOException {
|
||||
byte[] actual = new byte[FILE_BLOCK_READ_SIZE];
|
||||
byte[] expected = new byte[FILE_BLOCK_READ_SIZE];
|
||||
|
||||
AutoCloseInputStream input = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
|
||||
|
||||
assertEquals(file.length(), pfd.getStatSize());
|
||||
|
||||
DataInputStream inFile = new DataInputStream(new FileInputStream(file));
|
||||
int actualRead = 0;
|
||||
int expectedRead = 0;
|
||||
|
||||
while (((actualRead = input.read(actual)) != -1) &&
|
||||
((expectedRead = inFile.read(expected)) != -1)) {
|
||||
assertEquals(actualRead, expectedRead);
|
||||
compareByteArrays(actual, expected);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the MIME type of file that will be served from the mock server
|
||||
*
|
||||
* @param type The MIME type to return from the server
|
||||
*/
|
||||
protected void setServerMimeType(DownloadFileType type) {
|
||||
mFileType = getMimeMapping(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MIME content string for a given type
|
||||
*
|
||||
* @param type The MIME type to return
|
||||
* @return the String representation of that MIME content type
|
||||
*/
|
||||
protected String getMimeMapping(DownloadFileType type) {
|
||||
switch (type) {
|
||||
case APK:
|
||||
return "application/vnd.android.package-archive";
|
||||
case GIF:
|
||||
return "image/gif";
|
||||
case ZIP:
|
||||
return "application/x-zip-compressed";
|
||||
case GARBAGE:
|
||||
return "zip\\pidy/doo/da";
|
||||
case UNRECOGNIZED:
|
||||
return "application/new.undefined.type.of.app";
|
||||
}
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Uri that should be used to access the mock server
|
||||
*
|
||||
* @param filename The name of the file to try to retrieve from the mock server
|
||||
* @return the Uri to use for access the file on the mock server
|
||||
*/
|
||||
protected Uri getServerUri(String filename) throws Exception {
|
||||
URL url = mServer.getUrl("/" + filename);
|
||||
return Uri.parse(url.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Uri that should be used to access the mock server
|
||||
*
|
||||
* @param filename The name of the file to try to retrieve from the mock server
|
||||
* @return the Uri to use for access the file on the mock server
|
||||
*/
|
||||
protected void logDBColumnData(Cursor cursor, String column) {
|
||||
int index = cursor.getColumnIndex(column);
|
||||
Log.i(LOG_TAG, "columnName: " + column);
|
||||
Log.i(LOG_TAG, "columnValue: " + cursor.getString(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to create and register a new MultipleDownloadCompletedReciever
|
||||
*
|
||||
* This is used to track many simultaneous downloads by keeping count of all the downloads
|
||||
* that have completed.
|
||||
*
|
||||
* @return A new receiver that records and can be queried on how many downloads have completed.
|
||||
*/
|
||||
protected MultipleDownloadsCompletedReceiver registerNewMultipleDownloadsReceiver() {
|
||||
MultipleDownloadsCompletedReceiver receiver = new MultipleDownloadsCompletedReceiver();
|
||||
mContext.registerReceiver(receiver, new IntentFilter(
|
||||
DownloadManager.ACTION_DOWNLOAD_COMPLETE));
|
||||
return receiver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to verify a standard single-file download from the mock server, and clean up after
|
||||
* verification
|
||||
*
|
||||
* Note that this also calls the Download manager's remove, which cleans up the file from cache.
|
||||
*
|
||||
* @param requestId The id of the download to remove
|
||||
* @param fileData The data to verify the file contains
|
||||
*/
|
||||
protected void verifyAndCleanupSingleFileDownload(long requestId, byte[] fileData)
|
||||
throws Exception {
|
||||
int fileSize = fileData.length;
|
||||
ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(requestId);
|
||||
Cursor cursor = mDownloadManager.query(new Query().setFilterById(requestId));
|
||||
|
||||
try {
|
||||
assertEquals(1, cursor.getCount());
|
||||
assertTrue(cursor.moveToFirst());
|
||||
|
||||
mServer.checkForExceptions();
|
||||
|
||||
verifyFileSize(pfd, fileSize);
|
||||
verifyFileContents(pfd, fileData);
|
||||
} finally {
|
||||
pfd.close();
|
||||
cursor.close();
|
||||
mDownloadManager.remove(requestId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables WiFi.
|
||||
*
|
||||
* Note: Needs the following permissions:
|
||||
* android.permission.ACCESS_WIFI_STATE
|
||||
* android.permission.CHANGE_WIFI_STATE
|
||||
* @param enable true if it should be enabled, false if it should be disabled
|
||||
*/
|
||||
protected void setWiFiStateOn(boolean enable) throws Exception {
|
||||
WifiManager manager = (WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
|
||||
|
||||
manager.setWifiEnabled(enable);
|
||||
|
||||
String timeoutMessage = "Timed out waiting for Wifi to be "
|
||||
+ (enable ? "enabled!" : "disabled!");
|
||||
|
||||
WiFiChangedReceiver receiver = new WiFiChangedReceiver(mContext);
|
||||
mContext.registerReceiver(receiver, new IntentFilter(
|
||||
ConnectivityManager.CONNECTIVITY_ACTION));
|
||||
|
||||
synchronized (receiver) {
|
||||
long timeoutTime = SystemClock.elapsedRealtime() + DEFAULT_MAX_WAIT_TIME;
|
||||
boolean timedOut = false;
|
||||
|
||||
while (receiver.getWiFiIsOn() != enable && !timedOut) {
|
||||
try {
|
||||
receiver.wait(DEFAULT_MAX_WAIT_TIME);
|
||||
|
||||
if (SystemClock.elapsedRealtime() > timeoutTime) {
|
||||
timedOut = true;
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
// ignore InterruptedExceptions
|
||||
}
|
||||
}
|
||||
if (timedOut) {
|
||||
fail(timeoutMessage);
|
||||
}
|
||||
}
|
||||
assertEquals(enable, receiver.getWiFiIsOn());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to enables or disables airplane mode. If successful, it also broadcasts an intent
|
||||
* indicating that the mode has changed.
|
||||
*
|
||||
* Note: Needs the following permission:
|
||||
* android.permission.WRITE_SETTINGS
|
||||
* @param enable true if airplane mode should be ON, false if it should be OFF
|
||||
*/
|
||||
protected void setAirplaneModeOn(boolean enable) throws Exception {
|
||||
int state = enable ? 1 : 0;
|
||||
|
||||
// Change the system setting
|
||||
Settings.System.putInt(mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON,
|
||||
state);
|
||||
|
||||
String timeoutMessage = "Timed out waiting for airplane mode to be " +
|
||||
(enable ? "enabled!" : "disabled!");
|
||||
|
||||
// wait for airplane mode to change state
|
||||
int currentWaitTime = 0;
|
||||
while (Settings.System.getInt(mContext.getContentResolver(),
|
||||
Settings.System.AIRPLANE_MODE_ON, -1) != state) {
|
||||
timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME, DEFAULT_MAX_WAIT_TIME,
|
||||
timeoutMessage);
|
||||
}
|
||||
|
||||
// Post the intent
|
||||
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
|
||||
intent.putExtra("state", true);
|
||||
mContext.sendBroadcast(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to create a large file of random data on the SD card.
|
||||
*
|
||||
* @param filename (optional) The name of the file to create on the SD card; pass in null to
|
||||
* use a default temp filename.
|
||||
* @param type The type of file to create
|
||||
* @param subdirectory If not null, the subdirectory under the SD card where the file should go
|
||||
* @return The File that was created
|
||||
* @throws IOException if there was an error while creating the file.
|
||||
*/
|
||||
protected File createFileOnSD(String filename, long fileSize, DataType type,
|
||||
String subdirectory) throws IOException {
|
||||
|
||||
// Build up the file path and name
|
||||
String sdPath = Environment.getExternalStorageDirectory().getPath();
|
||||
StringBuilder fullPath = new StringBuilder(sdPath);
|
||||
if (subdirectory != null) {
|
||||
fullPath.append(File.separatorChar).append(subdirectory);
|
||||
}
|
||||
|
||||
File file = null;
|
||||
if (filename == null) {
|
||||
file = File.createTempFile("DMTEST_", null, new File(fullPath.toString()));
|
||||
}
|
||||
else {
|
||||
fullPath.append(File.separatorChar).append(filename);
|
||||
file = new File(fullPath.toString());
|
||||
file.createNewFile();
|
||||
}
|
||||
|
||||
// Fill the file with random data
|
||||
DataOutputStream output = new DataOutputStream(new FileOutputStream(file));
|
||||
final int CHUNK_SIZE = 1000000; // copy random data in 1000000-char chunks
|
||||
long remaining = fileSize;
|
||||
int nextChunkSize = CHUNK_SIZE;
|
||||
byte[] randomData = null;
|
||||
Random rng = new LoggingRng();
|
||||
|
||||
try {
|
||||
while (remaining > 0) {
|
||||
if (remaining < CHUNK_SIZE) {
|
||||
nextChunkSize = (int)remaining;
|
||||
remaining = 0;
|
||||
}
|
||||
else {
|
||||
remaining -= CHUNK_SIZE;
|
||||
}
|
||||
|
||||
randomData = generateData(nextChunkSize, type, rng);
|
||||
output.write(randomData);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(LOG_TAG, "Error writing to file " + file.getAbsolutePath());
|
||||
file.delete();
|
||||
throw e;
|
||||
} finally {
|
||||
output.close();
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to wait for a particular download to finish, or else a timeout to occur
|
||||
*
|
||||
* @param id The download id to query on (wait for)
|
||||
*/
|
||||
protected void waitForDownloadOrTimeout(long id) throws TimeoutException,
|
||||
InterruptedException {
|
||||
waitForDownloadOrTimeout(id, WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to wait for a particular download to finish, or else a timeout to occur
|
||||
*
|
||||
* @param id The download id to query on (wait for)
|
||||
* @param poll The amount of time to wait
|
||||
* @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
|
||||
*/
|
||||
protected void waitForDownloadOrTimeout(long id, long poll, long timeoutMillis)
|
||||
throws TimeoutException, InterruptedException {
|
||||
doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to wait for all downloads to finish, or else a specified timeout to occur
|
||||
*
|
||||
* @param poll The amount of time to wait
|
||||
* @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
|
||||
*/
|
||||
protected void waitForDownloadsOrTimeout(long poll, long timeoutMillis) throws TimeoutException,
|
||||
InterruptedException {
|
||||
doWaitForDownloadsOrTimeout(new Query(), poll, timeoutMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to wait for all downloads to finish, or else a timeout to occur, but does not throw
|
||||
*
|
||||
* @param id The id of the download to query against
|
||||
* @param poll The amount of time to wait
|
||||
* @param timeoutMillis The max time (in ms) to wait for the download(s) to complete
|
||||
* @return true if download completed successfully (didn't timeout), false otherwise
|
||||
*/
|
||||
protected boolean waitForDownloadOrTimeoutNoThrow(long id, long poll, long timeoutMillis) {
|
||||
try {
|
||||
doWaitForDownloadsOrTimeout(new Query().setFilterById(id), poll, timeoutMillis);
|
||||
} catch (TimeoutException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to synchronously wait, or timeout if the maximum threshold has been exceeded.
|
||||
*
|
||||
* @param currentTotalWaitTime The total time waited so far
|
||||
* @param poll The amount of time to wait
|
||||
* @param maxTimeoutMillis The total wait time threshold; if we've waited more than this long,
|
||||
* we timeout and fail
|
||||
* @param timedOutMessage The message to display in the failure message if we timeout
|
||||
* @return The new total amount of time we've waited so far
|
||||
* @throws TimeoutException if timed out waiting for SD card to mount
|
||||
*/
|
||||
protected int timeoutWait(int currentTotalWaitTime, long poll, long maxTimeoutMillis,
|
||||
String timedOutMessage) throws TimeoutException {
|
||||
long now = SystemClock.elapsedRealtime();
|
||||
long end = now + poll;
|
||||
|
||||
// if we get InterruptedException's, ignore them and just keep sleeping
|
||||
while (now < end) {
|
||||
try {
|
||||
Thread.sleep(end - now);
|
||||
} catch (InterruptedException e) {
|
||||
// ignore interrupted exceptions
|
||||
}
|
||||
now = SystemClock.elapsedRealtime();
|
||||
}
|
||||
|
||||
currentTotalWaitTime += poll;
|
||||
if (currentTotalWaitTime > maxTimeoutMillis) {
|
||||
throw new TimeoutException(timedOutMessage);
|
||||
}
|
||||
return currentTotalWaitTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to wait for all downloads to finish, or else a timeout to occur
|
||||
*
|
||||
* @param query The query to pass to the download manager
|
||||
* @param poll The poll time to wait between checks
|
||||
* @param timeoutMillis The max amount of time (in ms) to wait for the download(s) to complete
|
||||
*/
|
||||
protected void doWaitForDownloadsOrTimeout(Query query, long poll, long timeoutMillis)
|
||||
throws TimeoutException {
|
||||
int currentWaitTime = 0;
|
||||
while (true) {
|
||||
query.setFilterByStatus(DownloadManager.STATUS_PENDING | DownloadManager.STATUS_PAUSED
|
||||
| DownloadManager.STATUS_RUNNING);
|
||||
Cursor cursor = mDownloadManager.query(query);
|
||||
|
||||
try {
|
||||
// If we've finished the downloads then we're done
|
||||
if (cursor.getCount() == 0) {
|
||||
break;
|
||||
}
|
||||
currentWaitTime = timeoutWait(currentWaitTime, poll, timeoutMillis,
|
||||
"Timed out waiting for all downloads to finish");
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously waits for external store to be mounted (eg: SD Card).
|
||||
*
|
||||
* @throws InterruptedException if interrupted
|
||||
* @throws Exception if timed out waiting for SD card to mount
|
||||
*/
|
||||
protected void waitForExternalStoreMount() throws Exception {
|
||||
String extStorageState = Environment.getExternalStorageState();
|
||||
int currentWaitTime = 0;
|
||||
while (!extStorageState.equals(Environment.MEDIA_MOUNTED)) {
|
||||
Log.i(LOG_TAG, "Waiting for SD card...");
|
||||
currentWaitTime = timeoutWait(currentWaitTime, DEFAULT_WAIT_POLL_TIME,
|
||||
DEFAULT_MAX_WAIT_TIME, "Timed out waiting for SD Card to be ready!");
|
||||
extStorageState = Environment.getExternalStorageState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously waits for a download to start.
|
||||
*
|
||||
* @param dlRequest the download request id used by Download Manager to track the download.
|
||||
* @throws Exception if timed out while waiting for SD card to mount
|
||||
*/
|
||||
protected void waitForDownloadToStart(long dlRequest) throws Exception {
|
||||
Cursor cursor = getCursor(dlRequest);
|
||||
try {
|
||||
int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
|
||||
int value = cursor.getInt(columnIndex);
|
||||
int currentWaitTime = 0;
|
||||
|
||||
while (value != DownloadManager.STATUS_RUNNING &&
|
||||
(value != DownloadManager.STATUS_FAILED) &&
|
||||
(value != DownloadManager.STATUS_SUCCESSFUL)) {
|
||||
Log.i(LOG_TAG, "Waiting for download to start...");
|
||||
currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
|
||||
MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for download to start!");
|
||||
cursor.requery();
|
||||
assertTrue(cursor.moveToFirst());
|
||||
columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
|
||||
value = cursor.getInt(columnIndex);
|
||||
}
|
||||
assertFalse("Download failed immediately after start",
|
||||
value == DownloadManager.STATUS_FAILED);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously waits for a file to increase in size (such as to monitor that a download is
|
||||
* progressing).
|
||||
*
|
||||
* @param file The file whose size to track.
|
||||
* @throws Exception if timed out while waiting for the file to grow in size.
|
||||
*/
|
||||
protected void waitForFileToGrow(File file) throws Exception {
|
||||
int currentWaitTime = 0;
|
||||
|
||||
// File may not even exist yet, so wait until it does (or we timeout)
|
||||
while (!file.exists()) {
|
||||
Log.i(LOG_TAG, "Waiting for file to exist...");
|
||||
currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
|
||||
MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be created.");
|
||||
}
|
||||
|
||||
// Get original file size...
|
||||
long originalSize = file.length();
|
||||
|
||||
while (file.length() <= originalSize) {
|
||||
Log.i(LOG_TAG, "Waiting for file to be written to...");
|
||||
currentWaitTime = timeoutWait(currentWaitTime, WAIT_FOR_DOWNLOAD_POLL_TIME,
|
||||
MAX_WAIT_FOR_DOWNLOAD_TIME, "Timed out waiting for file to be written to.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to remove all downloads that are registered with the DL Manager.
|
||||
*
|
||||
* Note: This gives us a clean slate b/c it includes downloads that are pending, running,
|
||||
* paused, or have completed.
|
||||
*/
|
||||
protected void removeAllCurrentDownloads() {
|
||||
Log.i(LOG_TAG, "Removing all current registered downloads...");
|
||||
Cursor cursor = mDownloadManager.query(new Query());
|
||||
try {
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
int index = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
|
||||
long downloadId = cursor.getLong(index);
|
||||
|
||||
mDownloadManager.remove(downloadId);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to perform a standard enqueue of data to the mock server.
|
||||
*
|
||||
* @param body The body to return in the response from the server
|
||||
*/
|
||||
protected long doStandardEnqueue(byte[] body) throws Exception {
|
||||
// Prepare the mock server with a standard response
|
||||
enqueueResponse(HTTP_OK, body);
|
||||
return doCommonStandardEnqueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to perform a standard enqueue of data to the mock server.
|
||||
*
|
||||
* @param body The body to return in the response from the server, contained in the file
|
||||
*/
|
||||
protected long doStandardEnqueue(File body) throws Exception {
|
||||
// Prepare the mock server with a standard response
|
||||
enqueueResponse(HTTP_OK, body);
|
||||
return doCommonStandardEnqueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to do the additional steps (setting title and Uri of default filename) when
|
||||
* doing a standard enqueue request to the server.
|
||||
*/
|
||||
protected long doCommonStandardEnqueue() throws Exception {
|
||||
Uri uri = getServerUri(DEFAULT_FILENAME);
|
||||
Request request = new Request(uri);
|
||||
request.setTitle(DEFAULT_FILENAME);
|
||||
|
||||
long dlRequest = mDownloadManager.enqueue(request);
|
||||
Log.i(LOG_TAG, "request ID: " + dlRequest);
|
||||
return dlRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to verify an int value in a Cursor
|
||||
*
|
||||
* @param cursor The cursor containing the query results
|
||||
* @param columnName The name of the column to query
|
||||
* @param expected The expected int value
|
||||
*/
|
||||
protected void verifyInt(Cursor cursor, String columnName, int expected) {
|
||||
int index = cursor.getColumnIndex(columnName);
|
||||
int actual = cursor.getInt(index);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to verify a String value in a Cursor
|
||||
*
|
||||
* @param cursor The cursor containing the query results
|
||||
* @param columnName The name of the column to query
|
||||
* @param expected The expected String value
|
||||
*/
|
||||
protected void verifyString(Cursor cursor, String columnName, String expected) {
|
||||
int index = cursor.getColumnIndex(columnName);
|
||||
String actual = cursor.getString(index);
|
||||
Log.i(LOG_TAG, ": " + actual);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a query based on ID and returns a Cursor for the query.
|
||||
*
|
||||
* @param id The id of the download in DL Manager; pass -1 to query all downloads
|
||||
* @return A cursor for the query results
|
||||
*/
|
||||
protected Cursor getCursor(long id) throws Exception {
|
||||
Query query = new Query();
|
||||
if (id != -1) {
|
||||
query.setFilterById(id);
|
||||
}
|
||||
|
||||
Cursor cursor = mDownloadManager.query(query);
|
||||
int currentWaitTime = 0;
|
||||
|
||||
try {
|
||||
while (!cursor.moveToFirst()) {
|
||||
Thread.sleep(DEFAULT_WAIT_POLL_TIME);
|
||||
currentWaitTime += DEFAULT_WAIT_POLL_TIME;
|
||||
if (currentWaitTime > DEFAULT_MAX_WAIT_TIME) {
|
||||
fail("timed out waiting for a non-null query result");
|
||||
}
|
||||
cursor.requery();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
cursor.close();
|
||||
throw e;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.database.Cursor;
|
||||
import android.net.DownloadManager.Query;
|
||||
import android.net.DownloadManager.Request;
|
||||
import android.net.DownloadManagerBaseTest.DataType;
|
||||
import android.net.DownloadManagerBaseTest.MultipleDownloadsCompletedReceiver;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Environment;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.SystemClock;
|
||||
import android.test.InstrumentationTestCase;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Random;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
|
||||
import coretestutils.http.MockResponse;
|
||||
import coretestutils.http.MockWebServer;
|
||||
|
||||
/**
|
||||
* Integration tests of the DownloadManager API.
|
||||
*/
|
||||
public class DownloadManagerIntegrationTest extends DownloadManagerBaseTest {
|
||||
|
||||
private static String LOG_TAG = "android.net.DownloadManagerIntegrationTest";
|
||||
private static String PROHIBITED_DIRECTORY = "/system";
|
||||
protected MultipleDownloadsCompletedReceiver mReceiver = null;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
setWiFiStateOn(true);
|
||||
mServer.play();
|
||||
removeAllCurrentDownloads();
|
||||
mReceiver = registerNewMultipleDownloadsReceiver();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
setWiFiStateOn(true);
|
||||
|
||||
if (mReceiver != null) {
|
||||
mContext.unregisterReceiver(mReceiver);
|
||||
mReceiver = null;
|
||||
removeAllCurrentDownloads();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper that does the actual basic download verification.
|
||||
*/
|
||||
protected void doBasicDownload(byte[] blobData) throws Exception {
|
||||
long dlRequest = doStandardEnqueue(blobData);
|
||||
|
||||
// wait for the download to complete
|
||||
waitForDownloadOrTimeout(dlRequest);
|
||||
|
||||
verifyAndCleanupSingleFileDownload(dlRequest, blobData);
|
||||
assertEquals(1, mReceiver.numDownloadsCompleted());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a basic download of a binary file 500k in size.
|
||||
*/
|
||||
@LargeTest
|
||||
public void testBasicBinaryDownload() throws Exception {
|
||||
int fileSize = 500 * 1024; // 500k
|
||||
byte[] blobData = generateData(fileSize, DataType.BINARY);
|
||||
|
||||
doBasicDownload(blobData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the basic downloading of a text file 300000 bytes in size.
|
||||
*/
|
||||
@LargeTest
|
||||
public void testBasicTextDownload() throws Exception {
|
||||
int fileSize = 300000;
|
||||
byte[] blobData = generateData(fileSize, DataType.TEXT);
|
||||
|
||||
doBasicDownload(blobData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests when the server drops the connection after all headers (but before any data send).
|
||||
*/
|
||||
@LargeTest
|
||||
public void testDropConnection_headers() throws Exception {
|
||||
byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
|
||||
|
||||
MockResponse response = enqueueResponse(HTTP_OK, blobData);
|
||||
response.setCloseConnectionAfterHeader("content-length");
|
||||
long dlRequest = doCommonStandardEnqueue();
|
||||
|
||||
// Download will never complete when header is dropped
|
||||
boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest, DEFAULT_WAIT_POLL_TIME,
|
||||
DEFAULT_MAX_WAIT_TIME);
|
||||
|
||||
assertFalse(success);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we get an error code when the server drops the connection during a download.
|
||||
*/
|
||||
@LargeTest
|
||||
public void testServerDropConnection_body() throws Exception {
|
||||
byte[] blobData = generateData(25000, DataType.TEXT); // file size = 25000 bytes
|
||||
|
||||
MockResponse response = enqueueResponse(HTTP_OK, blobData);
|
||||
response.setCloseConnectionAfterXBytes(15382);
|
||||
long dlRequest = doCommonStandardEnqueue();
|
||||
waitForDownloadOrTimeout(dlRequest);
|
||||
|
||||
Cursor cursor = getCursor(dlRequest);
|
||||
try {
|
||||
verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
|
||||
verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE,
|
||||
DownloadManager.ERROR_CANNOT_RESUME);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
// Even tho the server drops the connection, we should still get a completed notification
|
||||
assertEquals(1, mReceiver.numDownloadsCompleted());
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to download several files simultaneously
|
||||
*/
|
||||
@LargeTest
|
||||
public void testMultipleDownloads() throws Exception {
|
||||
// need to be sure all current downloads have stopped first
|
||||
removeAllCurrentDownloads();
|
||||
int NUM_FILES = 50;
|
||||
int MAX_FILE_SIZE = 500 * 1024; // 500 kb
|
||||
|
||||
Random r = new LoggingRng();
|
||||
for (int i=0; i<NUM_FILES; ++i) {
|
||||
int size = r.nextInt(MAX_FILE_SIZE);
|
||||
byte[] blobData = generateData(size, DataType.TEXT);
|
||||
|
||||
Uri uri = getServerUri(DEFAULT_FILENAME);
|
||||
Request request = new Request(uri);
|
||||
request.setTitle(String.format("%s--%d", DEFAULT_FILENAME, i));
|
||||
|
||||
// Prepare the mock server with a standard response
|
||||
enqueueResponse(HTTP_OK, blobData);
|
||||
|
||||
Log.i(LOG_TAG, "request: " + i);
|
||||
mDownloadManager.enqueue(request);
|
||||
}
|
||||
|
||||
waitForDownloadsOrTimeout(WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME);
|
||||
Cursor cursor = mDownloadManager.query(new Query());
|
||||
try {
|
||||
assertEquals(NUM_FILES, cursor.getCount());
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
int status = cursor.getInt(cursor.getColumnIndex(
|
||||
DownloadManager.COLUMN_STATUS));
|
||||
String filename = cursor.getString(cursor.getColumnIndex(
|
||||
DownloadManager.COLUMN_URI));
|
||||
String errorString = String.format(
|
||||
"File %s failed to download successfully. Status code: %d",
|
||||
filename, status);
|
||||
assertEquals(errorString, DownloadManager.STATUS_SUCCESSFUL, status);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
assertEquals(NUM_FILES, mReceiver.numDownloadsCompleted());
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests trying to download to SD card when the file with same name already exists.
|
||||
*/
|
||||
@LargeTest
|
||||
public void testDownloadToExternal_fileExists() throws Exception {
|
||||
File existentFile = createFileOnSD(null, 1, DataType.TEXT, null);
|
||||
byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
|
||||
|
||||
// Prepare the mock server with a standard response
|
||||
enqueueResponse(HTTP_OK, blobData);
|
||||
|
||||
try {
|
||||
Uri uri = getServerUri(DEFAULT_FILENAME);
|
||||
Request request = new Request(uri);
|
||||
|
||||
Uri localUri = Uri.fromFile(existentFile);
|
||||
Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
|
||||
request.setDestinationUri(localUri);
|
||||
|
||||
long dlRequest = mDownloadManager.enqueue(request);
|
||||
|
||||
// wait for the download to complete
|
||||
waitForDownloadOrTimeout(dlRequest);
|
||||
Cursor cursor = getCursor(dlRequest);
|
||||
|
||||
try {
|
||||
verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
|
||||
verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE,
|
||||
DownloadManager.ERROR_FILE_ERROR);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
} finally {
|
||||
existentFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests trying to download a file to SD card.
|
||||
*/
|
||||
@LargeTest
|
||||
public void testDownloadToExternal() throws Exception {
|
||||
String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath();
|
||||
File downloadedFile = new File(localDownloadDirectory, DEFAULT_FILENAME);
|
||||
// make sure the file doesn't already exist in the directory
|
||||
downloadedFile.delete();
|
||||
|
||||
try {
|
||||
byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
|
||||
|
||||
// Prepare the mock server with a standard response
|
||||
enqueueResponse(HTTP_OK, blobData);
|
||||
|
||||
Uri uri = getServerUri(DEFAULT_FILENAME);
|
||||
Request request = new Request(uri);
|
||||
|
||||
Uri localUri = Uri.fromFile(downloadedFile);
|
||||
Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
|
||||
request.setDestinationUri(localUri);
|
||||
|
||||
long dlRequest = mDownloadManager.enqueue(request);
|
||||
|
||||
// wait for the download to complete
|
||||
waitForDownloadOrTimeout(dlRequest);
|
||||
|
||||
verifyAndCleanupSingleFileDownload(dlRequest, blobData);
|
||||
|
||||
assertEquals(1, mReceiver.numDownloadsCompleted());
|
||||
} finally {
|
||||
downloadedFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests trying to download a file to the system partition.
|
||||
*/
|
||||
@LargeTest
|
||||
public void testDownloadToProhibitedDirectory() throws Exception {
|
||||
File downloadedFile = new File(PROHIBITED_DIRECTORY, DEFAULT_FILENAME);
|
||||
try {
|
||||
byte[] blobData = generateData(DEFAULT_FILE_SIZE, DataType.TEXT);
|
||||
|
||||
// Prepare the mock server with a standard response
|
||||
enqueueResponse(HTTP_OK, blobData);
|
||||
|
||||
Uri uri = getServerUri(DEFAULT_FILENAME);
|
||||
Request request = new Request(uri);
|
||||
|
||||
Uri localUri = Uri.fromFile(downloadedFile);
|
||||
Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
|
||||
request.setDestinationUri(localUri);
|
||||
|
||||
try {
|
||||
mDownloadManager.enqueue(request);
|
||||
fail("Failed to throw SecurityException when trying to write to /system.");
|
||||
} catch (SecurityException s) {
|
||||
assertFalse(downloadedFile.exists());
|
||||
}
|
||||
} finally {
|
||||
// Just in case file somehow got created, make sure to delete it
|
||||
downloadedFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a download set for Wifi does not progress while Wifi is disabled, but resumes
|
||||
* once Wifi is re-enabled.
|
||||
*/
|
||||
@LargeTest
|
||||
public void testDownloadNoWifi() throws Exception {
|
||||
long timeout = 60 * 1000; // wait only 60 seconds before giving up
|
||||
int fileSize = 140 * 1024; // 140k
|
||||
byte[] blobData = generateData(fileSize, DataType.TEXT);
|
||||
|
||||
setWiFiStateOn(false);
|
||||
enqueueResponse(HTTP_OK, blobData);
|
||||
|
||||
try {
|
||||
Uri uri = getServerUri(DEFAULT_FILENAME);
|
||||
Request request = new Request(uri);
|
||||
request.setAllowedNetworkTypes(Request.NETWORK_WIFI);
|
||||
|
||||
long dlRequest = mDownloadManager.enqueue(request);
|
||||
|
||||
// wait for the download to complete
|
||||
boolean success = waitForDownloadOrTimeoutNoThrow(dlRequest,
|
||||
WAIT_FOR_DOWNLOAD_POLL_TIME, timeout);
|
||||
assertFalse("Download proceeded without Wifi connection!", success);
|
||||
|
||||
setWiFiStateOn(true);
|
||||
waitForDownloadOrTimeout(dlRequest);
|
||||
|
||||
assertEquals(1, mReceiver.numDownloadsCompleted());
|
||||
} finally {
|
||||
setWiFiStateOn(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests trying to download two large files (50M bytes, followed by 60M bytes)
|
||||
*/
|
||||
@LargeTest
|
||||
public void testInsufficientSpaceSingleFiles() throws Exception {
|
||||
long fileSize1 = 50000000L;
|
||||
long fileSize2 = 60000000L;
|
||||
File largeFile1 = createFileOnSD(null, fileSize1, DataType.TEXT, null);
|
||||
File largeFile2 = createFileOnSD(null, fileSize2, DataType.TEXT, null);
|
||||
|
||||
try {
|
||||
long dlRequest = doStandardEnqueue(largeFile1);
|
||||
waitForDownloadOrTimeout(dlRequest);
|
||||
ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest);
|
||||
verifyFileContents(pfd, largeFile1);
|
||||
verifyFileSize(pfd, largeFile1.length());
|
||||
|
||||
dlRequest = doStandardEnqueue(largeFile2);
|
||||
waitForDownloadOrTimeout(dlRequest);
|
||||
Cursor cursor = getCursor(dlRequest);
|
||||
try {
|
||||
verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE,
|
||||
DownloadManager.ERROR_INSUFFICIENT_SPACE);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
} finally {
|
||||
largeFile1.delete();
|
||||
largeFile2.delete();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Random;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.net.DownloadManager.Query;
|
||||
import android.net.DownloadManager.Request;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
import android.util.Log;
|
||||
|
||||
|
||||
public class DownloadManagerStressTest extends DownloadManagerBaseTest {
|
||||
private static String LOG_TAG = "android.net.DownloadManagerStressTest";
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
mServer.play(0);
|
||||
removeAllCurrentDownloads();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to downloading thousands of files simultaneously
|
||||
*/
|
||||
public void testDownloadThousands() throws Exception {
|
||||
int NUM_FILES = 1500;
|
||||
int MAX_FILE_SIZE = 3000;
|
||||
long[] reqs = new long[NUM_FILES];
|
||||
|
||||
// need to be sure all current downloads have stopped first
|
||||
MultipleDownloadsCompletedReceiver receiver = registerNewMultipleDownloadsReceiver();
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
Random r = new LoggingRng();
|
||||
for (int i = 0; i < NUM_FILES; ++i) {
|
||||
int size = r.nextInt(MAX_FILE_SIZE);
|
||||
byte[] blobData = generateData(size, DataType.TEXT);
|
||||
|
||||
Uri uri = getServerUri(DEFAULT_FILENAME);
|
||||
Request request = new Request(uri);
|
||||
request.setTitle(String.format("%s--%d", DEFAULT_FILENAME, i));
|
||||
|
||||
// Prepare the mock server with a standard response
|
||||
enqueueResponse(HTTP_OK, blobData);
|
||||
|
||||
Log.i(LOG_TAG, "issuing request: " + i);
|
||||
long reqId = mDownloadManager.enqueue(request);
|
||||
reqs[i] = reqId;
|
||||
}
|
||||
|
||||
// wait for the download to complete or timeout
|
||||
waitForDownloadsOrTimeout(WAIT_FOR_DOWNLOAD_POLL_TIME, MAX_WAIT_FOR_DOWNLOAD_TIME);
|
||||
cursor = mDownloadManager.query(new Query());
|
||||
assertEquals(NUM_FILES, cursor.getCount());
|
||||
Log.i(LOG_TAG, "Verified number of downloads in download manager is what we expect.");
|
||||
while (cursor.moveToNext()) {
|
||||
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
|
||||
String filename = cursor.getString(cursor.getColumnIndex(
|
||||
DownloadManager.COLUMN_URI));
|
||||
String errorString = String.format("File %s failed to download successfully. " +
|
||||
"Status code: %d", filename, status);
|
||||
assertEquals(errorString, DownloadManager.STATUS_SUCCESSFUL, status);
|
||||
}
|
||||
Log.i(LOG_TAG, "Verified each download was successful.");
|
||||
assertEquals(NUM_FILES, receiver.numDownloadsCompleted());
|
||||
Log.i(LOG_TAG, "Verified number of completed downloads in our receiver.");
|
||||
|
||||
// Verify that for each request, we can open the downloaded file
|
||||
for (int i = 0; i < NUM_FILES; ++i) {
|
||||
ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(reqs[i]);
|
||||
pfd.close();
|
||||
}
|
||||
Log.i(LOG_TAG, "Verified we can open each file.");
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
mContext.unregisterReceiver(receiver);
|
||||
removeAllCurrentDownloads();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests trying to download a large file (50M bytes).
|
||||
*/
|
||||
public void testDownloadLargeFile() throws Exception {
|
||||
long fileSize = 50000000L; // note: kept relatively small to not exceed /cache dir size
|
||||
File largeFile = createFileOnSD(null, fileSize, DataType.TEXT, null);
|
||||
MultipleDownloadsCompletedReceiver receiver = registerNewMultipleDownloadsReceiver();
|
||||
|
||||
try {
|
||||
long dlRequest = doStandardEnqueue(largeFile);
|
||||
|
||||
// wait for the download to complete
|
||||
waitForDownloadOrTimeout(dlRequest);
|
||||
|
||||
ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest);
|
||||
verifyFileContents(pfd, largeFile);
|
||||
verifyFileSize(pfd, largeFile.length());
|
||||
|
||||
assertEquals(1, receiver.numDownloadsCompleted());
|
||||
mContext.unregisterReceiver(receiver);
|
||||
} catch (Exception e) {
|
||||
throw e;
|
||||
} finally {
|
||||
largeFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests trying to download a large file (~300M bytes) when there's not enough space in cache
|
||||
*/
|
||||
public void testInsufficientSpace() throws Exception {
|
||||
long fileSize = 300000000L;
|
||||
File largeFile = createFileOnSD(null, fileSize, DataType.TEXT, null);
|
||||
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
long dlRequest = doStandardEnqueue(largeFile);
|
||||
|
||||
// wait for the download to complete
|
||||
waitForDownloadOrTimeout(dlRequest);
|
||||
|
||||
cursor = getCursor(dlRequest);
|
||||
verifyInt(cursor, DownloadManager.COLUMN_STATUS, DownloadManager.STATUS_FAILED);
|
||||
verifyInt(cursor, DownloadManager.COLUMN_ERROR_CODE,
|
||||
DownloadManager.ERROR_INSUFFICIENT_SPACE);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
largeFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
@ -37,7 +37,9 @@ import java.io.InputStreamReader;
|
||||
import java.io.StringReader;
|
||||
import java.lang.Runtime;
|
||||
import java.lang.Process;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -117,7 +119,14 @@ public class PackageManagerHostTestUtils extends Assert {
|
||||
|
||||
/**
|
||||
* Helper method to run tests and return the listener that collected the results.
|
||||
*
|
||||
* For the optional params, pass null to use the default values.
|
||||
|
||||
* @param pkgName Android application package for tests
|
||||
* @param className (optional) The class containing the method to test
|
||||
* @param methodName (optional) The method in the class of which to test
|
||||
* @param runnerName (optional) The name of the TestRunner of the test on the device to be run
|
||||
* @param params (optional) Any additional parameters to pass into the Test Runner
|
||||
* @return the {@link CollectingTestRunListener}
|
||||
* @throws TimeoutException in case of a timeout on the connection.
|
||||
* @throws AdbCommandRejectedException if adb rejects the command
|
||||
@ -125,15 +134,46 @@ public class PackageManagerHostTestUtils extends Assert {
|
||||
* a period longer than the max time to output.
|
||||
* @throws IOException if connection to device was lost.
|
||||
*/
|
||||
private CollectingTestRunListener doRunTests(String pkgName) throws IOException,
|
||||
private CollectingTestRunListener doRunTests(String pkgName, String className, String
|
||||
methodName, String runnerName, Map<String, String> params) throws IOException,
|
||||
TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
|
||||
RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(
|
||||
pkgName, mDevice);
|
||||
|
||||
RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(pkgName, runnerName,
|
||||
mDevice);
|
||||
|
||||
if (className != null && methodName != null) {
|
||||
testRunner.setMethodName(className, methodName);
|
||||
}
|
||||
|
||||
// Add in any additional args to pass into the test
|
||||
if (params != null) {
|
||||
for (Entry<String, String> argPair : params.entrySet()) {
|
||||
testRunner.addInstrumentationArg(argPair.getKey(), argPair.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
CollectingTestRunListener listener = new CollectingTestRunListener();
|
||||
testRunner.run(listener);
|
||||
return listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the specified packages tests, and returns whether all tests passed or not.
|
||||
*
|
||||
* @param pkgName Android application package for tests
|
||||
* @param className The class containing the method to test
|
||||
* @param methodName The method in the class of which to test
|
||||
* @param runnerName The name of the TestRunner of the test on the device to be run
|
||||
* @param params Any additional parameters to pass into the Test Runner
|
||||
* @return true if test passed, false otherwise.
|
||||
*/
|
||||
public boolean runDeviceTestsDidAllTestsPass(String pkgName, String className,
|
||||
String methodName, String runnerName, Map<String, String> params) throws IOException {
|
||||
CollectingTestRunListener listener = doRunTests(pkgName, className, methodName,
|
||||
runnerName, params);
|
||||
return listener.didAllTestsPass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the specified packages tests, and returns whether all tests passed or not.
|
||||
*
|
||||
@ -147,7 +187,7 @@ public class PackageManagerHostTestUtils extends Assert {
|
||||
*/
|
||||
public boolean runDeviceTestsDidAllTestsPass(String pkgName) throws IOException,
|
||||
TimeoutException, AdbCommandRejectedException, ShellCommandUnresponsiveException {
|
||||
CollectingTestRunListener listener = doRunTests(pkgName);
|
||||
CollectingTestRunListener listener = doRunTests(pkgName, null, null, null, null);
|
||||
return listener.didAllTestsPass();
|
||||
}
|
||||
|
||||
@ -531,7 +571,7 @@ public class PackageManagerHostTestUtils extends Assert {
|
||||
}
|
||||
|
||||
// For collecting results from running device tests
|
||||
private static class CollectingTestRunListener implements ITestRunListener {
|
||||
public static class CollectingTestRunListener implements ITestRunListener {
|
||||
|
||||
private boolean mAllTestsPassed = true;
|
||||
private String mTestRunErrorMessage = null;
|
||||
|
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.net;
|
||||
|
||||
import android.content.pm.PackageManagerHostTestUtils;
|
||||
import android.content.pm.PackageManagerHostTestUtils.CollectingTestRunListener;
|
||||
|
||||
import com.android.ddmlib.IDevice;
|
||||
import com.android.ddmlib.IShellOutputReceiver;
|
||||
import com.android.ddmlib.Log;
|
||||
import com.android.ddmlib.MultiLineReceiver;
|
||||
import com.android.ddmlib.SyncService;
|
||||
import com.android.ddmlib.SyncService.ISyncProgressMonitor;
|
||||
import com.android.ddmlib.SyncService.SyncResult;
|
||||
import com.android.hosttest.DeviceTestCase;
|
||||
import com.android.hosttest.DeviceTestSuite;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import junit.framework.Test;
|
||||
|
||||
/**
|
||||
* Host-based tests of the DownloadManager API. (Uses a device-based app to actually invoke the
|
||||
* various tests.)
|
||||
*/
|
||||
public class DownloadManagerHostTests extends DeviceTestCase {
|
||||
protected PackageManagerHostTestUtils mPMUtils = null;
|
||||
|
||||
private static final String LOG_TAG = "android.net.DownloadManagerHostTests";
|
||||
private static final String FILE_DOWNLOAD_APK = "DownloadManagerTestApp.apk";
|
||||
private static final String FILE_DOWNLOAD_PKG = "com.android.frameworks.downloadmanagertests";
|
||||
private static final String FILE_DOWNLOAD_CLASS =
|
||||
"com.android.frameworks.downloadmanagertests.DownloadManagerTestApp";
|
||||
private static final String DOWNLOAD_TEST_RUNNER_NAME =
|
||||
"com.android.frameworks.downloadmanagertests.DownloadManagerTestRunner";
|
||||
|
||||
// Extra parameters to pass to the TestRunner
|
||||
private static final String EXTERNAL_DOWNLOAD_URI_KEY = "external_download_uri";
|
||||
// Note: External environment variable ANDROID_TEST_EXTERNAL_URI must be set to point to the
|
||||
// external URI under which the files downloaded by the tests can be found. Note that the Uri
|
||||
// must be accessible by the device during a test run.
|
||||
private static String EXTERNAL_DOWNLOAD_URI_VALUE = null;
|
||||
|
||||
Hashtable<String, String> mExtraParams = null;
|
||||
|
||||
public static Test suite() {
|
||||
return new DeviceTestSuite(DownloadManagerHostTests.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
// ensure apk path has been set before test is run
|
||||
assertNotNull(getTestAppPath());
|
||||
mPMUtils = new PackageManagerHostTestUtils(getDevice());
|
||||
EXTERNAL_DOWNLOAD_URI_VALUE = System.getenv("ANDROID_TEST_EXTERNAL_URI");
|
||||
assertNotNull(EXTERNAL_DOWNLOAD_URI_VALUE);
|
||||
mExtraParams = getExtraParams();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get extra params that can be used to pass into the helper app.
|
||||
*/
|
||||
protected Hashtable<String, String> getExtraParams() {
|
||||
Hashtable<String, String> extraParams = new Hashtable<String, String>();
|
||||
extraParams.put(EXTERNAL_DOWNLOAD_URI_KEY, EXTERNAL_DOWNLOAD_URI_VALUE);
|
||||
return extraParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a large download over WiFi
|
||||
* @throws Exception if the test failed at any point
|
||||
*/
|
||||
public void testLargeDownloadOverWiFi() throws Exception {
|
||||
mPMUtils.installAppAndVerifyExistsOnDevice(String.format("%s%s%s", getTestAppPath(),
|
||||
File.separator, FILE_DOWNLOAD_APK), FILE_DOWNLOAD_PKG, true);
|
||||
|
||||
boolean testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG,
|
||||
FILE_DOWNLOAD_CLASS, "runLargeDownloadOverWiFi", DOWNLOAD_TEST_RUNNER_NAME,
|
||||
mExtraParams);
|
||||
|
||||
assertTrue("Failed to install large file over WiFi in < 10 minutes!", testPassed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a device-based function to initiate a download on the device, reboots the device,
|
||||
* then waits and verifies the download succeeded.
|
||||
*
|
||||
* @throws Exception if the test failed at any point
|
||||
*/
|
||||
public void testDownloadManagerSingleReboot() throws Exception {
|
||||
mPMUtils.installAppAndVerifyExistsOnDevice(String.format("%s%s%s", getTestAppPath(),
|
||||
File.separator, FILE_DOWNLOAD_APK), FILE_DOWNLOAD_PKG, true);
|
||||
|
||||
boolean testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG,
|
||||
FILE_DOWNLOAD_CLASS, "initiateDownload", DOWNLOAD_TEST_RUNNER_NAME,
|
||||
mExtraParams);
|
||||
|
||||
assertTrue("Failed to initiate download properly!", testPassed);
|
||||
mPMUtils.rebootDevice();
|
||||
testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG,
|
||||
FILE_DOWNLOAD_CLASS, "verifyFileDownloadSucceeded", DOWNLOAD_TEST_RUNNER_NAME,
|
||||
mExtraParams);
|
||||
assertTrue("Failed to verify initiated download completed properyly!", testPassed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a device-based function to initiate a download on the device, reboots the device three
|
||||
* times (using different intervals), then waits and verifies the download succeeded.
|
||||
*
|
||||
* @throws Exception if the test failed at any point
|
||||
*/
|
||||
public void testDownloadManagerMultipleReboots() throws Exception {
|
||||
mPMUtils.installAppAndVerifyExistsOnDevice(String.format("%s%s%s", getTestAppPath(),
|
||||
File.separator, FILE_DOWNLOAD_APK), FILE_DOWNLOAD_PKG, true);
|
||||
|
||||
boolean testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG,
|
||||
FILE_DOWNLOAD_CLASS, "initiateDownload", DOWNLOAD_TEST_RUNNER_NAME,
|
||||
mExtraParams);
|
||||
|
||||
assertTrue("Failed to initiate download properly!", testPassed);
|
||||
Thread.sleep(5000);
|
||||
|
||||
// Do 3 random reboots - after 13, 9, and 19 seconds
|
||||
Log.i(LOG_TAG, "First reboot...");
|
||||
mPMUtils.rebootDevice();
|
||||
Thread.sleep(13000);
|
||||
Log.i(LOG_TAG, "Second reboot...");
|
||||
mPMUtils.rebootDevice();
|
||||
Thread.sleep(9000);
|
||||
Log.i(LOG_TAG, "Third reboot...");
|
||||
mPMUtils.rebootDevice();
|
||||
Thread.sleep(19000);
|
||||
testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG,
|
||||
FILE_DOWNLOAD_CLASS, "verifyFileDownloadSucceeded", DOWNLOAD_TEST_RUNNER_NAME,
|
||||
mExtraParams);
|
||||
assertTrue("Failed to verify initiated download completed properyly!", testPassed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a device-based function to test download while WiFi is enabled/disabled multiple times
|
||||
* during the download.
|
||||
*
|
||||
* @throws Exception if the test failed at any point
|
||||
*/
|
||||
public void testDownloadMultipleWiFiEnableDisable() throws Exception {
|
||||
mPMUtils.installAppAndVerifyExistsOnDevice(String.format("%s%s%s", getTestAppPath(),
|
||||
File.separator, FILE_DOWNLOAD_APK), FILE_DOWNLOAD_PKG, true);
|
||||
|
||||
boolean testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG,
|
||||
FILE_DOWNLOAD_CLASS, "runDownloadMultipleWiFiEnableDisable",
|
||||
DOWNLOAD_TEST_RUNNER_NAME, mExtraParams);
|
||||
assertTrue(testPassed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a device-based function to test switching on/off both airplane mode and WiFi
|
||||
*
|
||||
* @throws Exception if the test failed at any point
|
||||
*/
|
||||
public void testDownloadMultipleSwitching() throws Exception {
|
||||
mPMUtils.installAppAndVerifyExistsOnDevice(String.format("%s%s%s", getTestAppPath(),
|
||||
File.separator, FILE_DOWNLOAD_APK), FILE_DOWNLOAD_PKG, true);
|
||||
|
||||
boolean testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG,
|
||||
FILE_DOWNLOAD_CLASS, "runDownloadMultipleSwitching",
|
||||
DOWNLOAD_TEST_RUNNER_NAME, mExtraParams);
|
||||
assertTrue(testPassed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a device-based function to test switching on/off airplane mode multiple times
|
||||
*
|
||||
* @throws Exception if the test failed at any point
|
||||
*/
|
||||
public void testDownloadMultipleAirplaneModeEnableDisable() throws Exception {
|
||||
mPMUtils.installAppAndVerifyExistsOnDevice(String.format("%s%s%s", getTestAppPath(),
|
||||
File.separator, FILE_DOWNLOAD_APK), FILE_DOWNLOAD_PKG, true);
|
||||
|
||||
boolean testPassed = mPMUtils.runDeviceTestsDidAllTestsPass(FILE_DOWNLOAD_PKG,
|
||||
FILE_DOWNLOAD_CLASS, "runDownloadMultipleAirplaneModeEnableDisable",
|
||||
DOWNLOAD_TEST_RUNNER_NAME, mExtraParams);
|
||||
assertTrue(testPassed);
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
# Copyright (C) 2010 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
|
||||
../../../coretests/src/android/net/DownloadManagerBaseTest.java
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib
|
||||
LOCAL_SDK_VERSION := current
|
||||
|
||||
LOCAL_PACKAGE_NAME := DownloadManagerTestApp
|
||||
|
||||
include $(BUILD_PACKAGE)
|
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2010 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.frameworks.downloadmanagertests">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
|
||||
|
||||
<application android:label="DownloadManagerTestApp">
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<instrumentation
|
||||
android:name=".DownloadManagerTestRunner"
|
||||
android:targetPackage="com.android.frameworks.downloadmanagertests"
|
||||
android:label="Frameworks Download Manager Test App" />
|
||||
|
||||
</manifest>
|
@ -0,0 +1,463 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.frameworks.downloadmanagertests;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.DownloadManager;
|
||||
import android.net.DownloadManager.Query;
|
||||
import android.net.DownloadManager.Request;
|
||||
import android.net.DownloadManagerBaseTest;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.provider.Settings;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileWriter;
|
||||
|
||||
import coretestutils.http.MockResponse;
|
||||
import coretestutils.http.MockWebServer;
|
||||
import coretestutils.http.RecordedRequest;
|
||||
|
||||
/**
|
||||
* Class to test downloading files from a real (not mock) external server.
|
||||
*/
|
||||
public class DownloadManagerTestApp extends DownloadManagerBaseTest {
|
||||
protected static String DOWNLOAD_STARTED_FLAG = "DMTEST_DOWNLOAD_STARTED";
|
||||
protected static String LOG_TAG =
|
||||
"com.android.frameworks.downloadmanagertests.DownloadManagerTestApp";
|
||||
|
||||
protected static String DOWNLOAD_500K_FILENAME = "External541kb.apk";
|
||||
protected static long DOWNLOAD_500K_FILESIZE = 570927;
|
||||
protected static String DOWNLOAD_1MB_FILENAME = "External1mb.apk";
|
||||
protected static long DOWNLOAD_1MB_FILESIZE = 1041262;
|
||||
protected static String DOWNLOAD_10MB_FILENAME = "External10mb.apk";
|
||||
protected static long DOWNLOAD_10MB_FILESIZE = 10258741;
|
||||
|
||||
// Values to be obtained from TestRunner
|
||||
private String externalDownloadUriValue = null;
|
||||
|
||||
/**
|
||||
* {@inheritDoc }
|
||||
*/
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
DownloadManagerTestRunner mRunner = (DownloadManagerTestRunner)getInstrumentation();
|
||||
externalDownloadUriValue = mRunner.externalDownloadUriValue;
|
||||
assertNotNull(externalDownloadUriValue);
|
||||
|
||||
if (!externalDownloadUriValue.endsWith("/")) {
|
||||
externalDownloadUriValue += "/";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the external URL of the file to download
|
||||
*
|
||||
* @return the Uri of the external file to download
|
||||
*/
|
||||
private Uri getExternalFileUri(String file) {
|
||||
return Uri.parse(externalDownloadUriValue + file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path to the file that flags that a download has started. The file contains the
|
||||
* DownloadManager id of the download being trackted between reboot sessions.
|
||||
*
|
||||
* @return The path of the file tracking that a download has started
|
||||
* @throws InterruptedException if interrupted
|
||||
* @throws Exception if timed out while waiting for SD card to mount
|
||||
*/
|
||||
protected String getDownloadStartedFilePath() {
|
||||
String path = Environment.getExternalStorageDirectory().getPath();
|
||||
return path + File.separatorChar + DOWNLOAD_STARTED_FLAG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Common setup steps for downloads.
|
||||
*
|
||||
* Note that these are not included in setUp, so that individual tests can control their own
|
||||
* state between reboots, etc.
|
||||
*/
|
||||
protected void doCommonDownloadSetup() throws Exception {
|
||||
setWiFiStateOn(true);
|
||||
setAirplaneModeOn(false);
|
||||
waitForExternalStoreMount();
|
||||
removeAllCurrentDownloads();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates a download.
|
||||
*
|
||||
* Queues up a download to the download manager, and saves the DownloadManager's assigned
|
||||
* download ID for this download to a file.
|
||||
*
|
||||
* @throws Exception if unsuccessful
|
||||
*/
|
||||
public void initiateDownload() throws Exception {
|
||||
String filename = DOWNLOAD_1MB_FILENAME;
|
||||
mContext.deleteFile(DOWNLOAD_STARTED_FLAG);
|
||||
FileOutputStream fileOutput = mContext.openFileOutput(DOWNLOAD_STARTED_FLAG, 0);
|
||||
DataOutputStream outputFile = null;
|
||||
doCommonDownloadSetup();
|
||||
|
||||
try {
|
||||
long dlRequest = -1;
|
||||
|
||||
// Make sure there are no pending downloads currently going on
|
||||
removeAllCurrentDownloads();
|
||||
|
||||
Uri remoteUri = getExternalFileUri(filename);
|
||||
Request request = new Request(remoteUri);
|
||||
|
||||
dlRequest = mDownloadManager.enqueue(request);
|
||||
waitForDownloadToStart(dlRequest);
|
||||
assertTrue(dlRequest != -1);
|
||||
|
||||
// Store ID of download for later retrieval
|
||||
outputFile = new DataOutputStream(fileOutput);
|
||||
outputFile.writeLong(dlRequest);
|
||||
} finally {
|
||||
if (outputFile != null) {
|
||||
outputFile.flush();
|
||||
outputFile.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a previously-initiated download and verifies it has completed successfully.
|
||||
*
|
||||
* @throws Exception if unsuccessful
|
||||
*/
|
||||
public void verifyFileDownloadSucceeded() throws Exception {
|
||||
String filename = DOWNLOAD_1MB_FILENAME;
|
||||
long filesize = DOWNLOAD_1MB_FILESIZE;
|
||||
long dlRequest = -1;
|
||||
boolean rebootMarkerValid = false;
|
||||
DataInputStream dataInputFile = null;
|
||||
|
||||
setWiFiStateOn(true);
|
||||
setAirplaneModeOn(false);
|
||||
|
||||
try {
|
||||
FileInputStream inFile = mContext.openFileInput(DOWNLOAD_STARTED_FLAG);
|
||||
dataInputFile = new DataInputStream(inFile);
|
||||
dlRequest = dataInputFile.readLong();
|
||||
} catch (Exception e) {
|
||||
// The file was't valid so we just leave the flag false
|
||||
Log.i(LOG_TAG, "Unable to determine initial download id.");
|
||||
throw e;
|
||||
} finally {
|
||||
if (dataInputFile != null) {
|
||||
dataInputFile.close();
|
||||
}
|
||||
mContext.deleteFile(DOWNLOAD_STARTED_FLAG);
|
||||
}
|
||||
|
||||
assertTrue(dlRequest != -1);
|
||||
Cursor cursor = getCursor(dlRequest);
|
||||
ParcelFileDescriptor pfd = null;
|
||||
try {
|
||||
assertTrue("Unable to query last initiated download!", cursor.moveToFirst());
|
||||
|
||||
int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
|
||||
int status = cursor.getInt(columnIndex);
|
||||
int currentWaitTime = 0;
|
||||
|
||||
// Wait until the download finishes
|
||||
waitForDownloadOrTimeout(dlRequest);
|
||||
|
||||
Log.i(LOG_TAG, "Verifying download information...");
|
||||
// Verify specific info about the file (size, name, etc)...
|
||||
pfd = mDownloadManager.openDownloadedFile(dlRequest);
|
||||
verifyFileSize(pfd, filesize);
|
||||
} catch (Exception e) {
|
||||
Log.i(LOG_TAG, "error: " + e.toString());
|
||||
throw e;
|
||||
} finally {
|
||||
// Clean up...
|
||||
cursor.close();
|
||||
mDownloadManager.remove(dlRequest);
|
||||
if (pfd != null) {
|
||||
pfd.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests downloading a large file over WiFi (~10 Mb).
|
||||
*
|
||||
* @throws Exception if unsuccessful
|
||||
*/
|
||||
public void runLargeDownloadOverWiFi() throws Exception {
|
||||
String filename = DOWNLOAD_10MB_FILENAME;
|
||||
long filesize = DOWNLOAD_10MB_FILESIZE;
|
||||
long dlRequest = -1;
|
||||
doCommonDownloadSetup();
|
||||
|
||||
// Make sure there are no pending downloads currently going on
|
||||
removeAllCurrentDownloads();
|
||||
|
||||
Uri remoteUri = getExternalFileUri(filename);
|
||||
Request request = new Request(remoteUri);
|
||||
request.setMediaType(getMimeMapping(DownloadFileType.APK));
|
||||
|
||||
dlRequest = mDownloadManager.enqueue(request);
|
||||
|
||||
// Rather large file, so wait up to 15 mins...
|
||||
waitForDownloadOrTimeout(dlRequest, WAIT_FOR_DOWNLOAD_POLL_TIME, 15 * 60 * 1000);
|
||||
|
||||
Cursor cursor = getCursor(dlRequest);
|
||||
ParcelFileDescriptor pfd = null;
|
||||
try {
|
||||
Log.i(LOG_TAG, "Verifying download information...");
|
||||
// Verify specific info about the file (size, name, etc)...
|
||||
pfd = mDownloadManager.openDownloadedFile(dlRequest);
|
||||
verifyFileSize(pfd, filesize);
|
||||
} finally {
|
||||
if (pfd != null) {
|
||||
pfd.close();
|
||||
}
|
||||
mDownloadManager.remove(dlRequest);
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that downloads resume when switching back and forth from having connectivity to
|
||||
* having no connectivity using both WiFi and airplane mode.
|
||||
*
|
||||
* Note: Device has no mobile access when running this test.
|
||||
*
|
||||
* @throws Exception if unsuccessful
|
||||
*/
|
||||
public void runDownloadMultipleSwitching() throws Exception {
|
||||
String filename = DOWNLOAD_500K_FILENAME;
|
||||
long filesize = DOWNLOAD_500K_FILESIZE;
|
||||
doCommonDownloadSetup();
|
||||
|
||||
String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath();
|
||||
File downloadedFile = new File(localDownloadDirectory, filename);
|
||||
|
||||
long dlRequest = -1;
|
||||
try {
|
||||
downloadedFile.delete();
|
||||
|
||||
// Make sure there are no pending downloads currently going on
|
||||
removeAllCurrentDownloads();
|
||||
|
||||
Uri remoteUri = getExternalFileUri(filename);
|
||||
Request request = new Request(remoteUri);
|
||||
|
||||
// Local destination of downloaded file
|
||||
Uri localUri = Uri.fromFile(downloadedFile);
|
||||
Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
|
||||
request.setDestinationUri(localUri);
|
||||
|
||||
request.setAllowedNetworkTypes(Request.NETWORK_MOBILE | Request.NETWORK_WIFI);
|
||||
|
||||
dlRequest = mDownloadManager.enqueue(request);
|
||||
waitForDownloadToStart(dlRequest);
|
||||
// make sure we're starting to download some data...
|
||||
waitForFileToGrow(downloadedFile);
|
||||
|
||||
// download disable
|
||||
setWiFiStateOn(false);
|
||||
|
||||
// download disable
|
||||
Log.i(LOG_TAG, "Turning on airplane mode...");
|
||||
setAirplaneModeOn(true);
|
||||
Thread.sleep(30 * 1000); // wait 30 secs
|
||||
|
||||
// download disable
|
||||
setWiFiStateOn(true);
|
||||
Thread.sleep(30 * 1000); // wait 30 secs
|
||||
|
||||
// download enable
|
||||
Log.i(LOG_TAG, "Turning off airplane mode...");
|
||||
setAirplaneModeOn(false);
|
||||
Thread.sleep(5 * 1000); // wait 5 seconds
|
||||
|
||||
// download disable
|
||||
Log.i(LOG_TAG, "Turning off WiFi...");
|
||||
setWiFiStateOn(false);
|
||||
Thread.sleep(30 * 1000); // wait 30 secs
|
||||
|
||||
// finally, turn WiFi back on and finish up the download
|
||||
Log.i(LOG_TAG, "Turning on WiFi...");
|
||||
setWiFiStateOn(true);
|
||||
Log.i(LOG_TAG, "Waiting up to 3 minutes for download to complete...");
|
||||
waitForDownloadsOrTimeout(dlRequest, 3 * 60 * 1000);
|
||||
ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest);
|
||||
verifyFileSize(pfd, filesize);
|
||||
} finally {
|
||||
Log.i(LOG_TAG, "Cleaning up files...");
|
||||
if (dlRequest != -1) {
|
||||
mDownloadManager.remove(dlRequest);
|
||||
}
|
||||
downloadedFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that downloads resume when switching on/off WiFi at various intervals.
|
||||
*
|
||||
* Note: Device has no mobile access when running this test.
|
||||
*
|
||||
* @throws Exception if unsuccessful
|
||||
*/
|
||||
public void runDownloadMultipleWiFiEnableDisable() throws Exception {
|
||||
String filename = DOWNLOAD_500K_FILENAME;
|
||||
long filesize = DOWNLOAD_500K_FILESIZE;
|
||||
doCommonDownloadSetup();
|
||||
|
||||
String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath();
|
||||
File downloadedFile = new File(localDownloadDirectory, filename);
|
||||
long dlRequest = -1;
|
||||
try {
|
||||
downloadedFile.delete();
|
||||
|
||||
// Make sure there are no pending downloads currently going on
|
||||
removeAllCurrentDownloads();
|
||||
|
||||
Uri remoteUri = getExternalFileUri(filename);
|
||||
Request request = new Request(remoteUri);
|
||||
|
||||
// Local destination of downloaded file
|
||||
Uri localUri = Uri.fromFile(downloadedFile);
|
||||
Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
|
||||
request.setDestinationUri(localUri);
|
||||
|
||||
request.setAllowedNetworkTypes(Request.NETWORK_WIFI);
|
||||
|
||||
dlRequest = mDownloadManager.enqueue(request);
|
||||
waitForDownloadToStart(dlRequest);
|
||||
// are we making any progress?
|
||||
waitForFileToGrow(downloadedFile);
|
||||
|
||||
// download disable
|
||||
Log.i(LOG_TAG, "Turning off WiFi...");
|
||||
setWiFiStateOn(false);
|
||||
Thread.sleep(40 * 1000); // wait 40 seconds
|
||||
|
||||
// enable download...
|
||||
Log.i(LOG_TAG, "Turning on WiFi again...");
|
||||
setWiFiStateOn(true);
|
||||
waitForFileToGrow(downloadedFile);
|
||||
|
||||
// download disable
|
||||
Log.i(LOG_TAG, "Turning off WiFi...");
|
||||
setWiFiStateOn(false);
|
||||
Thread.sleep(20 * 1000); // wait 20 seconds
|
||||
|
||||
// enable download...
|
||||
Log.i(LOG_TAG, "Turning on WiFi again...");
|
||||
setWiFiStateOn(true);
|
||||
|
||||
Log.i(LOG_TAG, "Waiting up to 3 minutes for download to complete...");
|
||||
waitForDownloadsOrTimeout(dlRequest, 3 * 60 * 1000);
|
||||
ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest);
|
||||
verifyFileSize(pfd, filesize);
|
||||
} finally {
|
||||
Log.i(LOG_TAG, "Cleaning up files...");
|
||||
if (dlRequest != -1) {
|
||||
mDownloadManager.remove(dlRequest);
|
||||
}
|
||||
downloadedFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that downloads resume when switching on/off Airplane mode numerous times at
|
||||
* various intervals.
|
||||
*
|
||||
* Note: Device has no mobile access when running this test.
|
||||
*
|
||||
* @throws Exception if unsuccessful
|
||||
*/
|
||||
public void runDownloadMultipleAirplaneModeEnableDisable() throws Exception {
|
||||
String filename = DOWNLOAD_500K_FILENAME;
|
||||
long filesize = DOWNLOAD_500K_FILESIZE;
|
||||
// make sure WiFi is enabled, and airplane mode is not on
|
||||
doCommonDownloadSetup();
|
||||
|
||||
String localDownloadDirectory = Environment.getExternalStorageDirectory().getPath();
|
||||
File downloadedFile = new File(localDownloadDirectory, filename);
|
||||
long dlRequest = -1;
|
||||
try {
|
||||
downloadedFile.delete();
|
||||
|
||||
// Make sure there are no pending downloads currently going on
|
||||
removeAllCurrentDownloads();
|
||||
|
||||
Uri remoteUri = getExternalFileUri(filename);
|
||||
Request request = new Request(remoteUri);
|
||||
|
||||
// Local destination of downloaded file
|
||||
Uri localUri = Uri.fromFile(downloadedFile);
|
||||
Log.i(LOG_TAG, "setting localUri to: " + localUri.getPath());
|
||||
request.setDestinationUri(localUri);
|
||||
|
||||
request.setAllowedNetworkTypes(Request.NETWORK_WIFI);
|
||||
|
||||
dlRequest = mDownloadManager.enqueue(request);
|
||||
waitForDownloadToStart(dlRequest);
|
||||
// are we making any progress?
|
||||
waitForFileToGrow(downloadedFile);
|
||||
|
||||
// download disable
|
||||
Log.i(LOG_TAG, "Turning on Airplane mode...");
|
||||
setAirplaneModeOn(true);
|
||||
Thread.sleep(60 * 1000); // wait 1 minute
|
||||
|
||||
// download enable
|
||||
Log.i(LOG_TAG, "Turning off Airplane mode...");
|
||||
setAirplaneModeOn(false);
|
||||
// make sure we're starting to download some data...
|
||||
waitForFileToGrow(downloadedFile);
|
||||
|
||||
// reenable the connection to start up the download again
|
||||
Log.i(LOG_TAG, "Turning on Airplane mode again...");
|
||||
setAirplaneModeOn(true);
|
||||
Thread.sleep(20 * 1000); // wait 20 seconds
|
||||
|
||||
// Finish up the download...
|
||||
Log.i(LOG_TAG, "Turning off Airplane mode again...");
|
||||
setAirplaneModeOn(false);
|
||||
|
||||
Log.i(LOG_TAG, "Waiting up to 3 minutes for donwload to complete...");
|
||||
waitForDownloadsOrTimeout(dlRequest, 180 * 1000); // wait up to 3 mins before timeout
|
||||
ParcelFileDescriptor pfd = mDownloadManager.openDownloadedFile(dlRequest);
|
||||
verifyFileSize(pfd, filesize);
|
||||
} finally {
|
||||
Log.i(LOG_TAG, "Cleaning up files...");
|
||||
if (dlRequest != -1) {
|
||||
mDownloadManager.remove(dlRequest);
|
||||
}
|
||||
downloadedFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2010, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.frameworks.downloadmanagertests;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.test.InstrumentationTestRunner;
|
||||
import android.test.InstrumentationTestSuite;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.frameworks.downloadmanagertests.DownloadManagerTestApp;
|
||||
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
/**
|
||||
* Instrumentation Test Runner for all download manager tests.
|
||||
*
|
||||
* To run the download manager tests:
|
||||
*
|
||||
* adb shell am instrument -e external_download_1mb_uri <uri> external_download_500k_uri <uri> \
|
||||
* -w com.android.frameworks.downloadmanagertests/.DownloadManagerTestRunner
|
||||
*/
|
||||
|
||||
public class DownloadManagerTestRunner extends InstrumentationTestRunner {
|
||||
private static final String EXTERNAL_DOWNLOAD_URI_KEY = "external_download_uri";
|
||||
public String externalDownloadUriValue = null;
|
||||
|
||||
@Override
|
||||
public TestSuite getAllTests() {
|
||||
TestSuite suite = new InstrumentationTestSuite(this);
|
||||
suite.addTestSuite(DownloadManagerTestApp.class);
|
||||
return suite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getLoader() {
|
||||
return DownloadManagerTestRunner.class.getClassLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
// Extract the extra params passed in from the bundle...
|
||||
String externalDownloadUri = (String) icicle.get(EXTERNAL_DOWNLOAD_URI_KEY);
|
||||
if (externalDownloadUri != null) {
|
||||
externalDownloadUriValue = externalDownloadUri;
|
||||
}
|
||||
super.onCreate(icicle);
|
||||
}
|
||||
|
||||
}
|
27
core/tests/utillib/Android.mk
Normal file
27
core/tests/utillib/Android.mk
Normal file
@ -0,0 +1,27 @@
|
||||
# Copyright (C) 2010 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-subdir-java-files)
|
||||
|
||||
LOCAL_MODULE := frameworks-core-util-lib
|
||||
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||
|
||||
# Build the test APKs using their own makefiles
|
||||
include $(call all-makefiles-under,$(LOCAL_PATH))
|
||||
|
239
core/tests/utillib/src/coretestutils/http/MockResponse.java
Normal file
239
core/tests/utillib/src/coretestutils/http/MockResponse.java
Normal file
@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package coretestutils.http;
|
||||
|
||||
import static coretestutils.http.MockWebServer.ASCII;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A scripted response to be replayed by the mock web server.
|
||||
*/
|
||||
public class MockResponse {
|
||||
private static final byte[] EMPTY_BODY = new byte[0];
|
||||
static final String LOG_TAG = "coretestutils.http.MockResponse";
|
||||
|
||||
private String status = "HTTP/1.1 200 OK";
|
||||
private Map<String, String> headers = new HashMap<String, String>();
|
||||
private byte[] body = EMPTY_BODY;
|
||||
private boolean closeConnectionAfter = false;
|
||||
private String closeConnectionAfterHeader = null;
|
||||
private int closeConnectionAfterXBytes = -1;
|
||||
private int pauseConnectionAfterXBytes = -1;
|
||||
private File bodyExternalFile = null;
|
||||
|
||||
public MockResponse() {
|
||||
addHeader("Content-Length", 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP response line, such as "HTTP/1.1 200 OK".
|
||||
*/
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public MockResponse setResponseCode(int code) {
|
||||
this.status = "HTTP/1.1 " + code + " OK";
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HTTP headers, such as "Content-Length: 0".
|
||||
*/
|
||||
public List<String> getHeaders() {
|
||||
List<String> headerStrings = new ArrayList<String>();
|
||||
for (String header : headers.keySet()) {
|
||||
headerStrings.add(header + ": " + headers.get(header));
|
||||
}
|
||||
return headerStrings;
|
||||
}
|
||||
|
||||
public MockResponse addHeader(String header, String value) {
|
||||
headers.put(header.toLowerCase(), value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockResponse addHeader(String header, long value) {
|
||||
return addHeader(header, Long.toString(value));
|
||||
}
|
||||
|
||||
public MockResponse removeHeader(String header) {
|
||||
headers.remove(header.toLowerCase());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the body should come from an external file, false otherwise.
|
||||
*/
|
||||
private boolean bodyIsExternal() {
|
||||
return bodyExternalFile != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an input stream containing the raw HTTP payload.
|
||||
*/
|
||||
public InputStream getBody() {
|
||||
if (bodyIsExternal()) {
|
||||
try {
|
||||
return new FileInputStream(bodyExternalFile);
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e(LOG_TAG, "File not found: " + bodyExternalFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
return new ByteArrayInputStream(this.body);
|
||||
}
|
||||
|
||||
public MockResponse setBody(File body) {
|
||||
addHeader("Content-Length", body.length());
|
||||
this.bodyExternalFile = body;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockResponse setBody(byte[] body) {
|
||||
addHeader("Content-Length", body.length);
|
||||
this.body = body;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockResponse setBody(String body) {
|
||||
try {
|
||||
return setBody(body.getBytes(ASCII));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the body as chunked.
|
||||
*
|
||||
* Currently chunked body is not supported for external files as bodies.
|
||||
*/
|
||||
public MockResponse setChunkedBody(byte[] body, int maxChunkSize) throws IOException {
|
||||
addHeader("Transfer-encoding", "chunked");
|
||||
|
||||
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
|
||||
int pos = 0;
|
||||
while (pos < body.length) {
|
||||
int chunkSize = Math.min(body.length - pos, maxChunkSize);
|
||||
bytesOut.write(Integer.toHexString(chunkSize).getBytes(ASCII));
|
||||
bytesOut.write("\r\n".getBytes(ASCII));
|
||||
bytesOut.write(body, pos, chunkSize);
|
||||
bytesOut.write("\r\n".getBytes(ASCII));
|
||||
pos += chunkSize;
|
||||
}
|
||||
bytesOut.write("0\r\n".getBytes(ASCII));
|
||||
this.body = bytesOut.toByteArray();
|
||||
return this;
|
||||
}
|
||||
|
||||
public MockResponse setChunkedBody(String body, int maxChunkSize) throws IOException {
|
||||
return setChunkedBody(body.getBytes(ASCII), maxChunkSize);
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public boolean shouldCloseConnectionAfter() {
|
||||
return closeConnectionAfter;
|
||||
}
|
||||
|
||||
public MockResponse setCloseConnectionAfter(boolean closeConnectionAfter) {
|
||||
this.closeConnectionAfter = closeConnectionAfter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the header after which sending the server should close the connection.
|
||||
*/
|
||||
public MockResponse setCloseConnectionAfterHeader(String header) {
|
||||
closeConnectionAfterHeader = header;
|
||||
setCloseConnectionAfter(true);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the header after which sending the server should close the connection.
|
||||
*/
|
||||
public String getCloseConnectionAfterHeader() {
|
||||
return closeConnectionAfterHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of bytes in the body to send before which the server should close the
|
||||
* connection. Set to -1 to unset and send the entire body (default).
|
||||
*/
|
||||
public MockResponse setCloseConnectionAfterXBytes(int position) {
|
||||
closeConnectionAfterXBytes = position;
|
||||
setCloseConnectionAfter(true);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes in the body to send before which the server should close the
|
||||
* connection. Returns -1 if the entire body should be sent (default).
|
||||
*/
|
||||
public int getCloseConnectionAfterXBytes() {
|
||||
return closeConnectionAfterXBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of bytes in the body to send before which the server should pause the
|
||||
* connection (stalls in sending data). Only one pause per response is supported.
|
||||
* Set to -1 to unset pausing (default).
|
||||
*/
|
||||
public MockResponse setPauseConnectionAfterXBytes(int position) {
|
||||
pauseConnectionAfterXBytes = position;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes in the body to send before which the server should pause the
|
||||
* connection (stalls in sending data). (Returns -1 if it should not pause).
|
||||
*/
|
||||
public int getPauseConnectionAfterXBytes() {
|
||||
return pauseConnectionAfterXBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this response is flagged to pause the connection mid-stream, false otherwise
|
||||
*/
|
||||
public boolean getShouldPause() {
|
||||
return (pauseConnectionAfterXBytes != -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this response is flagged to close the connection mid-stream, false otherwise
|
||||
*/
|
||||
public boolean getShouldClose() {
|
||||
return (closeConnectionAfterXBytes != -1);
|
||||
}
|
||||
}
|
426
core/tests/utillib/src/coretestutils/http/MockWebServer.java
Normal file
426
core/tests/utillib/src/coretestutils/http/MockWebServer.java
Normal file
@ -0,0 +1,426 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package coretestutils.http;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A scriptable web server. Callers supply canned responses and the server
|
||||
* replays them upon request in sequence.
|
||||
*
|
||||
* TODO: merge with the version from libcore/support/src/tests/java once it's in.
|
||||
*/
|
||||
public final class MockWebServer {
|
||||
static final String ASCII = "US-ASCII";
|
||||
static final String LOG_TAG = "coretestutils.http.MockWebServer";
|
||||
|
||||
private final BlockingQueue<RecordedRequest> requestQueue
|
||||
= new LinkedBlockingQueue<RecordedRequest>();
|
||||
private final BlockingQueue<MockResponse> responseQueue
|
||||
= new LinkedBlockingQueue<MockResponse>();
|
||||
private int bodyLimit = Integer.MAX_VALUE;
|
||||
private final ExecutorService executor = Executors.newCachedThreadPool();
|
||||
// keep Futures around so we can rethrow any exceptions thrown by Callables
|
||||
private final Queue<Future<?>> futures = new LinkedList<Future<?>>();
|
||||
private final Object downloadPauseLock = new Object();
|
||||
// global flag to signal when downloads should resume on the server
|
||||
private volatile boolean downloadResume = false;
|
||||
|
||||
private int port = -1;
|
||||
|
||||
public int getPort() {
|
||||
if (port == -1) {
|
||||
throw new IllegalStateException("Cannot retrieve port before calling play()");
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a URL for connecting to this server.
|
||||
*
|
||||
* @param path the request path, such as "/".
|
||||
*/
|
||||
public URL getUrl(String path) throws MalformedURLException {
|
||||
return new URL("http://localhost:" + getPort() + path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of bytes of the POST body to keep in memory to the given
|
||||
* limit.
|
||||
*/
|
||||
public void setBodyLimit(int maxBodyLength) {
|
||||
this.bodyLimit = maxBodyLength;
|
||||
}
|
||||
|
||||
public void enqueue(MockResponse response) {
|
||||
responseQueue.add(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Awaits the next HTTP request, removes it, and returns it. Callers should
|
||||
* use this to verify the request sent was as intended.
|
||||
*/
|
||||
public RecordedRequest takeRequest() throws InterruptedException {
|
||||
return requestQueue.take();
|
||||
}
|
||||
|
||||
public RecordedRequest takeRequestWithTimeout(long timeoutMillis) throws InterruptedException {
|
||||
return requestQueue.poll(timeoutMillis, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public List<RecordedRequest> drainRequests() {
|
||||
List<RecordedRequest> requests = new ArrayList<RecordedRequest>();
|
||||
requestQueue.drainTo(requests);
|
||||
return requests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the server, serves all enqueued requests, and shuts the server
|
||||
* down using the default (server-assigned) port.
|
||||
*/
|
||||
public void play() throws IOException {
|
||||
play(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the server, serves all enqueued requests, and shuts the server
|
||||
* down.
|
||||
*
|
||||
* @param port The port number to use to listen to connections on; pass in 0 to have the
|
||||
* server automatically assign a free port
|
||||
*/
|
||||
public void play(int portNumber) throws IOException {
|
||||
final ServerSocket ss = new ServerSocket(portNumber);
|
||||
ss.setReuseAddress(true);
|
||||
port = ss.getLocalPort();
|
||||
submitCallable(new Callable<Void>() {
|
||||
public Void call() throws Exception {
|
||||
int count = 0;
|
||||
while (true) {
|
||||
if (count > 0 && responseQueue.isEmpty()) {
|
||||
ss.close();
|
||||
executor.shutdown();
|
||||
return null;
|
||||
}
|
||||
|
||||
serveConnection(ss.accept());
|
||||
count++;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void serveConnection(final Socket s) {
|
||||
submitCallable(new Callable<Void>() {
|
||||
public Void call() throws Exception {
|
||||
InputStream in = new BufferedInputStream(s.getInputStream());
|
||||
OutputStream out = new BufferedOutputStream(s.getOutputStream());
|
||||
|
||||
int sequenceNumber = 0;
|
||||
while (true) {
|
||||
RecordedRequest request = readRequest(in, sequenceNumber);
|
||||
if (request == null) {
|
||||
if (sequenceNumber == 0) {
|
||||
throw new IllegalStateException("Connection without any request!");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
requestQueue.add(request);
|
||||
MockResponse response = computeResponse(request);
|
||||
writeResponse(out, response);
|
||||
if (response.shouldCloseConnectionAfter()) {
|
||||
break;
|
||||
}
|
||||
sequenceNumber++;
|
||||
}
|
||||
|
||||
in.close();
|
||||
out.close();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void submitCallable(Callable<?> callable) {
|
||||
Future<?> future = executor.submit(callable);
|
||||
futures.add(future);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for and raise any exceptions that have been thrown by child threads. Will not block on
|
||||
* children still running.
|
||||
* @throws ExecutionException for the first child thread that threw an exception
|
||||
*/
|
||||
public void checkForExceptions() throws ExecutionException, InterruptedException {
|
||||
final int originalSize = futures.size();
|
||||
for (int i = 0; i < originalSize; i++) {
|
||||
Future<?> future = futures.remove();
|
||||
try {
|
||||
future.get(0, TimeUnit.SECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
futures.add(future); // still running
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sequenceNumber the index of this request on this connection.
|
||||
*/
|
||||
private RecordedRequest readRequest(InputStream in, int sequenceNumber) throws IOException {
|
||||
String request = readAsciiUntilCrlf(in);
|
||||
if (request.equals("")) {
|
||||
return null; // end of data; no more requests
|
||||
}
|
||||
|
||||
List<String> headers = new ArrayList<String>();
|
||||
int contentLength = -1;
|
||||
boolean chunked = false;
|
||||
String header;
|
||||
while (!(header = readAsciiUntilCrlf(in)).equals("")) {
|
||||
headers.add(header);
|
||||
String lowercaseHeader = header.toLowerCase();
|
||||
if (contentLength == -1 && lowercaseHeader.startsWith("content-length:")) {
|
||||
contentLength = Integer.parseInt(header.substring(15).trim());
|
||||
}
|
||||
if (lowercaseHeader.startsWith("transfer-encoding:") &&
|
||||
lowercaseHeader.substring(18).trim().equals("chunked")) {
|
||||
chunked = true;
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasBody = false;
|
||||
TruncatingOutputStream requestBody = new TruncatingOutputStream();
|
||||
List<Integer> chunkSizes = new ArrayList<Integer>();
|
||||
if (contentLength != -1) {
|
||||
hasBody = true;
|
||||
transfer(contentLength, in, requestBody);
|
||||
} else if (chunked) {
|
||||
hasBody = true;
|
||||
while (true) {
|
||||
int chunkSize = Integer.parseInt(readAsciiUntilCrlf(in).trim(), 16);
|
||||
if (chunkSize == 0) {
|
||||
readEmptyLine(in);
|
||||
break;
|
||||
}
|
||||
chunkSizes.add(chunkSize);
|
||||
transfer(chunkSize, in, requestBody);
|
||||
readEmptyLine(in);
|
||||
}
|
||||
}
|
||||
|
||||
if (request.startsWith("GET ")) {
|
||||
if (hasBody) {
|
||||
throw new IllegalArgumentException("GET requests should not have a body!");
|
||||
}
|
||||
} else if (request.startsWith("POST ")) {
|
||||
if (!hasBody) {
|
||||
throw new IllegalArgumentException("POST requests must have a body!");
|
||||
}
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Unexpected method: " + request);
|
||||
}
|
||||
|
||||
return new RecordedRequest(request, headers, chunkSizes,
|
||||
requestBody.numBytesReceived, requestBody.toByteArray(), sequenceNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a response to satisfy {@code request}.
|
||||
*/
|
||||
private MockResponse computeResponse(RecordedRequest request) throws InterruptedException {
|
||||
if (responseQueue.isEmpty()) {
|
||||
throw new IllegalStateException("Unexpected request: " + request);
|
||||
}
|
||||
return responseQueue.take();
|
||||
}
|
||||
|
||||
private void writeResponse(OutputStream out, MockResponse response) throws IOException {
|
||||
out.write((response.getStatus() + "\r\n").getBytes(ASCII));
|
||||
boolean doCloseConnectionAfterHeader = (response.getCloseConnectionAfterHeader() != null);
|
||||
|
||||
// Send headers
|
||||
String closeConnectionAfterHeader = response.getCloseConnectionAfterHeader();
|
||||
for (String header : response.getHeaders()) {
|
||||
out.write((header + "\r\n").getBytes(ASCII));
|
||||
|
||||
if (doCloseConnectionAfterHeader && header.startsWith(closeConnectionAfterHeader)) {
|
||||
Log.i(LOG_TAG, "Closing connection after header" + header);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Send actual body data
|
||||
if (!doCloseConnectionAfterHeader) {
|
||||
out.write(("\r\n").getBytes(ASCII));
|
||||
|
||||
InputStream body = response.getBody();
|
||||
final int READ_BLOCK_SIZE = 10000; // process blocks this size
|
||||
byte[] currentBlock = new byte[READ_BLOCK_SIZE];
|
||||
int currentBlockSize = 0;
|
||||
int writtenSoFar = 0;
|
||||
|
||||
boolean shouldPause = response.getShouldPause();
|
||||
boolean shouldClose = response.getShouldClose();
|
||||
int pause = response.getPauseConnectionAfterXBytes();
|
||||
int close = response.getCloseConnectionAfterXBytes();
|
||||
|
||||
// Don't bother pausing if it's set to pause -after- the connection should be dropped
|
||||
if (shouldPause && shouldClose && (pause > close)) {
|
||||
shouldPause = false;
|
||||
}
|
||||
|
||||
// Process each block we read in...
|
||||
while ((currentBlockSize = body.read(currentBlock)) != -1) {
|
||||
int startIndex = 0;
|
||||
int writeLength = currentBlockSize;
|
||||
|
||||
// handle the case of pausing
|
||||
if (shouldPause && (writtenSoFar + currentBlockSize >= pause)) {
|
||||
writeLength = pause - writtenSoFar;
|
||||
out.write(currentBlock, 0, writeLength);
|
||||
out.flush();
|
||||
writtenSoFar += writeLength;
|
||||
|
||||
// now pause...
|
||||
try {
|
||||
Log.i(LOG_TAG, "Pausing connection after " + pause + " bytes");
|
||||
// Wait until someone tells us to resume sending...
|
||||
synchronized(downloadPauseLock) {
|
||||
while (!downloadResume) {
|
||||
downloadPauseLock.wait();
|
||||
}
|
||||
// reset resume back to false
|
||||
downloadResume = false;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(LOG_TAG, "Server was interrupted during pause in download.");
|
||||
}
|
||||
|
||||
startIndex = writeLength;
|
||||
writeLength = currentBlockSize - writeLength;
|
||||
}
|
||||
|
||||
// handle the case of closing the connection
|
||||
if (shouldClose && (writtenSoFar + writeLength > close)) {
|
||||
writeLength = close - writtenSoFar;
|
||||
out.write(currentBlock, startIndex, writeLength);
|
||||
writtenSoFar += writeLength;
|
||||
Log.i(LOG_TAG, "Closing connection after " + close + " bytes");
|
||||
break;
|
||||
}
|
||||
out.write(currentBlock, startIndex, writeLength);
|
||||
writtenSoFar += writeLength;
|
||||
}
|
||||
}
|
||||
out.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer bytes from {@code in} to {@code out} until either {@code length}
|
||||
* bytes have been transferred or {@code in} is exhausted.
|
||||
*/
|
||||
private void transfer(int length, InputStream in, OutputStream out) throws IOException {
|
||||
byte[] buffer = new byte[1024];
|
||||
while (length > 0) {
|
||||
int count = in.read(buffer, 0, Math.min(buffer.length, length));
|
||||
if (count == -1) {
|
||||
return;
|
||||
}
|
||||
out.write(buffer, 0, count);
|
||||
length -= count;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text from {@code in} until the next "\r\n", or null if
|
||||
* {@code in} is exhausted.
|
||||
*/
|
||||
private String readAsciiUntilCrlf(InputStream in) throws IOException {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
while (true) {
|
||||
int c = in.read();
|
||||
if (c == '\n' && builder.length() > 0 && builder.charAt(builder.length() - 1) == '\r') {
|
||||
builder.deleteCharAt(builder.length() - 1);
|
||||
return builder.toString();
|
||||
} else if (c == -1) {
|
||||
return builder.toString();
|
||||
} else {
|
||||
builder.append((char) c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readEmptyLine(InputStream in) throws IOException {
|
||||
String line = readAsciiUntilCrlf(in);
|
||||
if (!line.equals("")) {
|
||||
throw new IllegalStateException("Expected empty but was: " + line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An output stream that drops data after bodyLimit bytes.
|
||||
*/
|
||||
private class TruncatingOutputStream extends ByteArrayOutputStream {
|
||||
private int numBytesReceived = 0;
|
||||
@Override public void write(byte[] buffer, int offset, int len) {
|
||||
numBytesReceived += len;
|
||||
super.write(buffer, offset, Math.min(len, bodyLimit - count));
|
||||
}
|
||||
@Override public void write(int oneByte) {
|
||||
numBytesReceived++;
|
||||
if (count < bodyLimit) {
|
||||
super.write(oneByte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger the server to resume sending the download
|
||||
*/
|
||||
public void doResumeDownload() {
|
||||
synchronized (downloadPauseLock) {
|
||||
downloadResume = true;
|
||||
downloadPauseLock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package coretestutils.http;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An HTTP request that came into the mock web server.
|
||||
*/
|
||||
public final class RecordedRequest {
|
||||
private final String requestLine;
|
||||
private final List<String> headers;
|
||||
private final List<Integer> chunkSizes;
|
||||
private final int bodySize;
|
||||
private final byte[] body;
|
||||
private final int sequenceNumber;
|
||||
|
||||
RecordedRequest(String requestLine, List<String> headers, List<Integer> chunkSizes,
|
||||
int bodySize, byte[] body, int sequenceNumber) {
|
||||
this.requestLine = requestLine;
|
||||
this.headers = headers;
|
||||
this.chunkSizes = chunkSizes;
|
||||
this.bodySize = bodySize;
|
||||
this.body = body;
|
||||
this.sequenceNumber = sequenceNumber;
|
||||
}
|
||||
|
||||
public String getRequestLine() {
|
||||
return requestLine;
|
||||
}
|
||||
|
||||
public List<String> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sizes of the chunks of this request's body, or an empty list
|
||||
* if the request's body was empty or unchunked.
|
||||
*/
|
||||
public List<Integer> getChunkSizes() {
|
||||
return chunkSizes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total size of the body of this POST request (before
|
||||
* truncation).
|
||||
*/
|
||||
public int getBodySize() {
|
||||
return bodySize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the body of this POST request. This may be truncated.
|
||||
*/
|
||||
public byte[] getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of this request on its HTTP connection. Since a single
|
||||
* HTTP connection may serve multiple requests, each request is assigned its
|
||||
* own sequence number.
|
||||
*/
|
||||
public int getSequenceNumber() {
|
||||
return sequenceNumber;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return requestLine;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return getRequestLine().split(" ")[0];
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return getRequestLine().split(" ")[1];
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user