diff --git a/toolkit/mozapps/update/test/unit/head_update.js b/toolkit/mozapps/update/test/unit/head_update.js index fd6ca13c80f4..60600b115ca0 100644 --- a/toolkit/mozapps/update/test/unit/head_update.js +++ b/toolkit/mozapps/update/test/unit/head_update.js @@ -36,15 +36,25 @@ * ***** END LICENSE BLOCK ***** */ -const NS_APP_USER_PROFILE_50_DIR = "ProfD"; -const NS_APP_PROFILE_DIR_STARTUP = "ProfDS"; - // const Cc, Ci, and Cr are defined in netwerk/test/httpserver/httpd.js so we // need to define unique ones. const AUS_Cc = Components.classes; const AUS_Ci = Components.interfaces; const AUS_Cr = Components.results; +const NS_APP_USER_PROFILE_50_DIR = "ProfD"; +const NS_APP_PROFILE_DIR_STARTUP = "ProfDS"; +const NS_GRE_DIR = "GreD"; + +const MODE_RDONLY = 0x01; +const MODE_WRONLY = 0x02; +const MODE_CREATE = 0x08; +const MODE_APPEND = 0x10; +const MODE_TRUNCATE = 0x20; + +const PERMS_FILE = 0644; +const PERMS_DIRECTORY = 0755; + var gAUS = null; var gUpdateChecker = null; var gPrefs = null; @@ -80,6 +90,44 @@ function startAUS() { */ } +/** + * Writes text to a file. This will replace existing text if the file exists + * and create the file if it doesn't exist. + * @param aFile + * The file to write to. Will be created if it doesn't exist. + * @param aText + * The text to write to the file. If there is existing text it will be + * replaced. + */ +function writeFile(aFile, aText) { + var fos = AUS_Cc["@mozilla.org/network/safe-file-output-stream;1"] + .createInstance(AUS_Ci.nsIFileOutputStream); + if (!aFile.exists()) + aFile.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE); + var modeFlags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE; + fos.init(aFile, modeFlags, PERMS_FILE, 0); + fos.write(aText, aText.length); + closeSafeOutputStream(fos); +} + +/** + * Closes a Safe Output Stream + * @param fos + * The Safe Output Stream to close + */ +function closeSafeOutputStream(aFOS) { + if (aFOS instanceof AUS_Ci.nsISafeOutputStream) { + try { + aFOS.finish(); + } + catch (e) { + aFOS.close(); + } + } + else + aFOS.close(); +} + /** * Toggles network offline. * @@ -184,19 +232,29 @@ xhr.prototype = { * the tests are interrupted. */ function remove_dirs_and_files () { - var fileLocator = AUS_Cc["@mozilla.org/file/directory_service;1"] - .getService(AUS_Ci.nsIProperties); - var dir = fileLocator.get("GreD", AUS_Ci.nsIFile); + var dir = gDirSvc.get(NS_GRE_DIR, AUS_Ci.nsIFile); var file = dir.clone(); file.append("active-update.xml"); - if (file.exists()) - file.remove(false); + try { + if (file.exists()) + file.remove(false); + } + catch (e) { + dump("Unable to remove file\npath: " + file.path + + "\nException: " + e + "\n"); + } file = dir.clone(); file.append("updates.xml"); - if (file.exists()) - file.remove(false); + try { + if (file.exists()) + file.remove(false); + } + catch (e) { + dump("Unable to remove file\npath: " + file.path + + "\nException: " + e + "\n"); + } file = dir.clone(); file.append("updates"); @@ -206,28 +264,54 @@ function remove_dirs_and_files () { file.remove(false); } catch (e) { + dump("Unable to remove file\npath: " + file.path + + "\nException: " + e + "\n"); } - file = dir.clone(); - file.append("updates"); - file.append("0"); - file.append("update.mar"); - try { - if (file.exists()) - file.remove(false); - } - catch (e) { - } + var updatesSubDir = dir.clone(); + updatesSubDir.append("updates"); + updatesSubDir.append("0"); + if (updatesSubDir.exists()) { + file = updatesSubDir.clone(); + file.append("update.mar"); + try { + if (file.exists()) + file.remove(false); + } + catch (e) { + dump("Unable to remove file\npath: " + file.path + + "\nException: " + e + "\n"); + } - file = dir.clone(); - file.append("updates"); - file.append("0"); - file.append("update.status"); - try { - if (file.exists()) - file.remove(false); - } - catch (e) { + file = updatesSubDir.clone(); + file.append("update.status"); + try { + if (file.exists()) + file.remove(false); + } + catch (e) { + dump("Unable to remove file\npath: " + file.path + + "\nException: " + e + "\n"); + } + + file = updatesSubDir.clone(); + file.append("update.version"); + try { + if (file.exists()) + file.remove(false); + } + catch (e) { + dump("Unable to remove file\npath: " + file.path + + "\nException: " + e + "\n"); + } + + try { + updatesSubDir.remove(true); + } + catch (e) { + dump("Unable to remove directory\npath: " + updatesSubDir.path + + "\nException: " + e + "\n"); + } } // This fails sporadically on Mac OS X so wrap it in a try catch @@ -237,6 +321,8 @@ function remove_dirs_and_files () { dir.remove(true); } catch (e) { + dump("Unable to remove directory\npath: " + dir.path + + "\nException: " + e + "\n"); } } diff --git a/toolkit/mozapps/update/test/unit/test_0110_general.js b/toolkit/mozapps/update/test/unit/test_0110_general.js index 82fd5c820f26..8a2bd26308ae 100644 --- a/toolkit/mozapps/update/test/unit/test_0110_general.js +++ b/toolkit/mozapps/update/test/unit/test_0110_general.js @@ -36,125 +36,99 @@ * ***** END LICENSE BLOCK ***** */ -/* General MAR File Tests */ - -const DIR_DATA = "data" -const URL_PREFIX = "http://localhost:4444/" + DIR_DATA + "/"; - -const PREF_APP_UPDATE_URL_OVERRIDE = "app.update.url.override"; - -var gUpdates; -var gUpdateCount; -var gStatus; -var gExpectedStatus; -var gCheckFunc; -var gNextRunFunc; - -var gTestDir; -var gUpdater; -var gUpdatesDir; -var gUpdatesDirPath; +/* General Complete MAR File Patch Apply Tests */ function run_test() { - do_test_pending(); - - var fileLocator = AUS_Cc["@mozilla.org/file/directory_service;1"] - .getService(AUS_Ci.nsIProperties); - // The directory the updates will be applied to is the current working - // directory (e.g. obj-dir/toolkit/mozapps/update/test) and not dist/bin - gTestDir = do_get_cwd(); + // directory and not dist/bin. + var testDir = do_get_cwd(); // The mar files were created with all files in a subdirectory named - // mar_test... clear it out of the way if it exists and recreate it. - gTestDir.append("mar_test"); - if (gTestDir.exists()) - gTestDir.remove(true); - gTestDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, 0755); + // mar_test... clear it out of the way if it exists and then create it. + testDir.append("mar_test"); + try { + if (testDir.exists()) + testDir.remove(true); + } + catch (e) { + dump("Unable to remove directory\npath: " + testDir.path + + "\nException: " + e + "\n"); + } + dump("Testing: successful removal of the directory used to apply the mar file\n"); + do_check_false(testDir.exists()); + testDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, 0755); // Create an empty test file to test the complete mar's ability to replace an // existing file. - var testFile = gTestDir.clone(); + var testFile = testDir.clone(); testFile.append("text1"); testFile.create(AUS_Ci.nsIFile.NORMAL_FILE_TYPE, 0644); - var binDir = fileLocator.get("GreD", AUS_Ci.nsIFile); + var binDir = gDirSvc.get(NS_GRE_DIR, AUS_Ci.nsIFile); // The updater binary file - gUpdater = binDir.clone(); - gUpdater.append("updater.app"); - if (!gUpdater.exists()) { - gUpdater = binDir.clone(); - gUpdater.append("updater.exe"); - if (!gUpdater.exists()) { - gUpdater = binDir.clone(); - gUpdater.append("updater"); - if (!gUpdater.exists()) { + var updater = binDir.clone(); + updater.append("updater.app"); + if (!updater.exists()) { + updater = binDir.clone(); + updater.append("updater.exe"); + if (!updater.exists()) { + updater = binDir.clone(); + updater.append("updater"); + if (!updater.exists()) { do_throw("Unable to find updater binary!"); } } } - // The dir where the mar file is located - gUpdatesDir = binDir.clone(); - gUpdatesDir.append("updates"); - gUpdatesDir.append("0"); + // Use a directory outside of dist/bin to lessen the garbage in dist/bin + var updatesSubDir = do_get_cwd(); + updatesSubDir.append("app_dir"); + updatesSubDir.append("updates"); + updatesSubDir.append("0"); - // Quoted path to the dir where the mar file is located - gUpdatesDirPath = gUpdatesDir.path; - if (/ /.test(gUpdatesDirPath)) - gUpdatesDirPath = '"' + gUpdatesDirPath + '"'; + dump("Testing: cleanup of the updates directory used to test\n"); + if (updatesSubDir.exists()) + updatesSubDir.remove(true); + do_check_false(updatesSubDir.exists()); - startAUS(); - start_httpserver(DIR_DATA); - do_timeout(0, "run_test_pt1()"); -} + var mar = do_get_file("data/aus-0110_general-1.mar"); + mar.copyTo(updatesSubDir, "update.mar"); -function end_test() { - stop_httpserver(); - if (gTestDir.exists()) - gTestDir.remove(true); - do_test_finished(); -} + // apply the complete mar and check the innards of the files + var exitValue = runUpdate(updatesSubDir, updater); + dump("Testing: updater binary process exitValue for success when applying " + + "a complete mar\n"); + do_check_eq(exitValue, 0); -// Helper functions for testing mar downloads that have the correct size -// specified in the update xml. -function run_test_helper(aUpdateXML, aMsg, aResult, aNextRunFunc) { - gUpdates = null; - gUpdateCount = null; - gStatus = null; - gCheckFunc = check_test_helper_pt1; - gNextRunFunc = aNextRunFunc; - gExpectedStatus = aResult; - var url = URL_PREFIX + aUpdateXML; - dump("Testing: " + aMsg + " - " + url + "\n"); - gPrefs.setCharPref(PREF_APP_UPDATE_URL_OVERRIDE, url); - gUpdateChecker.checkForUpdates(updateCheckListener, true); -} + dump("Testing: contents of files added by a complete mar\n"); + do_check_eq(getFileBytes(getTestFile(testDir, "text1")), "ToBeModified\n"); + do_check_eq(getFileBytes(getTestFile(testDir, "text2")), "ToBeDeleted\n"); -function check_test_helper_pt1() { - do_check_eq(gUpdateCount, 1); - gCheckFunc = check_test_helper_pt2; - var bestUpdate = gAUS.selectUpdate(gUpdates, gUpdateCount); - var state = gAUS.downloadUpdate(bestUpdate, false); - if (state == "null" || state == "failed") - do_throw("nsIApplicationUpdateService:downloadUpdate returned " + state); - gAUS.addDownloadListener(downloadListener); -} + var refImage = do_get_file("data/aus-0110_general_ref_image1.png"); + var srcImage = getTestFile(testDir, "image1.png"); + do_check_eq(getFileBytes(srcImage), getFileBytes(refImage)); -function check_test_helper_pt2() { - do_check_eq(gStatus, gExpectedStatus); - gAUS.removeDownloadListener(downloadListener); - gNextRunFunc(); + try { + if (updatesSubDir.exists()) + updatesSubDir.remove(true); + } + catch (e) { + dump("Unable to remove directory\npath: " + updatesSubDir.path + + "\nException: " + e + "\n"); + } + dump("Testing: successful removal of the updates subdirectory where the " + + "updater binary was launched\n"); + do_check_false(updatesSubDir.exists()); } // Launches the updater binary to apply a mar file -function runUpdate() { +function runUpdate(aUpdatesSubDir, aUpdater) { // Copy the updater binary to the update directory so the updater.ini is not // in the same directory as it is. This prevents ui from displaying and the // PostUpdate executable which is defined in the updater.ini from launching. - gUpdater.copyTo(gUpdatesDir, gUpdater.leafName); - var updateBin = gUpdatesDir.clone(); - updateBin.append(gUpdater.leafName); + aUpdater.copyTo(aUpdatesSubDir, aUpdater.leafName); + var updateBin = aUpdatesSubDir.clone(); + updateBin.append(aUpdater.leafName); if (updateBin.leafName == "updater.app") { updateBin.append("Contents"); updateBin.append("MacOS"); @@ -163,30 +137,34 @@ function runUpdate() { do_throw("Unable to find the updater executable!"); } + var updatesSubDirPath = aUpdatesSubDir.path; + if (/ /.test(updatesSubDirPath)) + updatesSubDirPath = '"' + updatesSubDirPath + '"'; + var process = AUS_Cc["@mozilla.org/process/util;1"] .createInstance(AUS_Ci.nsIProcess); process.init(updateBin); - var args = [gUpdatesDirPath]; + var args = [updatesSubDirPath]; process.run(true, args, args.length); return process.exitValue; } -// Gets a file in the directory (the current working directory) where the mar -// will be applied. -function getTestFile(leafName) { - var file = gTestDir.clone(); - file.append(leafName); +// Gets a file in the mar_test subdirectory of the current working directory +// which is where the mar will be applied. +function getTestFile(aDir, aLeafName) { + var file = aDir.clone(); + file.append(aLeafName); if (!(file instanceof AUS_Ci.nsILocalFile)) - do_throw("File must be a nsILocalFile for this test! File: " + leafName); + do_throw("File must be a nsILocalFile for this test! File: " + aLeafName); return file; } // Returns the binary contents of a file -function getFileBytes(file) { +function getFileBytes(aFile) { var fis = AUS_Cc["@mozilla.org/network/file-input-stream;1"] .createInstance(AUS_Ci.nsIFileInputStream); - fis.init(file, -1, -1, false); + fis.init(aFile, -1, -1, false); var bis = AUS_Cc["@mozilla.org/binaryinputstream;1"] .createInstance(AUS_Ci.nsIBinaryInputStream); bis.setInputStream(fis); @@ -195,102 +173,3 @@ function getFileBytes(file) { fis.close(); return data; } - -// applying a complete mar and replacing an existing file -function run_test_pt1() { - run_test_helper("aus-0110_general-1.xml", "applying a complete mar", - AUS_Cr.NS_OK, run_test_pt2); -} - -// apply the complete mar and check the innards of the files -function run_test_pt2() { - var exitValue = runUpdate(); - do_check_eq(exitValue, 0); - - dump("Testing: contents of files added\n"); - do_check_eq(getFileBytes(getTestFile("text1")), "ToBeModified\n"); - do_check_eq(getFileBytes(getTestFile("text2")), "ToBeDeleted\n"); - - var refImage = do_get_file("data/aus-0110_general_ref_image1.png"); - var srcImage = getTestFile("image1.png"); - do_check_eq(getFileBytes(srcImage), getFileBytes(refImage)); - - remove_dirs_and_files(); - run_test_pt3(); -} - -// applying a partial mar and remove an existing file -function run_test_pt3() { - run_test_helper("aus-0110_general-2.xml", "applying a partial mar", - AUS_Cr.NS_OK, run_test_pt4); -} - -// apply the partial mar and check the innards of the files -function run_test_pt4() { - var exitValue = runUpdate(); - do_check_eq(exitValue, 0); - - dump("Testing: removal of a file and contents of added / modified files\n"); - do_check_eq(getFileBytes(getTestFile("text1")), "Modified\n"); - do_check_false(getTestFile("text2").exists()); // file removed - do_check_eq(getFileBytes(getTestFile("text3")), "Added\n"); - - var refImage = do_get_file("data/aus-0110_general_ref_image2.png"); - var srcImage = getTestFile("image1.png"); - do_check_eq(getFileBytes(srcImage), getFileBytes(refImage)); - - end_test(); -} - -// Update check listener -const updateCheckListener = { - onProgress: function(request, position, totalSize) { - }, - - onCheckComplete: function(request, updates, updateCount) { - gUpdateCount = updateCount; - gUpdates = updates; - dump("onCheckComplete url = " + request.channel.originalURI.spec + "\n\n"); - // Use a timeout to allow the XHR to complete - do_timeout(0, "gCheckFunc()"); - }, - - onError: function(request, update) { - dump("onError url = " + request.channel.originalURI.spec + "\n\n"); - // Use a timeout to allow the XHR to complete - do_timeout(0, "gCheckFunc()"); - }, - - QueryInterface: function(aIID) { - if (!aIID.equals(AUS_Ci.nsIUpdateCheckListener) && - !aIID.equals(AUS_Ci.nsISupports)) - throw AUS_Cr.NS_ERROR_NO_INTERFACE; - return this; - } -}; - -/* Update download listener - nsIRequestObserver */ -const downloadListener = { - onStartRequest: function(request, context) { - }, - - onProgress: function(request, context, progress, maxProgress) { - }, - - onStatus: function(request, context, status, statusText) { - }, - - onStopRequest: function(request, context, status) { - gStatus = status; - // Use a timeout to allow the request to complete - do_timeout(0, "gCheckFunc()"); - }, - - QueryInterface: function(iid) { - if (!iid.equals(AUS_Ci.nsIRequestObserver) && - !iid.equals(AUS_Ci.nsIProgressEventSink) && - !iid.equals(AUS_Ci.nsISupports)) - throw AUS_Cr.NS_ERROR_NO_INTERFACE; - return this; - } -}; diff --git a/toolkit/mozapps/update/test/unit/test_0111_general.js b/toolkit/mozapps/update/test/unit/test_0111_general.js new file mode 100644 index 000000000000..8b003f3bf65a --- /dev/null +++ b/toolkit/mozapps/update/test/unit/test_0111_general.js @@ -0,0 +1,184 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Application Update Service. + * + * The Initial Developer of the Original Code is + * Robert Strong . + * + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Mozilla Foundation . All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** + */ + +/* General Partial MAR File Patch Apply Tests */ + +function run_test() { + // The directory the updates will be applied to is the current working + // directory and not dist/bin. + var testDir = do_get_cwd(); + // The mar files were created with all files in a subdirectory named + // mar_test... clear it out of the way if it exists and then create it. + testDir.append("mar_test"); + try { + if (testDir.exists()) + testDir.remove(true); + } + catch (e) { + dump("Unable to remove directory\npath: " + testDir.path + + "\nException: " + e + "\n"); + } + dump("Testing: successful removal of the directory used to apply the mar file\n"); + do_check_false(testDir.exists()); + testDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, 0755); + + // Create the files to test the partial mar's ability to modify and delete + // files. + var testFile = testDir.clone(); + testFile.append("text1"); + writeFile(testFile, "ToBeModified\n"); + + testFile = testDir.clone(); + testFile.append("text2"); + writeFile(testFile, "ToBeDeleted\n"); + + testFile = do_get_file("data/aus-0110_general_ref_image1.png"); + testFile.copyTo(testDir, "image1.png"); + + var binDir = gDirSvc.get(NS_GRE_DIR, AUS_Ci.nsIFile); + + // The updater binary file + var updater = binDir.clone(); + updater.append("updater.app"); + if (!updater.exists()) { + updater = binDir.clone(); + updater.append("updater.exe"); + if (!updater.exists()) { + updater = binDir.clone(); + updater.append("updater"); + if (!updater.exists()) { + do_throw("Unable to find updater binary!"); + } + } + } + + // Use a directory outside of dist/bin to lessen the garbage in dist/bin + var updatesSubDir = do_get_cwd(); + updatesSubDir.append("app_dir"); + updatesSubDir.append("updates"); + updatesSubDir.append("0"); + + dump("Testing: cleanup of the updates directory used to test\n"); + if (updatesSubDir.exists()) + updatesSubDir.remove(true); + do_check_false(updatesSubDir.exists()); + + var mar = do_get_file("data/aus-0110_general-2.mar"); + mar.copyTo(updatesSubDir, "update.mar"); + + // apply the partial mar and check the innards of the files + exitValue = runUpdate(updatesSubDir, updater); + dump("Testing: updater binary process exitValue for success when applying " + + "a partial mar\n"); + do_check_eq(exitValue, 0); + + dump("Testing: removal of a file and contents of added / modified files by " + + "a partial mar\n"); + do_check_eq(getFileBytes(getTestFile(testDir, "text1")), "Modified\n"); + do_check_false(getTestFile(testDir, "text2").exists()); // file removed + do_check_eq(getFileBytes(getTestFile(testDir, "text3")), "Added\n"); + + refImage = do_get_file("data/aus-0110_general_ref_image2.png"); + srcImage = getTestFile(testDir, "image1.png"); + do_check_eq(getFileBytes(srcImage), getFileBytes(refImage)); + + try { + if (updatesSubDir.exists()) + updatesSubDir.remove(true); + } + catch (e) { + dump("Unable to remove directory\npath: " + updatesSubDir.path + + "\nException: " + e + "\n"); + } + dump("Testing: successful removal of the updates subdirectory where the " + + "updater binary was launched\n"); + do_check_false(updatesSubDir.exists()); +} + +// Launches the updater binary to apply a mar file +function runUpdate(aUpdatesSubDir, aUpdater) { + // Copy the updater binary to the update directory so the updater.ini is not + // in the same directory as it is. This prevents ui from displaying and the + // PostUpdate executable which is defined in the updater.ini from launching. + aUpdater.copyTo(aUpdatesSubDir, aUpdater.leafName); + var updateBin = aUpdatesSubDir.clone(); + updateBin.append(aUpdater.leafName); + if (updateBin.leafName == "updater.app") { + updateBin.append("Contents"); + updateBin.append("MacOS"); + updateBin.append("updater"); + if (!updateBin.exists()) + do_throw("Unable to find the updater executable!"); + } + + var updatesSubDirPath = aUpdatesSubDir.path; + if (/ /.test(updatesSubDirPath)) + updatesSubDirPath = '"' + updatesSubDirPath + '"'; + + var process = AUS_Cc["@mozilla.org/process/util;1"] + .createInstance(AUS_Ci.nsIProcess); + process.init(updateBin); + var args = [updatesSubDirPath]; + process.run(true, args, args.length); + return process.exitValue; +} + +// Gets a file in the mar_test subdirectory of the current working directory +// which is where the mar will be applied. +function getTestFile(aDir, aLeafName) { + var file = aDir.clone(); + file.append(aLeafName); + if (!(file instanceof AUS_Ci.nsILocalFile)) + do_throw("File must be a nsILocalFile for this test! File: " + aLeafName); + + return file; +} + +// Returns the binary contents of a file +function getFileBytes(aFile) { + var fis = AUS_Cc["@mozilla.org/network/file-input-stream;1"] + .createInstance(AUS_Ci.nsIFileInputStream); + fis.init(aFile, -1, -1, false); + var bis = AUS_Cc["@mozilla.org/binaryinputstream;1"] + .createInstance(AUS_Ci.nsIBinaryInputStream); + bis.setInputStream(fis); + var data = bis.readBytes(bis.available()); + bis.close(); + fis.close(); + return data; +}