mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 835803 - Add tests for downloads whose size is zero bytes. r=enn
--HG-- extra : rebase_source : ed691306ee8976dabcb77a3947eec660a7a6e02e
This commit is contained in:
parent
342dd989e7
commit
7c2b4f05be
@ -601,6 +601,13 @@ DownloadCopySaver.prototype = {
|
||||
onStartRequest: function DCSE_onStartRequest(aRequest, aContext)
|
||||
{
|
||||
backgroundFileSaver.onStartRequest(aRequest, aContext);
|
||||
|
||||
// Ensure we report the value of "Content-Length", if available, even
|
||||
// if the download doesn't generate any progress events later.
|
||||
if (aRequest instanceof Ci.nsIChannel &&
|
||||
aRequest.contentLength >= 0) {
|
||||
aSetProgressBytesFn(0, aRequest.contentLength);
|
||||
}
|
||||
},
|
||||
onStopRequest: function DCSE_onStopRequest(aRequest, aContext,
|
||||
aStatusCode)
|
||||
|
@ -11,6 +11,7 @@ relativesrcdir = @relativesrcdir@
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
FILES = \
|
||||
empty.txt \
|
||||
source.txt \
|
||||
$(NULL)
|
||||
|
||||
|
0
toolkit/components/jsdownloads/test/data/empty.txt
Normal file
0
toolkit/components/jsdownloads/test/data/empty.txt
Normal file
@ -46,8 +46,13 @@ const FAKE_SERVER_PORT = 4445;
|
||||
const FAKE_BASE = "http://localhost:" + FAKE_SERVER_PORT;
|
||||
|
||||
const TEST_SOURCE_URI = NetUtil.newURI(HTTP_BASE + "/source.txt");
|
||||
const TEST_EMPTY_URI = NetUtil.newURI(HTTP_BASE + "/empty.txt");
|
||||
const TEST_FAKE_SOURCE_URI = NetUtil.newURI(FAKE_BASE + "/source.txt");
|
||||
|
||||
const TEST_EMPTY_NOPROGRESS_PATH = "/empty-noprogress.txt";
|
||||
const TEST_EMPTY_NOPROGRESS_URI = NetUtil.newURI(HTTP_BASE +
|
||||
TEST_EMPTY_NOPROGRESS_PATH);
|
||||
|
||||
const TEST_INTERRUPTIBLE_PATH = "/interruptible.txt";
|
||||
const TEST_INTERRUPTIBLE_URI = NetUtil.newURI(HTTP_BASE +
|
||||
TEST_INTERRUPTIBLE_PATH);
|
||||
@ -90,6 +95,20 @@ function getTempFile(aLeafName)
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for pending events to be processed.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When pending events have been processed.
|
||||
* @rejects Never.
|
||||
*/
|
||||
function promiseExecuteSoon()
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
do_execute_soon(deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Download object, using TEST_TARGET_FILE_NAME as the target.
|
||||
* The target is deleted by getTempFile when this function is called.
|
||||
@ -162,8 +181,15 @@ function startFakeServer()
|
||||
* This function allows testing events or actions that need to happen in the
|
||||
* middle of a download.
|
||||
*
|
||||
* Calling this function registers a new request handler in the internal HTTP
|
||||
* server, accessible at the TEST_INTERRUPTIBLE_URI address. The HTTP handler
|
||||
* Normally, the internal HTTP server returns all the available data as soon as
|
||||
* a request is received. In order for some requests to be served one part at a
|
||||
* time, special interruptible handlers are registered on the HTTP server.
|
||||
*
|
||||
* Before making a request to one of the addresses served by the interruptible
|
||||
* handlers, you may call "deferNextResponse" to get a reference to an object
|
||||
* that allows you to control the next request.
|
||||
*
|
||||
* For example, the handler accessible at the TEST_INTERRUPTIBLE_URI address
|
||||
* returns the TEST_DATA_SHORT text, then waits until the "resolve" method is
|
||||
* called on the object returned by the function. At this point, the handler
|
||||
* sends the TEST_DATA_SHORT text again to complete the response.
|
||||
@ -172,32 +198,85 @@ function startFakeServer()
|
||||
* response midway. Because of how the network layer is implemented, this does
|
||||
* not cause the socket to return an error.
|
||||
*
|
||||
* The handler is unregistered when the response finishes or is interrupted.
|
||||
*
|
||||
* @returns Deferred object used to control the response.
|
||||
*/
|
||||
function startInterruptibleResponseHandler()
|
||||
function deferNextResponse()
|
||||
{
|
||||
let deferResponse = Promise.defer();
|
||||
gHttpServer.registerPathHandler(TEST_INTERRUPTIBLE_PATH,
|
||||
function (aRequest, aResponse)
|
||||
{
|
||||
aResponse.processAsync();
|
||||
aResponse.setHeader("Content-Type", "text/plain", false);
|
||||
aResponse.setHeader("Content-Length", "" + (TEST_DATA_SHORT.length * 2),
|
||||
false);
|
||||
aResponse.write(TEST_DATA_SHORT);
|
||||
do_print("Interruptible request will be controlled.");
|
||||
|
||||
deferResponse.promise.then(function SIRH_onSuccess() {
|
||||
aResponse.write(TEST_DATA_SHORT);
|
||||
aResponse.finish();
|
||||
gHttpServer.registerPathHandler(TEST_INTERRUPTIBLE_PATH, null);
|
||||
}, function SIRH_onFailure() {
|
||||
aResponse.abort();
|
||||
gHttpServer.registerPathHandler(TEST_INTERRUPTIBLE_PATH, null);
|
||||
});
|
||||
// Store an internal reference that should not be used directly by tests.
|
||||
if (!deferNextResponse._deferred) {
|
||||
deferNextResponse._deferred = Promise.defer();
|
||||
}
|
||||
return deferNextResponse._deferred;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that is resolved when the next interruptible response
|
||||
* handler has received the request, and has started sending the first part of
|
||||
* the response. The response might not have been received by the client yet.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @resolves When the next request has been received.
|
||||
* @rejects Never.
|
||||
*/
|
||||
function promiseNextRequestReceived()
|
||||
{
|
||||
do_print("Requested notification when interruptible request is received.");
|
||||
|
||||
// Store an internal reference that should not be used directly by tests.
|
||||
promiseNextRequestReceived._deferred = Promise.defer();
|
||||
return promiseNextRequestReceived._deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an interruptible response handler.
|
||||
*
|
||||
* @param aPath
|
||||
* Path passed to nsIHttpServer.registerPathHandler.
|
||||
* @param aFirstPartFn
|
||||
* This function is called when the response is received, with the
|
||||
* aRequest and aResponse arguments of the server.
|
||||
* @param aSecondPartFn
|
||||
* This function is called after the "resolve" method of the object
|
||||
* returned by deferNextResponse is called. This function is called with
|
||||
* the aRequest and aResponse arguments of the server.
|
||||
*/
|
||||
function registerInterruptibleHandler(aPath, aFirstPartFn, aSecondPartFn)
|
||||
{
|
||||
gHttpServer.registerPathHandler(aPath, function (aRequest, aResponse) {
|
||||
// Get a reference to the controlling object for this request. If the
|
||||
// deferNextResponse function was not called, interrupt the test.
|
||||
let deferResponse = deferNextResponse._deferred;
|
||||
deferNextResponse._deferred = null;
|
||||
if (deferResponse) {
|
||||
do_print("Interruptible request started under control.");
|
||||
} else {
|
||||
do_print("Interruptible request started without being controlled.");
|
||||
deferResponse = Promise.defer();
|
||||
deferResponse.resolve();
|
||||
}
|
||||
|
||||
// Process the first part of the response.
|
||||
aResponse.processAsync();
|
||||
aFirstPartFn(aRequest, aResponse);
|
||||
|
||||
if (promiseNextRequestReceived._deferred) {
|
||||
do_print("Notifying that interruptible request has been received.");
|
||||
promiseNextRequestReceived._deferred.resolve();
|
||||
promiseNextRequestReceived._deferred = null;
|
||||
}
|
||||
|
||||
// Wait on the deferred object, then finish or abort the request.
|
||||
deferResponse.promise.then(function RIH_onSuccess() {
|
||||
aSecondPartFn(aRequest, aResponse);
|
||||
aResponse.finish();
|
||||
do_print("Interruptible request finished.");
|
||||
}, function RIH_onFailure() {
|
||||
aResponse.abort();
|
||||
do_print("Interruptible request aborted.");
|
||||
});
|
||||
return deferResponse;
|
||||
});
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -211,4 +290,19 @@ add_task(function test_common_initialize()
|
||||
gHttpServer = new HttpServer();
|
||||
gHttpServer.registerDirectory("/", do_get_file("../data"));
|
||||
gHttpServer.start(HTTP_SERVER_PORT);
|
||||
|
||||
registerInterruptibleHandler(TEST_INTERRUPTIBLE_PATH,
|
||||
function firstPart(aRequest, aResponse) {
|
||||
aResponse.setHeader("Content-Type", "text/plain", false);
|
||||
aResponse.setHeader("Content-Length", "" + (TEST_DATA_SHORT.length * 2),
|
||||
false);
|
||||
aResponse.write(TEST_DATA_SHORT);
|
||||
}, function secondPart(aRequest, aResponse) {
|
||||
aResponse.write(TEST_DATA_SHORT);
|
||||
});
|
||||
|
||||
registerInterruptibleHandler(TEST_EMPTY_NOPROGRESS_PATH,
|
||||
function firstPart(aRequest, aResponse) {
|
||||
aResponse.setHeader("Content-Type", "text/plain", false);
|
||||
}, function secondPart(aRequest, aResponse) { });
|
||||
});
|
||||
|
@ -88,7 +88,7 @@ add_task(function test_download_final_state_notified()
|
||||
*/
|
||||
add_task(function test_download_intermediate_progress()
|
||||
{
|
||||
let interruptible = startInterruptibleResponseHandler();
|
||||
let deferResponse = deferNextResponse();
|
||||
|
||||
let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI);
|
||||
|
||||
@ -99,7 +99,7 @@ add_task(function test_download_intermediate_progress()
|
||||
do_check_eq(download.totalBytes, TEST_DATA_SHORT.length * 2);
|
||||
|
||||
// Continue after the first chunk of data is fully received.
|
||||
interruptible.resolve();
|
||||
deferResponse.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
@ -113,6 +113,71 @@ add_task(function test_download_intermediate_progress()
|
||||
TEST_DATA_SHORT + TEST_DATA_SHORT);
|
||||
});
|
||||
|
||||
/**
|
||||
* Downloads a file with a "Content-Length" of 0 and checks the progress.
|
||||
*/
|
||||
add_task(function test_download_empty_progress()
|
||||
{
|
||||
let download = yield promiseSimpleDownload(TEST_EMPTY_URI);
|
||||
|
||||
yield download.start();
|
||||
|
||||
do_check_true(download.stopped);
|
||||
do_check_true(download.hasProgress);
|
||||
do_check_eq(download.progress, 100);
|
||||
do_check_eq(download.currentBytes, 0);
|
||||
do_check_eq(download.totalBytes, 0);
|
||||
|
||||
do_check_eq(download.target.file.fileSize, 0);
|
||||
});
|
||||
|
||||
/**
|
||||
* Downloads an empty file with no "Content-Length" and checks the progress.
|
||||
*/
|
||||
add_task(function test_download_empty_noprogress()
|
||||
{
|
||||
let deferResponse = deferNextResponse();
|
||||
let promiseEmptyRequestReceived = promiseNextRequestReceived();
|
||||
|
||||
let download = yield promiseSimpleDownload(TEST_EMPTY_NOPROGRESS_URI);
|
||||
|
||||
download.onchange = function () {
|
||||
if (!download.stopped) {
|
||||
do_check_false(download.hasProgress);
|
||||
do_check_eq(download.currentBytes, 0);
|
||||
do_check_eq(download.totalBytes, 0);
|
||||
}
|
||||
};
|
||||
|
||||
// Start the download, while waiting for the request to be received.
|
||||
let promiseAttempt = download.start();
|
||||
|
||||
// Wait for the request to be received by the HTTP server, but don't allow the
|
||||
// request to finish yet. Before checking the download state, wait for the
|
||||
// events to be processed by the client.
|
||||
yield promiseEmptyRequestReceived;
|
||||
yield promiseExecuteSoon();
|
||||
|
||||
// Check that this download has no progress report.
|
||||
do_check_false(download.stopped);
|
||||
do_check_false(download.hasProgress);
|
||||
do_check_eq(download.currentBytes, 0);
|
||||
do_check_eq(download.totalBytes, 0);
|
||||
|
||||
// Now allow the response to finish, and wait for the download to complete.
|
||||
deferResponse.resolve();
|
||||
yield promiseAttempt;
|
||||
|
||||
// Verify the state of the completed download.
|
||||
do_check_true(download.stopped);
|
||||
do_check_false(download.hasProgress);
|
||||
do_check_eq(download.progress, 100);
|
||||
do_check_eq(download.currentBytes, 0);
|
||||
do_check_eq(download.totalBytes, 0);
|
||||
|
||||
do_check_eq(download.target.file.fileSize, 0);
|
||||
});
|
||||
|
||||
/**
|
||||
* Calls the "start" method two times before the download is finished.
|
||||
*/
|
||||
@ -121,14 +186,14 @@ add_task(function test_download_start_twice()
|
||||
let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI);
|
||||
|
||||
// Ensure that the download cannot complete before start is called twice.
|
||||
let interruptible = startInterruptibleResponseHandler();
|
||||
let deferResponse = deferNextResponse();
|
||||
|
||||
// Call the start method two times.
|
||||
let promiseAttempt1 = download.start();
|
||||
let promiseAttempt2 = download.start();
|
||||
|
||||
// Allow the download to finish.
|
||||
interruptible.resolve();
|
||||
deferResponse.resolve();
|
||||
|
||||
// Both promises should now be resolved.
|
||||
yield promiseAttempt1;
|
||||
@ -150,7 +215,7 @@ add_task(function test_download_cancel_midway()
|
||||
{
|
||||
let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI);
|
||||
|
||||
let interruptible = startInterruptibleResponseHandler();
|
||||
let deferResponse = deferNextResponse();
|
||||
try {
|
||||
// Cancel the download after receiving the first part of the response.
|
||||
let deferCancel = Promise.defer();
|
||||
@ -190,7 +255,7 @@ add_task(function test_download_cancel_midway()
|
||||
do_check_false(ex.becauseTargetFailed);
|
||||
}
|
||||
} finally {
|
||||
interruptible.reject();
|
||||
deferResponse.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
@ -200,7 +265,7 @@ add_task(function test_download_cancel_midway()
|
||||
add_task(function test_download_cancel_immediately()
|
||||
{
|
||||
// Ensure that the download cannot complete before cancel is called.
|
||||
let interruptible = startInterruptibleResponseHandler();
|
||||
let deferResponse = deferNextResponse();
|
||||
try {
|
||||
let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI);
|
||||
|
||||
@ -230,8 +295,14 @@ add_task(function test_download_cancel_immediately()
|
||||
// Check that the promise returned by the "cancel" method has been resolved.
|
||||
yield promiseCancel;
|
||||
} finally {
|
||||
interruptible.reject();
|
||||
deferResponse.resolve();
|
||||
}
|
||||
|
||||
// Even if we canceled the download immediately, the HTTP request might have
|
||||
// been made, and the internal HTTP handler might be waiting to process it.
|
||||
// Thus, we process any pending events now, to avoid that the request is
|
||||
// processed during the tests that follow, interfering with them.
|
||||
yield promiseExecuteSoon();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -242,7 +313,7 @@ add_task(function test_download_cancel_midway_restart()
|
||||
let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI);
|
||||
|
||||
// The first time, cancel the download midway.
|
||||
let interruptible = startInterruptibleResponseHandler();
|
||||
let deferResponse = deferNextResponse();
|
||||
try {
|
||||
let deferCancel = Promise.defer();
|
||||
download.onchange = function () {
|
||||
@ -253,13 +324,12 @@ add_task(function test_download_cancel_midway_restart()
|
||||
download.start();
|
||||
yield deferCancel.promise;
|
||||
} finally {
|
||||
interruptible.reject();
|
||||
deferResponse.resolve();
|
||||
}
|
||||
|
||||
do_check_true(download.stopped);
|
||||
|
||||
// The second time, we'll provide the entire interruptible response.
|
||||
startInterruptibleResponseHandler().resolve();
|
||||
download.onchange = null;
|
||||
let promiseAttempt = download.start();
|
||||
|
||||
@ -275,9 +345,6 @@ add_task(function test_download_cancel_midway_restart()
|
||||
do_check_eq(download.totalBytes, 0);
|
||||
do_check_eq(download.currentBytes, 0);
|
||||
|
||||
// Allow the second request to complete.
|
||||
interruptible.resolve();
|
||||
|
||||
yield promiseAttempt;
|
||||
|
||||
do_check_true(download.stopped);
|
||||
@ -297,7 +364,7 @@ add_task(function test_download_cancel_immediately_restart_immediately()
|
||||
let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI);
|
||||
|
||||
// Ensure that the download cannot complete before cancel is called.
|
||||
let interruptible = startInterruptibleResponseHandler();
|
||||
let deferResponse = deferNextResponse();
|
||||
|
||||
let promiseAttempt = download.start();
|
||||
do_check_false(download.stopped);
|
||||
@ -318,8 +385,15 @@ add_task(function test_download_cancel_immediately_restart_immediately()
|
||||
do_check_eq(download.totalBytes, 0);
|
||||
do_check_eq(download.currentBytes, 0);
|
||||
|
||||
// Allow the second request to complete.
|
||||
startInterruptibleResponseHandler().resolve();
|
||||
// Even if we canceled the download immediately, the HTTP request might have
|
||||
// been made, and the internal HTTP handler might be waiting to process it.
|
||||
// Thus, we process any pending events now, to avoid that the request is
|
||||
// processed during the tests that follow, interfering with them.
|
||||
yield promiseExecuteSoon();
|
||||
|
||||
// Ensure the next request is now allowed to complete, regardless of whether
|
||||
// the canceled request was received by the server or not.
|
||||
deferResponse.resolve();
|
||||
|
||||
try {
|
||||
yield promiseAttempt;
|
||||
@ -336,7 +410,8 @@ add_task(function test_download_cancel_immediately_restart_immediately()
|
||||
do_check_false(download.canceled);
|
||||
do_check_true(download.error === null);
|
||||
|
||||
do_check_true(download.target.file.exists());
|
||||
yield promiseVerifyContents(download.target.file,
|
||||
TEST_DATA_SHORT + TEST_DATA_SHORT);
|
||||
});
|
||||
|
||||
/**
|
||||
@ -347,7 +422,7 @@ add_task(function test_download_cancel_midway_restart_immediately()
|
||||
let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI);
|
||||
|
||||
// The first time, cancel the download midway.
|
||||
let interruptible = startInterruptibleResponseHandler();
|
||||
let deferResponse = deferNextResponse();
|
||||
|
||||
let deferMidway = Promise.defer();
|
||||
download.onchange = function () {
|
||||
@ -375,11 +450,9 @@ add_task(function test_download_cancel_midway_restart_immediately()
|
||||
do_check_eq(download.totalBytes, 0);
|
||||
do_check_eq(download.currentBytes, 0);
|
||||
|
||||
interruptible.reject();
|
||||
|
||||
// Allow the second request to complete.
|
||||
startInterruptibleResponseHandler().resolve();
|
||||
deferResponse.resolve();
|
||||
|
||||
// The second request is allowed to complete.
|
||||
try {
|
||||
yield promiseAttempt;
|
||||
do_throw("The download should have been canceled.");
|
||||
@ -395,8 +468,6 @@ add_task(function test_download_cancel_midway_restart_immediately()
|
||||
do_check_false(download.canceled);
|
||||
do_check_true(download.error === null);
|
||||
|
||||
do_check_true(download.target.file.exists());
|
||||
|
||||
yield promiseVerifyContents(download.target.file,
|
||||
TEST_DATA_SHORT + TEST_DATA_SHORT);
|
||||
});
|
||||
@ -430,8 +501,7 @@ add_task(function test_download_cancel_twice()
|
||||
let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI);
|
||||
|
||||
// Ensure that the download cannot complete before cancel is called.
|
||||
let interruptible = startInterruptibleResponseHandler();
|
||||
|
||||
let deferResponse = deferNextResponse();
|
||||
try {
|
||||
let promiseAttempt = download.start();
|
||||
do_check_false(download.stopped);
|
||||
@ -459,7 +529,7 @@ add_task(function test_download_cancel_twice()
|
||||
|
||||
do_check_false(download.target.file.exists());
|
||||
} finally {
|
||||
interruptible.reject();
|
||||
deferResponse.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
@ -471,7 +541,7 @@ add_task(function test_download_whenSucceeded()
|
||||
let download = yield promiseSimpleDownload(TEST_INTERRUPTIBLE_URI);
|
||||
|
||||
// Ensure that the download cannot complete before cancel is called.
|
||||
let interruptible = startInterruptibleResponseHandler();
|
||||
let deferResponse = deferNextResponse();
|
||||
|
||||
// Get a reference before the first download attempt.
|
||||
let promiseSucceeded = download.whenSucceeded();
|
||||
@ -480,10 +550,9 @@ add_task(function test_download_whenSucceeded()
|
||||
download.start();
|
||||
yield download.cancel();
|
||||
|
||||
interruptible.reject();
|
||||
deferResponse.resolve();
|
||||
|
||||
// Allow the second request to complete.
|
||||
startInterruptibleResponseHandler().resolve();
|
||||
// The second request is allowed to complete.
|
||||
download.start();
|
||||
|
||||
// Wait for the download to finish by waiting on the whenSucceeded promise.
|
||||
@ -494,7 +563,8 @@ add_task(function test_download_whenSucceeded()
|
||||
do_check_false(download.canceled);
|
||||
do_check_true(download.error === null);
|
||||
|
||||
do_check_true(download.target.file.exists());
|
||||
yield promiseVerifyContents(download.target.file,
|
||||
TEST_DATA_SHORT + TEST_DATA_SHORT);
|
||||
});
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user