From 5ddd8f2c64510839c3bc7887007a67d8fd5697fa Mon Sep 17 00:00:00 2001 From: "Brian R. Bondy" Date: Tue, 6 Nov 2012 20:09:50 -0500 Subject: [PATCH] Bug 791829 - Tests for resuming a stopped/partial update. r=ehsan --- toolkit/mozapps/update/test/shared.js | 3 + .../update/test/unit/test_0030_general.js | 193 +++++++++++++++++- 2 files changed, 195 insertions(+), 1 deletion(-) diff --git a/toolkit/mozapps/update/test/shared.js b/toolkit/mozapps/update/test/shared.js index 9c5223437dd5..024a2732008b 100644 --- a/toolkit/mozapps/update/test/shared.js +++ b/toolkit/mozapps/update/test/shared.js @@ -10,6 +10,7 @@ const AUS_Cc = Components.classes; const AUS_Ci = Components.interfaces; const AUS_Cr = Components.results; const AUS_Cu = Components.utils; +const AUS_Cm = Components.manager; const PREF_APP_UPDATE_AUTO = "app.update.auto"; const PREF_APP_UPDATE_STAGE_ENABLED = "app.update.staging.enabled"; @@ -31,6 +32,8 @@ const PREF_APP_UPDATE_SILENT = "app.update.silent"; const PREF_APP_UPDATE_URL = "app.update.url"; const PREF_APP_UPDATE_URL_DETAILS = "app.update.url.details"; const PREF_APP_UPDATE_URL_OVERRIDE = "app.update.url.override"; +const PREF_APP_UPDATE_SOCKET_ERRORS = "app.update.socket.maxErrors"; +const PREF_APP_UPDATE_RETRY_TIMEOUT = "app.update.socket.retryTimeout"; const PREF_APP_UPDATE_CERT_INVALID_ATTR_NAME = PREF_APP_UPDATE_CERTS_BRANCH + "1.invalidName"; diff --git a/toolkit/mozapps/update/test/unit/test_0030_general.js b/toolkit/mozapps/update/test/unit/test_0030_general.js index 669415158260..9e3f08f00225 100644 --- a/toolkit/mozapps/update/test/unit/test_0030_general.js +++ b/toolkit/mozapps/update/test/unit/test_0030_general.js @@ -5,9 +5,19 @@ /* General MAR File Download Tests */ +const INC_CONTRACT_ID = "@mozilla.org/network/incremental-download;1"; +AUS_Cu.import("resource://gre/modules/FileUtils.jsm"); +AUS_Cu.import("resource://gre/modules/Services.jsm"); +AUS_Cu.import("resource://gre/modules/XPCOMUtils.jsm") + var gNextRunFunc; var gStatusResult; var gExpectedStatusResult; +var gIncrementalDownloadClassID, gIncOldFactory; + +// gIncrementalDownloadErrorType is used to loop through each of the connection +// error types in the Mock incremental downloader. +var gIncrementalDownloadErrorType = 0; function run_test() { do_test_pending(); @@ -29,6 +39,7 @@ function finish_test() { } function end_test() { + cleanupMockIncrementalDownload(); cleanUp(); } @@ -168,7 +179,187 @@ function run_test_pt12() { const arbitraryFileSize = 1024000; setResponseBody("MD5", MD5_HASH_SIMPLE_MAR ,arbitraryFileSize); run_test_helper_pt1("mar download with a valid MD5 hash but invalid file size", - AUS_Cr.NS_ERROR_UNEXPECTED, finish_test); + AUS_Cr.NS_ERROR_UNEXPECTED, run_test_pt13); +} + +var newFactory = { + createInstance: function(aOuter, aIID) { + if (aOuter) + throw Components.results.NS_ERROR_NO_AGGREGATION; + return new IncrementalDownload().QueryInterface(aIID); + }, + lockFactory: function(aLock) { + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + }, + QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsIFactory]) +}; + +function initMockIncrementalDownload() { + var registrar = AUS_Cm.QueryInterface(AUS_Ci.nsIComponentRegistrar); + gIncrementalDownloadClassID = registrar.contractIDToCID(INC_CONTRACT_ID); + gIncOldFactory = AUS_Cm.getClassObject(AUS_Cc[INC_CONTRACT_ID], + AUS_Ci.nsIFactory); + registrar.unregisterFactory(gIncrementalDownloadClassID, gIncOldFactory); + var components = [IncrementalDownload]; + registrar.registerFactory(gIncrementalDownloadClassID, "", + INC_CONTRACT_ID, newFactory); + gIncOldFactory = AUS_Cm.getClassObject(AUS_Cc[INC_CONTRACT_ID], + AUS_Ci.nsIFactory); +} + +function cleanupMockIncrementalDownload() { + if (gIncOldFactory) { + var registrar = AUS_Cm.QueryInterface(AUS_Ci.nsIComponentRegistrar); + registrar.unregisterFactory(gIncrementalDownloadClassID, newFactory); + registrar.registerFactory(gIncrementalDownloadClassID, "", + INC_CONTRACT_ID, gIncOldFactory); + } + gIncOldFactory = null; +} + +/* This Mock incremental downloader is used to verify that connection + * interrupts work correctly in updater code. The implementation of + * the mock incremental downloader is very simple, it simply copies + * the file to the destination location. +*/ + +function IncrementalDownload() { + this.wrappedJSObject = this; +} + +IncrementalDownload.prototype = { + QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsIIncrementalDownload]), + + /* nsIIncrementalDownload */ + init: function(uri, file, chunkSize, intervalInSeconds) { + this._destination = file; + this._URI = uri; + this._finalURI = uri; + }, + + start: function(observer, ctxt) { + var tm = Components.classes["@mozilla.org/thread-manager;1"]. + getService(AUS_Ci.nsIThreadManager); + // Do the actual operation async to give a chance for observers + // to add themselves. + tm.mainThread.dispatch(function() { + this._observer = observer.QueryInterface(AUS_Ci.nsIRequestObserver); + this._ctxt = ctxt; + this._observer.onStartRequest(this, this.ctxt); + let mar = do_get_file("data/" + FILE_SIMPLE_MAR); + mar.copyTo(this._destination.parent, this._destination.leafName); + var status = AUS_Cr.NS_OK + switch (gIncrementalDownloadErrorType++) { + case 0: + status = AUS_Cr.NS_ERROR_NET_RESET; + break; + case 1: + status = AUS_Cr.NS_ERROR_CONNECTION_REFUSED; + break; + case 2: + status = AUS_Cr.NS_ERROR_NET_RESET; + break; + case 3: + status = AUS_Cr.NS_OK; + break; + case 4: + status = AUS_Cr.NS_ERROR_OFFLINE; + // After we report offline, we want to eventually show offline + // status being changed to online. + var tm = Components.classes["@mozilla.org/thread-manager;1"]. + getService(AUS_Ci.nsIThreadManager); + tm.mainThread.dispatch(function() { + Services.obs.notifyObservers(gAUS, + "network:offline-status-changed", + "online"); + }, AUS_Ci.nsIThread.DISPATCH_NORMAL); + break; + } + this._observer.onStopRequest(this, this._ctxt, status); + }.bind(this), AUS_Ci.nsIThread.DISPATCH_NORMAL); + }, + + get URI() { + return this._URI; + }, + + get currentSize() { + throw AUS_Cr.NS_ERROR_NOT_IMPLEMENTED; + }, + + get destination() { + return this._destination; + }, + + get finalURI() { + return this._finalURI; + }, + + get totalSize() { + throw AUS_Cr.NS_ERROR_NOT_IMPLEMENTED; + }, + + /* nsIRequest */ + cancel: function(aStatus) { + throw AUS_Cr.NS_ERROR_NOT_IMPLEMENTED; + }, + suspend: function() { + throw AUS_Cr.NS_ERROR_NOT_IMPLEMENTED; + }, + isPending: function() { + throw AUS_Cr.NS_ERROR_NOT_IMPLEMENTED; + }, + _loadFlags: 0, + get loadFlags() { + return this._loadFlags; + }, + set loadFlags(val) { + this._loadFlags = val; + }, + + _loadGroup: null, + get loadGroup() { + return this._loadGroup; + }, + set loadGroup(val) { + this._loadGroup = val; + }, + + _name: "", + get name() { + return this._name; + }, + + _status: 0, + get status() { + return this._status; + } +} + +// Test disconnecting during an update +function run_test_pt13() { + initMockIncrementalDownload(); + setResponseBody("MD5", MD5_HASH_SIMPLE_MAR); + run_test_helper_pt1("mar download with connection interruption", + AUS_Cr.NS_OK, run_test_pt14); +} + +// Test disconnecting during an update +function run_test_pt14() { + gIncrementalDownloadErrorType = 0; + Services.prefs.setIntPref(PREF_APP_UPDATE_SOCKET_ERRORS, 2); + Services.prefs.setIntPref(PREF_APP_UPDATE_RETRY_TIMEOUT, 0); + setResponseBody("MD5", MD5_HASH_SIMPLE_MAR); + run_test_helper_pt1("mar download with connection interruption without recovery", + AUS_Cr.NS_ERROR_NET_RESET, run_test_pt15); +} + +// Test entering offline mode while downloading +function run_test_pt15() { + gIncrementalDownloadErrorType = 4; + setResponseBody("MD5", MD5_HASH_SIMPLE_MAR); + run_test_helper_pt1("mar download with offline mode", + AUS_Cr.NS_OK, finish_test); } /* Update download listener - nsIRequestObserver */