1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is the Application Update Service.
16 * The Initial Developer of the Original Code is
17 * Robert Strong <robert.bugzilla@gmail.com>.
19 * Portions created by the Initial Developer are Copyright (C) 2008
20 * the Mozilla Foundation <http://www.mozilla.org/>. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK *****
39 /* General MAR File Tests */
41 const DIR_DATA = "data"
42 const URL_PREFIX = "http://localhost:4444/" + DIR_DATA + "/";
44 const PREF_APP_UPDATE_URL_OVERRIDE = "app.update.url.override";
61 var fileLocator = AUS_Cc["@mozilla.org/file/directory_service;1"]
62 .getService(AUS_Ci.nsIProperties);
64 // The directory the updates will be applied to is the current working
65 // directory (e.g. obj-dir/toolkit/mozapps/update/test) and not dist/bin
66 gTestDir = fileLocator.get("CurWorkD", AUS_Ci.nsIFile);
67 // The mar files were created with all files in a subdirectory named
68 // mar_test... clear it out of the way if it exists and recreate it.
69 gTestDir.append("mar_test");
70 if (gTestDir.exists())
71 gTestDir.remove(true);
72 gTestDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, 0755);
74 // Create an empty test file to test the complete mar's ability to replace an
76 var testFile = gTestDir.clone();
77 testFile.append("text1");
78 testFile.create(AUS_Ci.nsIFile.NORMAL_FILE_TYPE, 0644);
80 var binDir = fileLocator.get("XCurProcD", AUS_Ci.nsIFile);
82 // The updater binary file
83 gUpdater = binDir.clone();
84 gUpdater.append("updater.app");
85 if (!gUpdater.exists()) {
86 gUpdater = binDir.clone();
87 gUpdater.append("updater.exe");
88 if (!gUpdater.exists()) {
89 gUpdater = binDir.clone();
90 gUpdater.append("updater");
91 if (!gUpdater.exists()) {
92 do_throw("Unable to find updater binary!");
97 // The dir where the mar file is located
98 gUpdatesDir = binDir.clone();
99 gUpdatesDir.append("updates");
100 gUpdatesDir.append("0");
102 // Quoted path to the dir where the mar file is located
103 gUpdatesDirPath = gUpdatesDir.path;
104 if (/ /.test(gUpdatesDirPath))
105 gUpdatesDirPath = '"' + gUpdatesDirPath + '"';
108 start_httpserver(DIR_DATA);
109 do_timeout(0, "run_test_pt1()");
112 function end_test() {
114 if (gTestDir.exists())
115 gTestDir.remove(true);
119 // Helper functions for testing mar downloads that have the correct size
120 // specified in the update xml.
121 function run_test_helper(aUpdateXML, aMsg, aResult, aNextRunFunc) {
125 gCheckFunc = check_test_helper_pt1;
126 gNextRunFunc = aNextRunFunc;
127 gExpectedStatus = aResult;
128 var url = URL_PREFIX + aUpdateXML;
129 dump("Testing: " + aMsg + " - " + url + "\n");
130 gPrefs.setCharPref(PREF_APP_UPDATE_URL_OVERRIDE, url);
131 gUpdateChecker.checkForUpdates(updateCheckListener, true);
134 function check_test_helper_pt1() {
135 do_check_eq(gUpdateCount, 1);
136 gCheckFunc = check_test_helper_pt2;
137 var bestUpdate = gAUS.selectUpdate(gUpdates, gUpdateCount);
138 var state = gAUS.downloadUpdate(bestUpdate, false);
139 if (state == "null" || state == "failed")
140 do_throw("nsIApplicationUpdateService:downloadUpdate returned " + state);
141 gAUS.addDownloadListener(downloadListener);
144 function check_test_helper_pt2() {
145 do_check_eq(gStatus, gExpectedStatus);
146 gAUS.removeDownloadListener(downloadListener);
150 // Launches the updater binary to apply a mar file
151 function runUpdate() {
152 // Copy the updater binary to the update directory so the updater.ini is not
153 // in the same directory as it is. This prevents ui from displaying and the
154 // PostUpdate executable which is defined in the updater.ini from launching.
155 gUpdater.copyTo(gUpdatesDir, gUpdater.leafName);
156 var updateBin = gUpdatesDir.clone();
157 updateBin.append(gUpdater.leafName);
158 if (updateBin.leafName == "updater.app") {
159 updateBin.append("Contents");
160 updateBin.append("MacOS");
161 updateBin.append("updater");
162 if (!updateBin.exists())
163 do_throw("Unable to find the updater executable!");
166 var process = AUS_Cc["@mozilla.org/process/util;1"]
167 .createInstance(AUS_Ci.nsIProcess);
168 process.init(updateBin);
169 var args = [gUpdatesDirPath];
170 process.run(true, args, args.length);
171 return process.exitValue;
174 // Gets a file in the directory (the current working directory) where the mar
176 function getTestFile(leafName) {
177 var file = gTestDir.clone();
178 file.append(leafName);
179 if (!(file instanceof AUS_Ci.nsILocalFile))
180 do_throw("File must be a nsILocalFile for this test! File: " + leafName);
185 // Returns the binary contents of a file
186 function getFileBytes(file) {
187 var fis = AUS_Cc["@mozilla.org/network/file-input-stream;1"]
188 .createInstance(AUS_Ci.nsIFileInputStream);
189 fis.init(file, -1, -1, false);
190 var bis = AUS_Cc["@mozilla.org/binaryinputstream;1"]
191 .createInstance(AUS_Ci.nsIBinaryInputStream);
192 bis.setInputStream(fis);
193 var data = bis.readBytes(bis.available());
199 // applying a complete mar and replacing an existing file
200 function run_test_pt1() {
201 run_test_helper("aus-0110_general-1.xml", "applying a complete mar",
202 AUS_Cr.NS_OK, run_test_pt2);
205 // apply the complete mar and check the innards of the files
206 function run_test_pt2() {
207 var exitValue = runUpdate();
208 do_check_eq(exitValue, 0);
210 dump("Testing: contents of files added\n");
211 do_check_eq(getFileBytes(getTestFile("text1")), "ToBeModified\n");
212 do_check_eq(getFileBytes(getTestFile("text2")), "ToBeDeleted\n");
214 var refImage = do_get_file("toolkit/mozapps/update/test/unit/data/aus-0110_general_ref_image1.png");
215 var srcImage = getTestFile("image1.png");
216 do_check_eq(getFileBytes(srcImage), getFileBytes(refImage));
218 remove_dirs_and_files();
222 // applying a partial mar and remove an existing file
223 function run_test_pt3() {
224 run_test_helper("aus-0110_general-2.xml", "applying a partial mar",
225 AUS_Cr.NS_OK, run_test_pt4);
228 // apply the partial mar and check the innards of the files
229 function run_test_pt4() {
230 var exitValue = runUpdate();
231 do_check_eq(exitValue, 0);
233 dump("Testing: removal of a file and contents of added / modified files\n");
234 do_check_eq(getFileBytes(getTestFile("text1")), "Modified\n");
235 do_check_false(getTestFile("text2").exists()); // file removed
236 do_check_eq(getFileBytes(getTestFile("text3")), "Added\n");
238 var refImage = do_get_file("toolkit/mozapps/update/test/unit/data/aus-0110_general_ref_image2.png");
239 var srcImage = getTestFile("image1.png");
240 do_check_eq(getFileBytes(srcImage), getFileBytes(refImage));
245 // Update check listener
246 const updateCheckListener = {
247 onProgress: function(request, position, totalSize) {
250 onCheckComplete: function(request, updates, updateCount) {
251 gUpdateCount = updateCount;
253 dump("onCheckComplete url = " + request.channel.originalURI.spec + "\n\n");
254 // Use a timeout to allow the XHR to complete
255 do_timeout(0, "gCheckFunc()");
258 onError: function(request, update) {
259 dump("onError url = " + request.channel.originalURI.spec + "\n\n");
260 // Use a timeout to allow the XHR to complete
261 do_timeout(0, "gCheckFunc()");
264 QueryInterface: function(aIID) {
265 if (!aIID.equals(AUS_Ci.nsIUpdateCheckListener) &&
266 !aIID.equals(AUS_Ci.nsISupports))
267 throw AUS_Cr.NS_ERROR_NO_INTERFACE;
272 /* Update download listener - nsIRequestObserver */
273 const downloadListener = {
274 onStartRequest: function(request, context) {
277 onProgress: function(request, context, progress, maxProgress) {
280 onStatus: function(request, context, status, statusText) {
283 onStopRequest: function(request, context, status) {
285 // Use a timeout to allow the request to complete
286 do_timeout(0, "gCheckFunc()");
289 QueryInterface: function(iid) {
290 if (!iid.equals(AUS_Ci.nsIRequestObserver) &&
291 !iid.equals(AUS_Ci.nsIProgressEventSink) &&
292 !iid.equals(AUS_Ci.nsISupports))
293 throw AUS_Cr.NS_ERROR_NO_INTERFACE;