|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 const INSTALL_LOCALE = "@AB_CD@"; |
|
6 const MOZ_APP_NAME = "@MOZ_APP_NAME@"; |
|
7 const BIN_SUFFIX = "@BIN_SUFFIX@"; |
|
8 |
|
9 #ifdef XP_WIN |
|
10 // MOZ_APP_VENDOR is optional. |
|
11 // On Windows, if MOZ_APP_VENDOR is not defined the updates directory will be |
|
12 // located under %LOCALAPPDATA%\@MOZ_APP_BASENAME@\updates\TaskBarID |
|
13 #ifdef MOZ_APP_VENDOR |
|
14 const MOZ_APP_VENDOR = "@MOZ_APP_VENDOR@"; |
|
15 #else |
|
16 const MOZ_APP_VENDOR = ""; |
|
17 #endif |
|
18 |
|
19 // MOZ_APP_BASENAME is not optional for tests. |
|
20 const MOZ_APP_BASENAME = "@MOZ_APP_BASENAME@"; |
|
21 #endif // XP_WIN |
|
22 |
|
23 const APP_INFO_NAME = "XPCShell"; |
|
24 const APP_INFO_VENDOR = "Mozilla"; |
|
25 |
|
26 #ifdef XP_UNIX |
|
27 const APP_BIN_SUFFIX = "-bin"; |
|
28 #else |
|
29 const APP_BIN_SUFFIX = "@BIN_SUFFIX@"; |
|
30 #endif |
|
31 |
|
32 #ifdef XP_WIN |
|
33 const IS_WIN = true; |
|
34 #else |
|
35 const IS_WIN = false; |
|
36 #endif |
|
37 |
|
38 #ifdef XP_MACOSX |
|
39 const IS_MACOSX = true; |
|
40 #ifdef MOZ_SHARK |
|
41 const IS_SHARK = true; |
|
42 #else |
|
43 const IS_SHARK = false; |
|
44 #endif |
|
45 #else |
|
46 const IS_MACOSX = false; |
|
47 #endif |
|
48 |
|
49 #ifdef XP_UNIX |
|
50 const IS_UNIX = true; |
|
51 #else |
|
52 const IS_UNIX = false; |
|
53 #endif |
|
54 |
|
55 #ifdef ANDROID |
|
56 const IS_ANDROID = true; |
|
57 #else |
|
58 const IS_ANDROID = false; |
|
59 #endif |
|
60 |
|
61 #ifdef MOZ_WIDGET_GONK |
|
62 const IS_TOOLKIT_GONK = true; |
|
63 #else |
|
64 const IS_TOOLKIT_GONK = false; |
|
65 #endif |
|
66 |
|
67 const USE_EXECV = IS_UNIX && !IS_MACOSX; |
|
68 |
|
69 #ifdef MOZ_VERIFY_MAR_SIGNATURE |
|
70 const IS_MAR_CHECKS_ENABLED = true; |
|
71 #else |
|
72 const IS_MAR_CHECKS_ENABLED = false; |
|
73 #endif |
|
74 |
|
75 const URL_HOST = "http://localhost"; |
|
76 |
|
77 const FILE_APP_BIN = MOZ_APP_NAME + APP_BIN_SUFFIX; |
|
78 const FILE_COMPLETE_EXE = "complete.exe"; |
|
79 const FILE_COMPLETE_MAR = "complete.mar"; |
|
80 const FILE_HELPER_BIN = "TestAUSHelper" + BIN_SUFFIX; |
|
81 const FILE_MAINTENANCE_SERVICE_BIN = "maintenanceservice.exe"; |
|
82 const FILE_MAINTENANCE_SERVICE_INSTALLER_BIN = "maintenanceservice_installer.exe"; |
|
83 const FILE_OLD_VERSION_MAR = "old_version.mar"; |
|
84 const FILE_PARTIAL_EXE = "partial.exe"; |
|
85 const FILE_PARTIAL_MAR = "partial.mar"; |
|
86 const FILE_UPDATER_BIN = "updater" + BIN_SUFFIX; |
|
87 const FILE_UPDATER_INI_BAK = "updater.ini.bak"; |
|
88 const FILE_WRONG_CHANNEL_MAR = "wrong_product_channel.mar"; |
|
89 |
|
90 const LOG_COMPLETE_SUCCESS = "complete_log_success"; |
|
91 const LOG_PARTIAL_SUCCESS = "partial_log_success"; |
|
92 const LOG_PARTIAL_FAILURE = "partial_log_failure"; |
|
93 |
|
94 const LOG_SWITCH_SUCCESS = "rename_file: proceeding to rename the directory\n" + |
|
95 "rename_file: proceeding to rename the directory\n" + |
|
96 "Now, remove the tmpDir\n" + |
|
97 "succeeded\n" + |
|
98 "calling QuitProgressUI"; |
|
99 |
|
100 const ERR_RENAME_FILE = "rename_file: failed to rename file"; |
|
101 const ERR_UNABLE_OPEN_DEST = "unable to open destination file"; |
|
102 const ERR_BACKUP_DISCARD = "backup_discard: unable to remove"; |
|
103 |
|
104 const LOG_SVC_SUCCESSFUL_LAUNCH = "Process was started... waiting on result."; |
|
105 |
|
106 // All we care about is that the last modified time has changed so that Mac OS |
|
107 // X Launch Services invalidates its cache so the test allows up to one minute |
|
108 // difference in the last modified time. |
|
109 const MAC_MAX_TIME_DIFFERENCE = 60000; |
|
110 |
|
111 // Time to wait for the test helper process before continuing the test |
|
112 const TEST_HELPER_TIMEOUT = 100; |
|
113 |
|
114 // Time to wait for a check in the test before continuing the test |
|
115 const TEST_CHECK_TIMEOUT = 100; |
|
116 |
|
117 // How many of TEST_CHECK_TIMEOUT to wait before we abort the test. |
|
118 const MAX_TIMEOUT_RUNS = 2000; |
|
119 |
|
120 // Time in seconds the helper application should sleep.the helper's input and output files |
|
121 const HELPER_SLEEP_TIMEOUT = 180; |
|
122 |
|
123 // Maximum number of milliseconds the process that is launched can run before |
|
124 // the test will try to kill it. |
|
125 const APP_TIMER_TIMEOUT = 120000; |
|
126 |
|
127 #ifdef XP_WIN |
|
128 const PIPE_TO_NULL = ">nul"; |
|
129 #else |
|
130 const PIPE_TO_NULL = "> /dev/null 2>&1"; |
|
131 #endif |
|
132 |
|
133 // This default value will be overridden when using the http server. |
|
134 var gURLData = URL_HOST + "/"; |
|
135 |
|
136 var gTestID; |
|
137 |
|
138 var gTestserver; |
|
139 |
|
140 var gRegisteredServiceCleanup; |
|
141 |
|
142 var gXHR; |
|
143 var gXHRCallback; |
|
144 |
|
145 var gUpdatePrompt; |
|
146 var gUpdatePromptCallback; |
|
147 |
|
148 var gCheckFunc; |
|
149 var gResponseBody; |
|
150 var gResponseStatusCode = 200; |
|
151 var gRequestURL; |
|
152 var gUpdateCount; |
|
153 var gUpdates; |
|
154 var gStatusCode; |
|
155 var gStatusText; |
|
156 var gStatusResult; |
|
157 |
|
158 var gProcess; |
|
159 var gAppTimer; |
|
160 var gHandle; |
|
161 |
|
162 var gGREDirOrig; |
|
163 var gAppDirOrig; |
|
164 |
|
165 var gServiceLaunchedCallbackLog = null; |
|
166 var gServiceLaunchedCallbackArgs = null; |
|
167 |
|
168 // Variables are used instead of contants so tests can override these values if |
|
169 // necessary. |
|
170 var gCallbackBinFile = "callback_app" + BIN_SUFFIX; |
|
171 var gCallbackArgs = ["./", "callback.log", "Test Arg 2", "Test Arg 3"]; |
|
172 var gStageUpdate = false; |
|
173 var gSwitchApp = false; |
|
174 var gDisableReplaceFallback = false; |
|
175 var gUseTestAppDir = true; |
|
176 |
|
177 var gTimeoutRuns = 0; |
|
178 |
|
179 // Environment related globals |
|
180 var gShouldResetEnv = undefined; |
|
181 var gAddedEnvXRENoWindowsCrashDialog = false; |
|
182 var gEnvXPCOMDebugBreak; |
|
183 var gEnvXPCOMMemLeakLog; |
|
184 var gEnvDyldLibraryPath; |
|
185 var gEnvLdLibraryPath; |
|
186 |
|
187 // Set to true to log additional information for debugging. To log additional |
|
188 // information for an individual test set DEBUG_AUS_TEST to true in the test's |
|
189 // run_test function. |
|
190 var DEBUG_AUS_TEST = true; |
|
191 // Never set DEBUG_TEST_LOG to true except when running tests locally or on the |
|
192 // try server since this will force a test that failed a parallel run to fail |
|
193 // when the same test runs non-parallel so the log from parallel test run can |
|
194 // be displayed in the log. |
|
195 var DEBUG_TEST_LOG = false; |
|
196 // Set to false to keep the log file from the failed parallel test run. |
|
197 var gDeleteLogFile = true; |
|
198 var gRealDump; |
|
199 var gTestLogText = ""; |
|
200 var gPassed; |
|
201 |
|
202 #include ../shared.js |
|
203 |
|
204 var gTestFiles = []; |
|
205 var gTestDirs = []; |
|
206 |
|
207 // Common files for both successful and failed updates. |
|
208 var gTestFilesCommon = [ |
|
209 { |
|
210 description : "Should never change", |
|
211 fileName : FILE_UPDATE_SETTINGS_INI, |
|
212 relPathDir : "a/b/", |
|
213 originalContents : "ShouldNotBeReplaced\n", |
|
214 compareContents : "ShouldNotBeReplaced\n", |
|
215 originalFile : null, |
|
216 compareFile : null, |
|
217 originalPerms : 0o767, |
|
218 comparePerms : 0o767 |
|
219 }, { |
|
220 description : "Should never change", |
|
221 fileName : "channel-prefs.js", |
|
222 relPathDir : "a/b/defaults/pref/", |
|
223 originalContents : "ShouldNotBeReplaced\n", |
|
224 compareContents : "ShouldNotBeReplaced\n", |
|
225 originalFile : null, |
|
226 compareFile : null, |
|
227 originalPerms : 0o767, |
|
228 comparePerms : 0o767 |
|
229 }]; |
|
230 |
|
231 // Files for a complete successful update. This can be used for a complete |
|
232 // failed update by calling setTestFilesAndDirsForFailure. |
|
233 var gTestFilesCompleteSuccess = [ |
|
234 { |
|
235 description : "Added by update.manifest (add)", |
|
236 fileName : "precomplete", |
|
237 relPathDir : "", |
|
238 originalContents : null, |
|
239 compareContents : null, |
|
240 originalFile : "partial_precomplete", |
|
241 compareFile : "complete_precomplete", |
|
242 originalPerms : 0o666, |
|
243 comparePerms : 0o644 |
|
244 }, { |
|
245 description : "Added by update.manifest (add)", |
|
246 fileName : "searchpluginstext0", |
|
247 relPathDir : "a/b/searchplugins/", |
|
248 originalContents : "ToBeReplacedWithFromComplete\n", |
|
249 compareContents : "FromComplete\n", |
|
250 originalFile : null, |
|
251 compareFile : null, |
|
252 originalPerms : 0o775, |
|
253 comparePerms : 0o644 |
|
254 }, { |
|
255 description : "Added by update.manifest (add)", |
|
256 fileName : "searchpluginspng1.png", |
|
257 relPathDir : "a/b/searchplugins/", |
|
258 originalContents : null, |
|
259 compareContents : null, |
|
260 originalFile : null, |
|
261 compareFile : "complete.png", |
|
262 originalPerms : null, |
|
263 comparePerms : 0o644 |
|
264 }, { |
|
265 description : "Added by update.manifest (add)", |
|
266 fileName : "searchpluginspng0.png", |
|
267 relPathDir : "a/b/searchplugins/", |
|
268 originalContents : null, |
|
269 compareContents : null, |
|
270 originalFile : "partial.png", |
|
271 compareFile : "complete.png", |
|
272 originalPerms : 0o666, |
|
273 comparePerms : 0o644 |
|
274 }, { |
|
275 description : "Added by update.manifest (add)", |
|
276 fileName : "removed-files", |
|
277 relPathDir : "a/b/", |
|
278 originalContents : null, |
|
279 compareContents : null, |
|
280 originalFile : "partial_removed-files", |
|
281 compareFile : "complete_removed-files", |
|
282 originalPerms : 0o666, |
|
283 comparePerms : 0o644 |
|
284 }, { |
|
285 description : "Added by update.manifest if the parent directory " + |
|
286 "exists (add-if)", |
|
287 fileName : "extensions1text0", |
|
288 relPathDir : "a/b/distribution/extensions/extensions1/", |
|
289 originalContents : null, |
|
290 compareContents : "FromComplete\n", |
|
291 originalFile : null, |
|
292 compareFile : null, |
|
293 originalPerms : null, |
|
294 comparePerms : 0o644 |
|
295 }, { |
|
296 description : "Added by update.manifest if the parent directory " + |
|
297 "exists (add-if)", |
|
298 fileName : "extensions1png1.png", |
|
299 relPathDir : "a/b/distribution/extensions/extensions1/", |
|
300 originalContents : null, |
|
301 compareContents : null, |
|
302 originalFile : "partial.png", |
|
303 compareFile : "complete.png", |
|
304 originalPerms : 0o666, |
|
305 comparePerms : 0o644 |
|
306 }, { |
|
307 description : "Added by update.manifest if the parent directory " + |
|
308 "exists (add-if)", |
|
309 fileName : "extensions1png0.png", |
|
310 relPathDir : "a/b/distribution/extensions/extensions1/", |
|
311 originalContents : null, |
|
312 compareContents : null, |
|
313 originalFile : null, |
|
314 compareFile : "complete.png", |
|
315 originalPerms : null, |
|
316 comparePerms : 0o644 |
|
317 }, { |
|
318 description : "Added by update.manifest if the parent directory " + |
|
319 "exists (add-if)", |
|
320 fileName : "extensions0text0", |
|
321 relPathDir : "a/b/distribution/extensions/extensions0/", |
|
322 originalContents : "ToBeReplacedWithFromComplete\n", |
|
323 compareContents : "FromComplete\n", |
|
324 originalFile : null, |
|
325 compareFile : null, |
|
326 originalPerms : null, |
|
327 comparePerms : 0o644 |
|
328 }, { |
|
329 description : "Added by update.manifest if the parent directory " + |
|
330 "exists (add-if)", |
|
331 fileName : "extensions0png1.png", |
|
332 relPathDir : "a/b/distribution/extensions/extensions0/", |
|
333 originalContents : null, |
|
334 compareContents : null, |
|
335 originalFile : null, |
|
336 compareFile : "complete.png", |
|
337 originalPerms : null, |
|
338 comparePerms : 0o644 |
|
339 }, { |
|
340 description : "Added by update.manifest if the parent directory " + |
|
341 "exists (add-if)", |
|
342 fileName : "extensions0png0.png", |
|
343 relPathDir : "a/b/distribution/extensions/extensions0/", |
|
344 originalContents : null, |
|
345 compareContents : null, |
|
346 originalFile : null, |
|
347 compareFile : "complete.png", |
|
348 originalPerms : null, |
|
349 comparePerms : 0o644 |
|
350 }, { |
|
351 description : "Added by update.manifest (add)", |
|
352 fileName : "exe0.exe", |
|
353 relPathDir : "a/b/", |
|
354 originalContents : null, |
|
355 compareContents : null, |
|
356 originalFile : FILE_HELPER_BIN, |
|
357 compareFile : FILE_COMPLETE_EXE, |
|
358 originalPerms : 0o777, |
|
359 comparePerms : 0o755 |
|
360 }, { |
|
361 description : "Added by update.manifest (add)", |
|
362 fileName : "10text0", |
|
363 relPathDir : "a/b/1/10/", |
|
364 originalContents : "ToBeReplacedWithFromComplete\n", |
|
365 compareContents : "FromComplete\n", |
|
366 originalFile : null, |
|
367 compareFile : null, |
|
368 originalPerms : 0o767, |
|
369 comparePerms : 0o644 |
|
370 }, { |
|
371 description : "Added by update.manifest (add)", |
|
372 fileName : "0exe0.exe", |
|
373 relPathDir : "a/b/0/", |
|
374 originalContents : null, |
|
375 compareContents : null, |
|
376 originalFile : FILE_HELPER_BIN, |
|
377 compareFile : FILE_COMPLETE_EXE, |
|
378 originalPerms : 0o777, |
|
379 comparePerms : 0o755 |
|
380 }, { |
|
381 description : "Added by update.manifest (add)", |
|
382 fileName : "00text1", |
|
383 relPathDir : "a/b/0/00/", |
|
384 originalContents : "ToBeReplacedWithFromComplete\n", |
|
385 compareContents : "FromComplete\n", |
|
386 originalFile : null, |
|
387 compareFile : null, |
|
388 originalPerms : 0o677, |
|
389 comparePerms : 0o644 |
|
390 }, { |
|
391 description : "Added by update.manifest (add)", |
|
392 fileName : "00text0", |
|
393 relPathDir : "a/b/0/00/", |
|
394 originalContents : "ToBeReplacedWithFromComplete\n", |
|
395 compareContents : "FromComplete\n", |
|
396 originalFile : null, |
|
397 compareFile : null, |
|
398 originalPerms : 0o775, |
|
399 comparePerms : 0o644 |
|
400 }, { |
|
401 description : "Added by update.manifest (add)", |
|
402 fileName : "00png0.png", |
|
403 relPathDir : "a/b/0/00/", |
|
404 originalContents : null, |
|
405 compareContents : null, |
|
406 originalFile : null, |
|
407 compareFile : "complete.png", |
|
408 originalPerms : 0o776, |
|
409 comparePerms : 0o644 |
|
410 }, { |
|
411 description : "Removed by precomplete (remove)", |
|
412 fileName : "20text0", |
|
413 relPathDir : "a/b/2/20/", |
|
414 originalContents : "ToBeDeleted\n", |
|
415 compareContents : null, |
|
416 originalFile : null, |
|
417 compareFile : null, |
|
418 originalPerms : null, |
|
419 comparePerms : null |
|
420 }, { |
|
421 description : "Removed by precomplete (remove)", |
|
422 fileName : "20png0.png", |
|
423 relPathDir : "a/b/2/20/", |
|
424 originalContents : "ToBeDeleted\n", |
|
425 compareContents : null, |
|
426 originalFile : null, |
|
427 compareFile : null, |
|
428 originalPerms : null, |
|
429 comparePerms : null |
|
430 }]; |
|
431 |
|
432 // Concatenate the common files to the end of the array. |
|
433 gTestFilesCompleteSuccess = gTestFilesCompleteSuccess.concat(gTestFilesCommon); |
|
434 |
|
435 // Files for a partial successful update. This can be used for a partial failed |
|
436 // update by calling setTestFilesAndDirsForFailure. |
|
437 var gTestFilesPartialSuccess = [ |
|
438 { |
|
439 description : "Added by update.manifest (add)", |
|
440 fileName : "precomplete", |
|
441 relPathDir : "", |
|
442 originalContents : null, |
|
443 compareContents : null, |
|
444 originalFile : "complete_precomplete", |
|
445 compareFile : "partial_precomplete", |
|
446 originalPerms : 0o666, |
|
447 comparePerms : 0o644 |
|
448 }, { |
|
449 description : "Added by update.manifest (add)", |
|
450 fileName : "searchpluginstext0", |
|
451 relPathDir : "a/b/searchplugins/", |
|
452 originalContents : "ToBeReplacedWithFromPartial\n", |
|
453 compareContents : "FromPartial\n", |
|
454 originalFile : null, |
|
455 compareFile : null, |
|
456 originalPerms : 0o775, |
|
457 comparePerms : 0o644 |
|
458 }, { |
|
459 description : "Patched by update.manifest if the file exists " + |
|
460 "(patch-if)", |
|
461 fileName : "searchpluginspng1.png", |
|
462 relPathDir : "a/b/searchplugins/", |
|
463 originalContents : null, |
|
464 compareContents : null, |
|
465 originalFile : "complete.png", |
|
466 compareFile : "partial.png", |
|
467 originalPerms : 0o666, |
|
468 comparePerms : 0o666 |
|
469 }, { |
|
470 description : "Patched by update.manifest if the file exists " + |
|
471 "(patch-if)", |
|
472 fileName : "searchpluginspng0.png", |
|
473 relPathDir : "a/b/searchplugins/", |
|
474 originalContents : null, |
|
475 compareContents : null, |
|
476 originalFile : "complete.png", |
|
477 compareFile : "partial.png", |
|
478 originalPerms : 0o666, |
|
479 comparePerms : 0o666 |
|
480 }, { |
|
481 description : "Added by update.manifest if the parent directory " + |
|
482 "exists (add-if)", |
|
483 fileName : "extensions1text0", |
|
484 relPathDir : "a/b/distribution/extensions/extensions1/", |
|
485 originalContents : null, |
|
486 compareContents : "FromPartial\n", |
|
487 originalFile : null, |
|
488 compareFile : null, |
|
489 originalPerms : null, |
|
490 comparePerms : 0o644 |
|
491 }, { |
|
492 description : "Patched by update.manifest if the parent directory " + |
|
493 "exists (patch-if)", |
|
494 fileName : "extensions1png1.png", |
|
495 relPathDir : "a/b/distribution/extensions/extensions1/", |
|
496 originalContents : null, |
|
497 compareContents : null, |
|
498 originalFile : "complete.png", |
|
499 compareFile : "partial.png", |
|
500 originalPerms : 0o666, |
|
501 comparePerms : 0o666 |
|
502 }, { |
|
503 description : "Patched by update.manifest if the parent directory " + |
|
504 "exists (patch-if)", |
|
505 fileName : "extensions1png0.png", |
|
506 relPathDir : "a/b/distribution/extensions/extensions1/", |
|
507 originalContents : null, |
|
508 compareContents : null, |
|
509 originalFile : "complete.png", |
|
510 compareFile : "partial.png", |
|
511 originalPerms : 0o666, |
|
512 comparePerms : 0o666 |
|
513 }, { |
|
514 description : "Added by update.manifest if the parent directory " + |
|
515 "exists (add-if)", |
|
516 fileName : "extensions0text0", |
|
517 relPathDir : "a/b/distribution/extensions/extensions0/", |
|
518 originalContents : "ToBeReplacedWithFromPartial\n", |
|
519 compareContents : "FromPartial\n", |
|
520 originalFile : null, |
|
521 compareFile : null, |
|
522 originalPerms : 0o644, |
|
523 comparePerms : 0o644 |
|
524 }, { |
|
525 description : "Patched by update.manifest if the parent directory " + |
|
526 "exists (patch-if)", |
|
527 fileName : "extensions0png1.png", |
|
528 relPathDir : "a/b/distribution/extensions/extensions0/", |
|
529 originalContents : null, |
|
530 compareContents : null, |
|
531 originalFile : "complete.png", |
|
532 compareFile : "partial.png", |
|
533 originalPerms : 0o644, |
|
534 comparePerms : 0o644 |
|
535 }, { |
|
536 description : "Patched by update.manifest if the parent directory " + |
|
537 "exists (patch-if)", |
|
538 fileName : "extensions0png0.png", |
|
539 relPathDir : "a/b/distribution/extensions/extensions0/", |
|
540 originalContents : null, |
|
541 compareContents : null, |
|
542 originalFile : "complete.png", |
|
543 compareFile : "partial.png", |
|
544 originalPerms : 0o644, |
|
545 comparePerms : 0o644 |
|
546 }, { |
|
547 description : "Patched by update.manifest (patch)", |
|
548 fileName : "exe0.exe", |
|
549 relPathDir : "a/b/", |
|
550 originalContents : null, |
|
551 compareContents : null, |
|
552 originalFile : FILE_COMPLETE_EXE, |
|
553 compareFile : FILE_PARTIAL_EXE, |
|
554 originalPerms : 0o755, |
|
555 comparePerms : 0o755 |
|
556 }, { |
|
557 description : "Patched by update.manifest (patch)", |
|
558 fileName : "0exe0.exe", |
|
559 relPathDir : "a/b/0/", |
|
560 originalContents : null, |
|
561 compareContents : null, |
|
562 originalFile : FILE_COMPLETE_EXE, |
|
563 compareFile : FILE_PARTIAL_EXE, |
|
564 originalPerms : 0o755, |
|
565 comparePerms : 0o755 |
|
566 }, { |
|
567 description : "Added by update.manifest (add)", |
|
568 fileName : "00text0", |
|
569 relPathDir : "a/b/0/00/", |
|
570 originalContents : "ToBeReplacedWithFromPartial\n", |
|
571 compareContents : "FromPartial\n", |
|
572 originalFile : null, |
|
573 compareFile : null, |
|
574 originalPerms : 0o644, |
|
575 comparePerms : 0o644 |
|
576 }, { |
|
577 description : "Patched by update.manifest (patch)", |
|
578 fileName : "00png0.png", |
|
579 relPathDir : "a/b/0/00/", |
|
580 originalContents : null, |
|
581 compareContents : null, |
|
582 originalFile : "complete.png", |
|
583 compareFile : "partial.png", |
|
584 originalPerms : 0o666, |
|
585 comparePerms : 0o666 |
|
586 }, { |
|
587 description : "Added by update.manifest (add)", |
|
588 fileName : "20text0", |
|
589 relPathDir : "a/b/2/20/", |
|
590 originalContents : null, |
|
591 compareContents : "FromPartial\n", |
|
592 originalFile : null, |
|
593 compareFile : null, |
|
594 originalPerms : null, |
|
595 comparePerms : 0o644 |
|
596 }, { |
|
597 description : "Added by update.manifest (add)", |
|
598 fileName : "20png0.png", |
|
599 relPathDir : "a/b/2/20/", |
|
600 originalContents : null, |
|
601 compareContents : null, |
|
602 originalFile : null, |
|
603 compareFile : "partial.png", |
|
604 originalPerms : null, |
|
605 comparePerms : 0o644 |
|
606 }, { |
|
607 description : "Added by update.manifest (add)", |
|
608 fileName : "00text2", |
|
609 relPathDir : "a/b/0/00/", |
|
610 originalContents : null, |
|
611 compareContents : "FromPartial\n", |
|
612 originalFile : null, |
|
613 compareFile : null, |
|
614 originalPerms : null, |
|
615 comparePerms : 0o644 |
|
616 }, { |
|
617 description : "Removed by update.manifest (remove)", |
|
618 fileName : "10text0", |
|
619 relPathDir : "a/b/1/10/", |
|
620 originalContents : "ToBeDeleted\n", |
|
621 compareContents : null, |
|
622 originalFile : null, |
|
623 compareFile : null, |
|
624 originalPerms : null, |
|
625 comparePerms : null |
|
626 }, { |
|
627 description : "Removed by update.manifest (remove)", |
|
628 fileName : "00text1", |
|
629 relPathDir : "a/b/0/00/", |
|
630 originalContents : "ToBeDeleted\n", |
|
631 compareContents : null, |
|
632 originalFile : null, |
|
633 compareFile : null, |
|
634 originalPerms : null, |
|
635 comparePerms : null |
|
636 }]; |
|
637 |
|
638 // Concatenate the common files to the end of the array. |
|
639 gTestFilesPartialSuccess = gTestFilesPartialSuccess.concat(gTestFilesCommon); |
|
640 |
|
641 /** |
|
642 * The mar files used for the updater tests contain the following remove |
|
643 * operations. |
|
644 * |
|
645 * partial and complete test mar remove operations |
|
646 * ----------------------------------------------- |
|
647 * remove "text1" |
|
648 * remove "text0" |
|
649 * rmrfdir "9/99/" |
|
650 * rmdir "9/99/" |
|
651 * rmrfdir "9/98/" |
|
652 * rmrfdir "9/97/" |
|
653 * rmrfdir "9/96/" |
|
654 * rmrfdir "9/95/" |
|
655 * rmrfdir "9/95/" |
|
656 * rmrfdir "9/94/" |
|
657 * rmdir "9/94/" |
|
658 * rmdir "9/93/" |
|
659 * rmdir "9/92/" |
|
660 * rmdir "9/91/" |
|
661 * rmdir "9/90/" |
|
662 * rmdir "9/90/" |
|
663 * rmrfdir "8/89/" |
|
664 * rmdir "8/89/" |
|
665 * rmrfdir "8/88/" |
|
666 * rmrfdir "8/87/" |
|
667 * rmrfdir "8/86/" |
|
668 * rmrfdir "8/85/" |
|
669 * rmrfdir "8/85/" |
|
670 * rmrfdir "8/84/" |
|
671 * rmdir "8/84/" |
|
672 * rmdir "8/83/" |
|
673 * rmdir "8/82/" |
|
674 * rmdir "8/81/" |
|
675 * rmdir "8/80/" |
|
676 * rmdir "8/80/" |
|
677 * rmrfdir "7/" |
|
678 * rmdir "6/" |
|
679 * remove "5/text1" |
|
680 * remove "5/text0" |
|
681 * rmrfdir "5/" |
|
682 * remove "4/text1" |
|
683 * remove "4/text0" |
|
684 * remove "4/exe0.exe" |
|
685 * rmdir "4/" |
|
686 * remove "3/text1" |
|
687 * remove "3/text0" |
|
688 * |
|
689 * partial test mar additional remove operations |
|
690 * --------------------------------------------- |
|
691 * remove "0/00/00text1" |
|
692 * remove "1/10/10text0" |
|
693 * rmdir "1/10/" |
|
694 * rmdir "1/" |
|
695 */ |
|
696 var gTestDirsCommon = [ |
|
697 { |
|
698 relPathDir : "a/b/3/", |
|
699 dirRemoved : false, |
|
700 files : ["3text0", "3text1"], |
|
701 filesRemoved : true |
|
702 }, { |
|
703 relPathDir : "a/b/4/", |
|
704 dirRemoved : true, |
|
705 files : ["4text0", "4text1"], |
|
706 filesRemoved : true |
|
707 }, { |
|
708 relPathDir : "a/b/5/", |
|
709 dirRemoved : true, |
|
710 files : ["5test.exe", "5text0", "5text1"], |
|
711 filesRemoved : true |
|
712 }, { |
|
713 relPathDir : "a/b/6/", |
|
714 dirRemoved : true |
|
715 }, { |
|
716 relPathDir : "a/b/7/", |
|
717 dirRemoved : true, |
|
718 files : ["7text0", "7text1"], |
|
719 subDirs : ["70/", "71/"], |
|
720 subDirFiles : ["7xtest.exe", "7xtext0", "7xtext1"] |
|
721 }, { |
|
722 relPathDir : "a/b/8/", |
|
723 dirRemoved : false |
|
724 }, { |
|
725 relPathDir : "a/b/8/80/", |
|
726 dirRemoved : true |
|
727 }, { |
|
728 relPathDir : "a/b/8/81/", |
|
729 dirRemoved : false, |
|
730 files : ["81text0", "81text1"] |
|
731 }, { |
|
732 relPathDir : "a/b/8/82/", |
|
733 dirRemoved : false, |
|
734 subDirs : ["820/", "821/"] |
|
735 }, { |
|
736 relPathDir : "a/b/8/83/", |
|
737 dirRemoved : true |
|
738 }, { |
|
739 relPathDir : "a/b/8/84/", |
|
740 dirRemoved : true |
|
741 }, { |
|
742 relPathDir : "a/b/8/85/", |
|
743 dirRemoved : true |
|
744 }, { |
|
745 relPathDir : "a/b/8/86/", |
|
746 dirRemoved : true, |
|
747 files : ["86text0", "86text1"] |
|
748 }, { |
|
749 relPathDir : "a/b/8/87/", |
|
750 dirRemoved : true, |
|
751 subDirs : ["870/", "871/"], |
|
752 subDirFiles : ["87xtext0", "87xtext1"] |
|
753 }, { |
|
754 relPathDir : "a/b/8/88/", |
|
755 dirRemoved : true |
|
756 }, { |
|
757 relPathDir : "a/b/8/89/", |
|
758 dirRemoved : true |
|
759 }, { |
|
760 relPathDir : "a/b/9/90/", |
|
761 dirRemoved : true |
|
762 }, { |
|
763 relPathDir : "a/b/9/91/", |
|
764 dirRemoved : false, |
|
765 files : ["91text0", "91text1"] |
|
766 }, { |
|
767 relPathDir : "a/b/9/92/", |
|
768 dirRemoved : false, |
|
769 subDirs : ["920/", "921/"] |
|
770 }, { |
|
771 relPathDir : "a/b/9/93/", |
|
772 dirRemoved : true |
|
773 }, { |
|
774 relPathDir : "a/b/9/94/", |
|
775 dirRemoved : true |
|
776 }, { |
|
777 relPathDir : "a/b/9/95/", |
|
778 dirRemoved : true |
|
779 }, { |
|
780 relPathDir : "a/b/9/96/", |
|
781 dirRemoved : true, |
|
782 files : ["96text0", "96text1"] |
|
783 }, { |
|
784 relPathDir : "a/b/9/97/", |
|
785 dirRemoved : true, |
|
786 subDirs : ["970/", "971/"], |
|
787 subDirFiles : ["97xtext0", "97xtext1"] |
|
788 }, { |
|
789 relPathDir : "a/b/9/98/", |
|
790 dirRemoved : true |
|
791 }, { |
|
792 relPathDir : "a/b/9/99/", |
|
793 dirRemoved : true |
|
794 }]; |
|
795 |
|
796 // Directories for a complete successful update. This array can be used for a |
|
797 // complete failed update by calling setTestFilesAndDirsForFailure. |
|
798 var gTestDirsCompleteSuccess = [ |
|
799 { |
|
800 description : "Removed by precomplete (rmdir)", |
|
801 relPathDir : "a/b/2/20/", |
|
802 dirRemoved : true |
|
803 }, { |
|
804 description : "Removed by precomplete (rmdir)", |
|
805 relPathDir : "a/b/2/", |
|
806 dirRemoved : true |
|
807 }]; |
|
808 |
|
809 // Concatenate the common files to the beginning of the array. |
|
810 gTestDirsCompleteSuccess = gTestDirsCommon.concat(gTestDirsCompleteSuccess); |
|
811 |
|
812 // Directories for a partial successful update. This array can be used for a |
|
813 // partial failed update by calling setTestFilesAndDirsForFailure. |
|
814 var gTestDirsPartialSuccess = [ |
|
815 { |
|
816 description : "Removed by update.manifest (rmdir)", |
|
817 relPathDir : "a/b/1/10/", |
|
818 dirRemoved : true |
|
819 }, { |
|
820 description : "Removed by update.manifest (rmdir)", |
|
821 relPathDir : "a/b/1/", |
|
822 dirRemoved : true |
|
823 }]; |
|
824 |
|
825 // Concatenate the common files to the beginning of the array. |
|
826 gTestDirsPartialSuccess = gTestDirsCommon.concat(gTestDirsPartialSuccess); |
|
827 |
|
828 // Extra directories to check for existence for both complete and partial |
|
829 // updates. Whether they exist or not is set when calling setupUpdaterTest. |
|
830 var gTestExtraDirs = [ |
|
831 { |
|
832 relPathDir : DIR_UPDATED, |
|
833 dirExists : false |
|
834 }, { |
|
835 relPathDir : DIR_TOBEDELETED, |
|
836 dirExists : false |
|
837 }]; |
|
838 |
|
839 // This makes it possible to run most tests on xulrunner where the update |
|
840 // channel default preference is not set. |
|
841 if (MOZ_APP_NAME == "xulrunner") { |
|
842 try { |
|
843 gDefaultPrefBranch.getCharPref(PREF_APP_UPDATE_CHANNEL); |
|
844 } catch (e) { |
|
845 setUpdateChannel("test_channel"); |
|
846 } |
|
847 } |
|
848 |
|
849 /** |
|
850 * Helper function for setting up the test environment. |
|
851 */ |
|
852 function setupTestCommon() { |
|
853 logTestInfo("start - general test setup"); |
|
854 |
|
855 do_test_pending(); |
|
856 |
|
857 if (gTestID) { |
|
858 do_throw("setupTestCommon should only be called once!"); |
|
859 } |
|
860 |
|
861 let caller = Components.stack.caller; |
|
862 gTestID = caller.filename.toString().split("/").pop().split(".")[0]; |
|
863 |
|
864 if (DEBUG_TEST_LOG) { |
|
865 let logFile = do_get_file(gTestID + ".log", true); |
|
866 if (logFile.exists()) { |
|
867 gPassed = false; |
|
868 logTestInfo("start - dumping previous test run log"); |
|
869 logTestInfo("\n" + readFile(logFile) + "\n"); |
|
870 logTestInfo("finish - dumping previous test run log"); |
|
871 if (gDeleteLogFile) { |
|
872 logFile.remove(false); |
|
873 } |
|
874 do_throw("The parallel run of this test failed. Failing non-parallel " + |
|
875 "test so the log from the parallel run can be displayed in " + |
|
876 "non-parallel log.") |
|
877 } else { |
|
878 gRealDump = dump; |
|
879 dump = dumpOverride; |
|
880 } |
|
881 } |
|
882 |
|
883 // Don't attempt to show a prompt when an update finishes. |
|
884 Services.prefs.setBoolPref(PREF_APP_UPDATE_SILENT, true); |
|
885 |
|
886 gGREDirOrig = getGREDir(); |
|
887 gAppDirOrig = getAppBaseDir(); |
|
888 |
|
889 let applyDir = getApplyDirFile(null, true).parent; |
|
890 |
|
891 // Try to remove the directory used to apply updates and the updates directory |
|
892 // on platforms other than Windows. Since the test hasn't ran yet and the |
|
893 // directory shouldn't exist finished this is non-fatal for the test. |
|
894 if (applyDir.exists()) { |
|
895 logTestInfo("attempting to remove directory. Path: " + applyDir.path); |
|
896 try { |
|
897 removeDirRecursive(applyDir); |
|
898 } catch (e) { |
|
899 logTestInfo("non-fatal error removing directory. Path: " + |
|
900 applyDir.path + ", Exception: " + e); |
|
901 } |
|
902 } |
|
903 |
|
904 // adjustGeneralPaths registers a cleanup function that calls end_test when |
|
905 // it is defined as a function. |
|
906 adjustGeneralPaths(); |
|
907 |
|
908 // Remove the updates directory on Windows which is located outside of the |
|
909 // application directory after the call to adjustGeneralPaths has set it up. |
|
910 // Since the test hasn't ran yet and the directory shouldn't exist finished |
|
911 // this is non-fatal for the test. |
|
912 if (IS_WIN) { |
|
913 let updatesDir = getMockUpdRootD(); |
|
914 if (updatesDir.exists()) { |
|
915 logTestInfo("attempting to remove directory. Path: " + updatesDir.path); |
|
916 try { |
|
917 removeDirRecursive(updatesDir); |
|
918 } catch (e) { |
|
919 logTestInfo("non-fatal error removing directory. Path: " + |
|
920 updatesDir.path + ", Exception: " + e); |
|
921 } |
|
922 } |
|
923 } |
|
924 |
|
925 logTestInfo("finish - general test setup"); |
|
926 } |
|
927 |
|
928 /** |
|
929 * Nulls out the most commonly used global vars used by tests to prevent leaks |
|
930 * as needed and attempts to restore the system to its original state. |
|
931 */ |
|
932 function cleanupTestCommon() { |
|
933 logTestInfo("start - general test cleanup"); |
|
934 |
|
935 // Force the update manager to reload the update data to prevent it from |
|
936 // writing the old data to the files that have just been removed. |
|
937 reloadUpdateManagerData(); |
|
938 |
|
939 if (gChannel) { |
|
940 gPrefRoot.removeObserver(PREF_APP_UPDATE_CHANNEL, observer); |
|
941 } |
|
942 |
|
943 // Call app update's observe method passing xpcom-shutdown to test that the |
|
944 // shutdown of app update runs without throwing or leaking. The observer |
|
945 // method is used directly instead of calling notifyObservers so components |
|
946 // outside of the scope of this test don't assert and thereby cause app update |
|
947 // tests to fail. |
|
948 gAUS.observe(null, "xpcom-shutdown", ""); |
|
949 |
|
950 if (gXHR) { |
|
951 gXHRCallback = null; |
|
952 |
|
953 gXHR.responseXML = null; |
|
954 // null out the event handlers to prevent a mFreeCount leak of 1 |
|
955 gXHR.onerror = null; |
|
956 gXHR.onload = null; |
|
957 gXHR.onprogress = null; |
|
958 |
|
959 gXHR = null; |
|
960 } |
|
961 |
|
962 gTestserver = null; |
|
963 |
|
964 if (IS_UNIX) { |
|
965 // This will delete the launch script if it exists. |
|
966 getLaunchScript(); |
|
967 } |
|
968 |
|
969 if (IS_WIN && MOZ_APP_BASENAME) { |
|
970 let appDir = getApplyDirFile(null, true); |
|
971 let vendor = MOZ_APP_VENDOR ? MOZ_APP_VENDOR : "Mozilla"; |
|
972 const REG_PATH = "SOFTWARE\\" + vendor + "\\" + MOZ_APP_BASENAME + |
|
973 "\\TaskBarIDs"; |
|
974 let key = AUS_Cc["@mozilla.org/windows-registry-key;1"]. |
|
975 createInstance(AUS_Ci.nsIWindowsRegKey); |
|
976 try { |
|
977 key.open(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, REG_PATH, |
|
978 AUS_Ci.nsIWindowsRegKey.ACCESS_ALL); |
|
979 if (key.hasValue(appDir.path)) { |
|
980 key.removeValue(appDir.path); |
|
981 } |
|
982 } catch (e) { |
|
983 } |
|
984 try { |
|
985 key.open(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, REG_PATH, |
|
986 AUS_Ci.nsIWindowsRegKey.ACCESS_ALL); |
|
987 if (key.hasValue(appDir.path)) { |
|
988 key.removeValue(appDir.path); |
|
989 } |
|
990 } catch (e) { |
|
991 } |
|
992 } |
|
993 |
|
994 // The updates directory is located outside of the application directory on |
|
995 // Windows so it also needs to be removed. |
|
996 if (IS_WIN) { |
|
997 let updatesDir = getMockUpdRootD(); |
|
998 // Try to remove the directory used to apply updates. Since the test has |
|
999 // already finished this is non-fatal for the test. |
|
1000 if (updatesDir.exists()) { |
|
1001 logTestInfo("attempting to remove directory. Path: " + updatesDir.path); |
|
1002 try { |
|
1003 removeDirRecursive(updatesDir); |
|
1004 } catch (e) { |
|
1005 logTestInfo("non-fatal error removing directory. Path: " + |
|
1006 updatesDir.path + ", Exception: " + e); |
|
1007 } |
|
1008 } |
|
1009 } |
|
1010 |
|
1011 let applyDir = getApplyDirFile(null, true).parent; |
|
1012 |
|
1013 // Try to remove the directory used to apply updates. Since the test has |
|
1014 // already finished this is non-fatal for the test. |
|
1015 if (applyDir.exists()) { |
|
1016 logTestInfo("attempting to remove directory. Path: " + applyDir.path); |
|
1017 try { |
|
1018 removeDirRecursive(applyDir); |
|
1019 } catch (e) { |
|
1020 logTestInfo("non-fatal error removing directory. Path: " + |
|
1021 applyDir.path + ", Exception: " + e); |
|
1022 } |
|
1023 } |
|
1024 |
|
1025 resetEnvironment(); |
|
1026 |
|
1027 logTestInfo("finish - general test cleanup"); |
|
1028 |
|
1029 if (gRealDump) { |
|
1030 dump = gRealDump; |
|
1031 gRealDump = null; |
|
1032 } |
|
1033 |
|
1034 if (DEBUG_TEST_LOG && !gPassed) { |
|
1035 let fos = AUS_Cc["@mozilla.org/network/file-output-stream;1"]. |
|
1036 createInstance(AUS_Ci.nsIFileOutputStream); |
|
1037 let logFile = do_get_file(gTestID + ".log", true); |
|
1038 if (!logFile.exists()) { |
|
1039 logFile.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE); |
|
1040 } |
|
1041 fos.init(logFile, MODE_WRONLY | MODE_CREATE | MODE_APPEND, PERMS_FILE, 0); |
|
1042 fos.write(gTestLogText, gTestLogText.length); |
|
1043 fos.close(); |
|
1044 } |
|
1045 |
|
1046 if (DEBUG_TEST_LOG) { |
|
1047 gTestLogText = null; |
|
1048 } else { |
|
1049 let logFile = do_get_file(gTestID + ".log", true); |
|
1050 if (logFile.exists()) { |
|
1051 logFile.remove(false); |
|
1052 } |
|
1053 } |
|
1054 } |
|
1055 |
|
1056 /** |
|
1057 * Helper function to store the log output of calls to dump in a variable so the |
|
1058 * values can be written to a file for a parallel run of a test and printed to |
|
1059 * the log file when the test runs synchronously. |
|
1060 */ |
|
1061 function dumpOverride(aText) { |
|
1062 gTestLogText += aText; |
|
1063 gRealDump(aText); |
|
1064 } |
|
1065 |
|
1066 /** |
|
1067 * Helper function that calls do_test_finished that tracks whether a parallel |
|
1068 * run of a test passed when it runs synchronously so the log output can be |
|
1069 * inspected. |
|
1070 */ |
|
1071 function doTestFinish() { |
|
1072 if (gPassed === undefined) { |
|
1073 gPassed = true; |
|
1074 } |
|
1075 do_test_finished(); |
|
1076 } |
|
1077 |
|
1078 /** |
|
1079 * Sets the most commonly used preferences used by tests |
|
1080 */ |
|
1081 function setDefaultPrefs() { |
|
1082 Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, true); |
|
1083 Services.prefs.setBoolPref(PREF_APP_UPDATE_METRO_ENABLED, true); |
|
1084 // Don't display UI for a successful installation. Some apps may not set this |
|
1085 // pref to false like Firefox does. |
|
1086 Services.prefs.setBoolPref(PREF_APP_UPDATE_SHOW_INSTALLED_UI, false); |
|
1087 // Enable Update logging |
|
1088 Services.prefs.setBoolPref(PREF_APP_UPDATE_LOG, true); |
|
1089 } |
|
1090 |
|
1091 /** |
|
1092 * Helper function for updater binary tests that sets the appropriate values |
|
1093 * to check for update failures. |
|
1094 */ |
|
1095 function setTestFilesAndDirsForFailure() { |
|
1096 gTestFiles.forEach(function STFADFF_Files(aTestFile) { |
|
1097 aTestFile.compareContents = aTestFile.originalContents; |
|
1098 aTestFile.compareFile = aTestFile.originalFile; |
|
1099 aTestFile.comparePerms = aTestFile.originalPerms; |
|
1100 }); |
|
1101 |
|
1102 gTestDirs.forEach(function STFADFF_Dirs(aTestDir) { |
|
1103 aTestDir.dirRemoved = false; |
|
1104 if (aTestDir.filesRemoved) { |
|
1105 aTestDir.filesRemoved = false; |
|
1106 } |
|
1107 }); |
|
1108 } |
|
1109 |
|
1110 /** |
|
1111 * Initializes the most commonly used settings and creates an instance of the |
|
1112 * update service stub. |
|
1113 */ |
|
1114 function standardInit() { |
|
1115 createAppInfo("xpcshell@tests.mozilla.org", APP_INFO_NAME, "1.0", "2.0"); |
|
1116 setDefaultPrefs(); |
|
1117 // Initialize the update service stub component |
|
1118 initUpdateServiceStub(); |
|
1119 } |
|
1120 |
|
1121 /** |
|
1122 * Custom path handler for the http server |
|
1123 * |
|
1124 * @param aMetadata |
|
1125 * The http metadata for the request. |
|
1126 * @param aResponse |
|
1127 * The http response for the request. |
|
1128 */ |
|
1129 function pathHandler(aMetadata, aResponse) { |
|
1130 aResponse.setHeader("Content-Type", "text/xml", false); |
|
1131 aResponse.setStatusLine(aMetadata.httpVersion, gResponseStatusCode, "OK"); |
|
1132 aResponse.bodyOutputStream.write(gResponseBody, gResponseBody.length); |
|
1133 } |
|
1134 |
|
1135 /** |
|
1136 * Helper function for getting the application version from the application.ini |
|
1137 * file. This will look in both the GRE and the application directories for the |
|
1138 * application.ini file. |
|
1139 * |
|
1140 * @return The version string from the application.ini file. |
|
1141 * @throws If the application.ini file is not found. |
|
1142 */ |
|
1143 function getAppVersion() { |
|
1144 // Read the application.ini and use its application version. |
|
1145 let iniFile = gGREDirOrig.clone(); |
|
1146 iniFile.append("application.ini"); |
|
1147 if (!iniFile.exists()) { |
|
1148 iniFile = gAppDirOrig.clone(); |
|
1149 iniFile.append("application.ini"); |
|
1150 } |
|
1151 if (!iniFile.exists()) { |
|
1152 do_throw("Unable to find application.ini!"); |
|
1153 } |
|
1154 let iniParser = AUS_Cc["@mozilla.org/xpcom/ini-parser-factory;1"]. |
|
1155 getService(AUS_Ci.nsIINIParserFactory). |
|
1156 createINIParser(iniFile); |
|
1157 return iniParser.getString("App", "Version"); |
|
1158 } |
|
1159 |
|
1160 /** |
|
1161 * Helper function for getting the relative path to the directory where the |
|
1162 * application binary is located (e.g. <test_file_leafname>/dir.app/). |
|
1163 * |
|
1164 * Note: The dir.app subdirectory under <test_file_leafname> is needed for |
|
1165 * platforms other than Mac OS X so the tests can run in parallel due to |
|
1166 * update staging creating a lock file named moz_update_in_progress.lock in |
|
1167 * the parent directory of the installation directory. |
|
1168 * |
|
1169 * @return The relative path to the directory where application binary is |
|
1170 * located. |
|
1171 */ |
|
1172 function getApplyDirPath() { |
|
1173 return gTestID + "/dir.app/"; |
|
1174 } |
|
1175 |
|
1176 /** |
|
1177 * Helper function for getting the nsIFile for a file in the directory where the |
|
1178 * update will be applied. |
|
1179 * |
|
1180 * The files for the update are located two directories below the apply to |
|
1181 * directory since Mac OS X sets the last modified time for the root directory |
|
1182 * to the current time and if the update changes any files in the root directory |
|
1183 * then it wouldn't be possible to test (bug 600098). |
|
1184 * |
|
1185 * @param aRelPath (optional) |
|
1186 * The relative path to the file or directory to get from the root of |
|
1187 * the test's directory. If not specified the test's directory will be |
|
1188 * returned. |
|
1189 * @param aAllowNonexistent (optional) |
|
1190 * Whether the file must exist. If false or not specified the file must |
|
1191 * exist or the function will throw. |
|
1192 * @return The nsIFile for the file in the directory where the update will be |
|
1193 * applied. |
|
1194 */ |
|
1195 function getApplyDirFile(aRelPath, aAllowNonexistent) { |
|
1196 let relpath = getApplyDirPath() + (aRelPath ? aRelPath : ""); |
|
1197 return do_get_file(relpath, aAllowNonexistent); |
|
1198 } |
|
1199 |
|
1200 /** |
|
1201 * Helper function for getting the relative path to the directory where the |
|
1202 * test data files are located. |
|
1203 * |
|
1204 * @return The relative path to the directory where the test data files are |
|
1205 * located. |
|
1206 */ |
|
1207 function getTestDirPath() { |
|
1208 return "../data/"; |
|
1209 } |
|
1210 |
|
1211 /** |
|
1212 * Helper function for getting the nsIFile for a file in the test data |
|
1213 * directory. |
|
1214 * |
|
1215 * @param aRelPath (optional) |
|
1216 * The relative path to the file or directory to get from the root of |
|
1217 * the test's data directory. If not specified the test's data |
|
1218 * directory will be returned. |
|
1219 * @return The nsIFile for the file in the test data directory. |
|
1220 * @throws If the file or directory does not exist. |
|
1221 */ |
|
1222 function getTestDirFile(aRelPath) { |
|
1223 let relpath = getTestDirPath() + (aRelPath ? aRelPath : ""); |
|
1224 return do_get_file(relpath, false); |
|
1225 } |
|
1226 |
|
1227 /** |
|
1228 * Helper function for getting the directory that was updated. This can either |
|
1229 * be the directory where the application binary is located or the directory |
|
1230 * that contains the staged update. |
|
1231 */ |
|
1232 function getUpdatedDirPath() { |
|
1233 return getApplyDirPath() + (gStageUpdate ? DIR_UPDATED + "/" : ""); |
|
1234 } |
|
1235 |
|
1236 #ifdef XP_WIN |
|
1237 XPCOMUtils.defineLazyGetter(this, "gInstallDirPathHash", |
|
1238 function test_gInstallDirPathHash() { |
|
1239 // Figure out where we should check for a cached hash value |
|
1240 if (!MOZ_APP_BASENAME) |
|
1241 return null; |
|
1242 |
|
1243 let vendor = MOZ_APP_VENDOR ? MOZ_APP_VENDOR : "Mozilla"; |
|
1244 let appDir = getApplyDirFile(null, true); |
|
1245 |
|
1246 const REG_PATH = "SOFTWARE\\" + vendor + "\\" + MOZ_APP_BASENAME + |
|
1247 "\\TaskBarIDs"; |
|
1248 let regKey = AUS_Cc["@mozilla.org/windows-registry-key;1"]. |
|
1249 createInstance(AUS_Ci.nsIWindowsRegKey); |
|
1250 try { |
|
1251 regKey.open(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, REG_PATH, |
|
1252 AUS_Ci.nsIWindowsRegKey.ACCESS_ALL); |
|
1253 regKey.writeStringValue(appDir.path, gTestID); |
|
1254 return gTestID; |
|
1255 } catch (e) { |
|
1256 } |
|
1257 |
|
1258 try { |
|
1259 regKey.create(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, REG_PATH, |
|
1260 AUS_Ci.nsIWindowsRegKey.ACCESS_ALL); |
|
1261 regKey.writeStringValue(appDir.path, gTestID); |
|
1262 return gTestID; |
|
1263 } catch (e) { |
|
1264 logTestInfo("failed to create registry key. Registry Path: " + REG_PATH + |
|
1265 ", Key Name: " + appDir.path + ", Key Value: " + gTestID + |
|
1266 ", Exception " + e); |
|
1267 } |
|
1268 return null; |
|
1269 }); |
|
1270 |
|
1271 XPCOMUtils.defineLazyGetter(this, "gLocalAppDataDir", |
|
1272 function test_gLocalAppDataDir() { |
|
1273 const CSIDL_LOCAL_APPDATA = 0x1c; |
|
1274 |
|
1275 AUS_Cu.import("resource://gre/modules/ctypes.jsm"); |
|
1276 let lib = ctypes.open("shell32"); |
|
1277 let SHGetSpecialFolderPath = lib.declare("SHGetSpecialFolderPathW", |
|
1278 ctypes.winapi_abi, |
|
1279 ctypes.bool, /* bool(return) */ |
|
1280 ctypes.int32_t, /* HWND hwndOwner */ |
|
1281 ctypes.jschar.ptr, /* LPTSTR lpszPath */ |
|
1282 ctypes.int32_t, /* int csidl */ |
|
1283 ctypes.bool /* BOOL fCreate */); |
|
1284 |
|
1285 let aryPathLocalAppData = ctypes.jschar.array()(260); |
|
1286 let rv = SHGetSpecialFolderPath(0, aryPathLocalAppData, CSIDL_LOCAL_APPDATA, false); |
|
1287 lib.close(); |
|
1288 |
|
1289 let pathLocalAppData = aryPathLocalAppData.readString(); // Convert the c-string to js-string |
|
1290 let updatesDir = AUS_Cc["@mozilla.org/file/local;1"]. |
|
1291 createInstance(AUS_Ci.nsILocalFile); |
|
1292 updatesDir.initWithPath(pathLocalAppData); |
|
1293 return updatesDir; |
|
1294 }); |
|
1295 |
|
1296 XPCOMUtils.defineLazyGetter(this, "gProgFilesDir", |
|
1297 function test_gProgFilesDir() { |
|
1298 const CSIDL_PROGRAM_FILES = 0x26; |
|
1299 |
|
1300 AUS_Cu.import("resource://gre/modules/ctypes.jsm"); |
|
1301 let lib = ctypes.open("shell32"); |
|
1302 let SHGetSpecialFolderPath = lib.declare("SHGetSpecialFolderPathW", |
|
1303 ctypes.winapi_abi, |
|
1304 ctypes.bool, /* bool(return) */ |
|
1305 ctypes.int32_t, /* HWND hwndOwner */ |
|
1306 ctypes.jschar.ptr, /* LPTSTR lpszPath */ |
|
1307 ctypes.int32_t, /* int csidl */ |
|
1308 ctypes.bool /* BOOL fCreate */); |
|
1309 |
|
1310 let aryPathProgFiles = ctypes.jschar.array()(260); |
|
1311 let rv = SHGetSpecialFolderPath(0, aryPathProgFiles, CSIDL_PROGRAM_FILES, false); |
|
1312 lib.close(); |
|
1313 |
|
1314 let pathProgFiles = aryPathProgFiles.readString(); // Convert the c-string to js-string |
|
1315 let progFilesDir = AUS_Cc["@mozilla.org/file/local;1"]. |
|
1316 createInstance(AUS_Ci.nsILocalFile); |
|
1317 progFilesDir.initWithPath(pathProgFiles); |
|
1318 return progFilesDir; |
|
1319 }); |
|
1320 |
|
1321 /** |
|
1322 * Helper function for getting the update root directory used by the tests. This |
|
1323 * returns the same directory as returned by nsXREDirProvider::GetUpdateRootDir |
|
1324 * in nsXREDirProvider.cpp so an application will be able to find the update |
|
1325 * when running a test that launches the application. |
|
1326 */ |
|
1327 function getMockUpdRootD() { |
|
1328 let localAppDataDir = gLocalAppDataDir.clone(); |
|
1329 let progFilesDir = gProgFilesDir.clone(); |
|
1330 let appDir = Services.dirsvc.get(XRE_EXECUTABLE_FILE, AUS_Ci.nsIFile).parent; |
|
1331 |
|
1332 let appDirPath = appDir.path; |
|
1333 var relPathUpdates = ""; |
|
1334 if (gInstallDirPathHash && (MOZ_APP_VENDOR || MOZ_APP_BASENAME)) { |
|
1335 relPathUpdates += (MOZ_APP_VENDOR ? MOZ_APP_VENDOR : MOZ_APP_BASENAME) + |
|
1336 "\\" + DIR_UPDATES + "\\" + gInstallDirPathHash; |
|
1337 } |
|
1338 |
|
1339 if (!relPathUpdates) { |
|
1340 if (appDirPath.length > progFilesDir.path.length) { |
|
1341 if (appDirPath.substr(0, progFilesDir.path.length) == progFilesDir.path) { |
|
1342 if (MOZ_APP_VENDOR && MOZ_APP_BASENAME) { |
|
1343 relPathUpdates += MOZ_APP_VENDOR + "\\" + MOZ_APP_BASENAME; |
|
1344 } else { |
|
1345 relPathUpdates += MOZ_APP_BASENAME; |
|
1346 } |
|
1347 relPathUpdates += appDirPath.substr(progFilesDir.path.length); |
|
1348 } |
|
1349 } |
|
1350 } |
|
1351 |
|
1352 if (!relPathUpdates) { |
|
1353 if (MOZ_APP_VENDOR && MOZ_APP_BASENAME) { |
|
1354 relPathUpdates += MOZ_APP_VENDOR + "\\" + MOZ_APP_BASENAME; |
|
1355 } else { |
|
1356 relPathUpdates += MOZ_APP_BASENAME; |
|
1357 } |
|
1358 relPathUpdates += "\\" + MOZ_APP_NAME; |
|
1359 } |
|
1360 |
|
1361 var updatesDir = AUS_Cc["@mozilla.org/file/local;1"]. |
|
1362 createInstance(AUS_Ci.nsILocalFile); |
|
1363 updatesDir.initWithPath(localAppDataDir.path + "\\" + relPathUpdates); |
|
1364 logTestInfo("returning UpdRootD Path: " + updatesDir.path); |
|
1365 return updatesDir; |
|
1366 } |
|
1367 #else |
|
1368 /** |
|
1369 * Helper function for getting the update root directory used by the tests. This |
|
1370 * returns the same directory as returned by nsXREDirProvider::GetUpdateRootDir |
|
1371 * in nsXREDirProvider.cpp so an application will be able to find the update |
|
1372 * when running a test that launches the application. |
|
1373 */ |
|
1374 function getMockUpdRootD() { |
|
1375 return getApplyDirFile(DIR_BIN_REL_PATH, true); |
|
1376 } |
|
1377 #endif |
|
1378 |
|
1379 /** |
|
1380 * Helper function for getting the nsIFile for the directory where the update |
|
1381 * has been applied. |
|
1382 * |
|
1383 * This will be the same as getApplyDirFile for foreground updates, but will |
|
1384 * point to a different file for the case of staged updates. |
|
1385 * |
|
1386 * Functions which attempt to access the files in the updated directory should |
|
1387 * be using this instead of getApplyDirFile. |
|
1388 * |
|
1389 * @param aRelPath (optional) |
|
1390 * The relative path to the file or directory to get from the root of |
|
1391 * the test's directory. If not specified the test's directory will be |
|
1392 * returned. |
|
1393 * @param aAllowNonexistent (optional) |
|
1394 * Whether the file must exist. If false or not specified the file must |
|
1395 * exist or the function will throw. |
|
1396 * @return The nsIFile for the directory where the update has been applied. |
|
1397 */ |
|
1398 function getTargetDirFile(aRelPath, aAllowNonexistent) { |
|
1399 let relpath = getUpdatedDirPath() + (aRelPath ? aRelPath : ""); |
|
1400 return do_get_file(relpath, aAllowNonexistent); |
|
1401 } |
|
1402 |
|
1403 if (IS_WIN) { |
|
1404 const kLockFileName = "updated.update_in_progress.lock"; |
|
1405 /** |
|
1406 * Helper function for locking a directory on Windows. |
|
1407 * |
|
1408 * @param aDir |
|
1409 * The nsIFile for the directory to lock. |
|
1410 */ |
|
1411 function lockDirectory(aDir) { |
|
1412 var file = aDir.clone(); |
|
1413 file.append(kLockFileName); |
|
1414 file.create(file.NORMAL_FILE_TYPE, 0o444); |
|
1415 file.QueryInterface(AUS_Ci.nsILocalFileWin); |
|
1416 file.fileAttributesWin |= file.WFA_READONLY; |
|
1417 file.fileAttributesWin &= ~file.WFA_READWRITE; |
|
1418 logTestInfo("testing the successful creation of the lock file"); |
|
1419 do_check_true(file.exists()); |
|
1420 do_check_false(file.isWritable()); |
|
1421 } |
|
1422 /** |
|
1423 * Helper function for unlocking a directory on Windows. |
|
1424 * |
|
1425 * @param aDir |
|
1426 * The nsIFile for the directory to unlock. |
|
1427 */ |
|
1428 function unlockDirectory(aDir) { |
|
1429 var file = aDir.clone(); |
|
1430 file.append(kLockFileName); |
|
1431 file.QueryInterface(AUS_Ci.nsILocalFileWin); |
|
1432 file.fileAttributesWin |= file.WFA_READWRITE; |
|
1433 file.fileAttributesWin &= ~file.WFA_READONLY; |
|
1434 logTestInfo("removing and testing the successful removal of the lock file"); |
|
1435 file.remove(false); |
|
1436 do_check_false(file.exists()); |
|
1437 } |
|
1438 } |
|
1439 |
|
1440 /** |
|
1441 * Helper function for updater tests for launching the updater binary to apply |
|
1442 * a mar file. |
|
1443 * |
|
1444 * @param aExpectedExitValue |
|
1445 * The expected exit value from the updater binary. |
|
1446 * @param aExpectedStatus |
|
1447 * The expected value of update.status when the test finishes. |
|
1448 * @param aCallback (optional) |
|
1449 * A callback function that will be called when this function finishes. |
|
1450 * If null no function will be called when this function finishes. |
|
1451 * If not specified the checkUpdateApplied function will be called when |
|
1452 * this function finishes. |
|
1453 */ |
|
1454 function runUpdate(aExpectedExitValue, aExpectedStatus, aCallback) { |
|
1455 // Copy the updater binary to the updates directory. |
|
1456 let binDir = gGREDirOrig.clone(); |
|
1457 let updater = binDir.clone(); |
|
1458 updater.append("updater.app"); |
|
1459 if (!updater.exists()) { |
|
1460 updater = binDir.clone(); |
|
1461 updater.append(FILE_UPDATER_BIN); |
|
1462 if (!updater.exists()) { |
|
1463 do_throw("Unable to find updater binary!"); |
|
1464 } |
|
1465 } |
|
1466 |
|
1467 let updatesDir = getUpdatesPatchDir(); |
|
1468 updater.copyToFollowingLinks(updatesDir, updater.leafName); |
|
1469 let updateBin = updatesDir.clone(); |
|
1470 updateBin.append(updater.leafName); |
|
1471 if (updateBin.leafName == "updater.app") { |
|
1472 updateBin.append("Contents"); |
|
1473 updateBin.append("MacOS"); |
|
1474 updateBin.append("updater"); |
|
1475 if (!updateBin.exists()) { |
|
1476 do_throw("Unable to find the updater executable!"); |
|
1477 } |
|
1478 } |
|
1479 |
|
1480 let applyToDir = getApplyDirFile(null, true); |
|
1481 let applyToDirPath = applyToDir.path; |
|
1482 if (gStageUpdate || gSwitchApp) { |
|
1483 applyToDirPath += "/" + DIR_UPDATED + "/"; |
|
1484 } |
|
1485 |
|
1486 if (IS_WIN) { |
|
1487 // Convert to native path |
|
1488 applyToDirPath = applyToDirPath.replace(/\//g, "\\"); |
|
1489 } |
|
1490 |
|
1491 let callbackApp = getApplyDirFile("a/b/" + gCallbackBinFile); |
|
1492 callbackApp.permissions = PERMS_DIRECTORY; |
|
1493 |
|
1494 let args = [updatesDir.path, applyToDirPath, 0]; |
|
1495 if (gStageUpdate) { |
|
1496 args[2] = -1; |
|
1497 } else { |
|
1498 if (gSwitchApp) { |
|
1499 args[2] = "0/replace"; |
|
1500 } |
|
1501 args = args.concat([callbackApp.parent.path, callbackApp.path]); |
|
1502 args = args.concat(gCallbackArgs); |
|
1503 } |
|
1504 logTestInfo("running the updater: " + updateBin.path + " " + args.join(" ")); |
|
1505 |
|
1506 let env = AUS_Cc["@mozilla.org/process/environment;1"]. |
|
1507 getService(AUS_Ci.nsIEnvironment); |
|
1508 if (gDisableReplaceFallback) { |
|
1509 env.set("MOZ_NO_REPLACE_FALLBACK", "1"); |
|
1510 } |
|
1511 |
|
1512 let process = AUS_Cc["@mozilla.org/process/util;1"]. |
|
1513 createInstance(AUS_Ci.nsIProcess); |
|
1514 process.init(updateBin); |
|
1515 process.run(true, args, args.length); |
|
1516 |
|
1517 if (gDisableReplaceFallback) { |
|
1518 env.set("MOZ_NO_REPLACE_FALLBACK", ""); |
|
1519 } |
|
1520 |
|
1521 let status = readStatusFile(); |
|
1522 if (process.exitValue != aExpectedExitValue || status != aExpectedStatus) { |
|
1523 if (process.exitValue != aExpectedExitValue) { |
|
1524 logTestInfo("updater exited with unexpected value! Got: " + |
|
1525 process.exitValue + ", Expected: " + aExpectedExitValue); |
|
1526 } |
|
1527 if (status != aExpectedStatus) { |
|
1528 logTestInfo("update status is not the expected status! Got: " + status + |
|
1529 ", Expected: " + aExpectedStatus); |
|
1530 } |
|
1531 let updateLog = getUpdatesPatchDir(); |
|
1532 updateLog.append(FILE_UPDATE_LOG); |
|
1533 logTestInfo("contents of " + updateLog.path + ":\n" + |
|
1534 readFileBytes(updateLog).replace(/\r\n/g, "\n")); |
|
1535 } |
|
1536 logTestInfo("testing updater binary process exitValue against expected " + |
|
1537 "exit value"); |
|
1538 do_check_eq(process.exitValue, aExpectedExitValue); |
|
1539 logTestInfo("testing update status against expected status"); |
|
1540 do_check_eq(status, aExpectedStatus); |
|
1541 |
|
1542 if (aCallback !== null) { |
|
1543 if (typeof(aCallback) == typeof(Function)) { |
|
1544 aCallback(); |
|
1545 } else { |
|
1546 checkUpdateApplied(); |
|
1547 } |
|
1548 } |
|
1549 } |
|
1550 /** |
|
1551 * Helper function for updater tests to stage an update. |
|
1552 */ |
|
1553 function stageUpdate() { |
|
1554 logTestInfo("start - staging update"); |
|
1555 Services.obs.addObserver(gUpdateStagedObserver, "update-staged", false); |
|
1556 |
|
1557 setEnvironment(); |
|
1558 // Stage the update. |
|
1559 AUS_Cc["@mozilla.org/updates/update-processor;1"]. |
|
1560 createInstance(AUS_Ci.nsIUpdateProcessor). |
|
1561 processUpdate(gUpdateManager.activeUpdate); |
|
1562 resetEnvironment(); |
|
1563 |
|
1564 logTestInfo("finish - staging update"); |
|
1565 } |
|
1566 |
|
1567 /** |
|
1568 * Helper function to check whether the maintenance service updater tests should |
|
1569 * run. See bug 711660 for more details. |
|
1570 * |
|
1571 * @param aFirstTest |
|
1572 * Whether this is the first test within the test. |
|
1573 * @return true if the test should run and false if it shouldn't. |
|
1574 */ |
|
1575 function shouldRunServiceTest(aFirstTest) { |
|
1576 // In case the machine is running an old maintenance service or if it |
|
1577 // is not installed, and permissions exist to install it. Then install |
|
1578 // the newer bin that we have. |
|
1579 attemptServiceInstall(); |
|
1580 |
|
1581 let binDir = getGREDir(); |
|
1582 let updaterBin = binDir.clone(); |
|
1583 updaterBin.append(FILE_UPDATER_BIN); |
|
1584 if (!updaterBin.exists()) { |
|
1585 do_throw("Unable to find updater binary!"); |
|
1586 } |
|
1587 |
|
1588 let updaterBinPath = updaterBin.path; |
|
1589 if (/ /.test(updaterBinPath)) { |
|
1590 updaterBinPath = '"' + updaterBinPath + '"'; |
|
1591 } |
|
1592 |
|
1593 const REG_PATH = "SOFTWARE\\Mozilla\\MaintenanceService\\" + |
|
1594 "3932ecacee736d366d6436db0f55bce4"; |
|
1595 |
|
1596 let key = AUS_Cc["@mozilla.org/windows-registry-key;1"]. |
|
1597 createInstance(AUS_Ci.nsIWindowsRegKey); |
|
1598 try { |
|
1599 key.open(AUS_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, REG_PATH, |
|
1600 AUS_Ci.nsIWindowsRegKey.ACCESS_READ | key.WOW64_64); |
|
1601 } catch (e) { |
|
1602 #ifndef DISABLE_UPDATER_AUTHENTICODE_CHECK |
|
1603 // The build system could sign the files and not have the test registry key |
|
1604 // in which case we should fail the test by throwing so it can be fixed. |
|
1605 if (isBinarySigned(updaterBinPath)) { |
|
1606 do_throw("binary is signed but the test registry key does not exists!"); |
|
1607 } |
|
1608 #endif |
|
1609 |
|
1610 logTestInfo("this test can only run on the buildbot build system at this " + |
|
1611 "time."); |
|
1612 return false; |
|
1613 } |
|
1614 |
|
1615 // Check to make sure the service is installed |
|
1616 let helperBin = getTestDirFile(FILE_HELPER_BIN); |
|
1617 let args = ["wait-for-service-stop", "MozillaMaintenance", "10"]; |
|
1618 let process = AUS_Cc["@mozilla.org/process/util;1"]. |
|
1619 createInstance(AUS_Ci.nsIProcess); |
|
1620 process.init(helperBin); |
|
1621 logTestInfo("checking if the service exists on this machine."); |
|
1622 process.run(true, args, args.length); |
|
1623 if (process.exitValue == 0xEE) { |
|
1624 do_throw("test registry key exists but this test can only run on systems " + |
|
1625 "with the maintenance service installed."); |
|
1626 } else { |
|
1627 logTestInfo("service exists, return value: " + process.exitValue); |
|
1628 } |
|
1629 |
|
1630 // If this is the first test in the series, then there is no reason the |
|
1631 // service should be anything but stopped, so be strict here and throw |
|
1632 // an error. |
|
1633 if (aFirstTest && process.exitValue != 0) { |
|
1634 do_throw("First test, check for service stopped state returned error " + |
|
1635 process.exitValue); |
|
1636 } |
|
1637 |
|
1638 #ifndef DISABLE_UPDATER_AUTHENTICODE_CHECK |
|
1639 if (!isBinarySigned(updaterBinPath)) { |
|
1640 logTestInfo("test registry key exists but this test can only run on " + |
|
1641 "builds with signed binaries when " + |
|
1642 "DISABLE_UPDATER_AUTHENTICODE_CHECK is not defined"); |
|
1643 do_throw("this test can only run on builds with signed binaries."); |
|
1644 } |
|
1645 #endif |
|
1646 return true; |
|
1647 } |
|
1648 |
|
1649 /** |
|
1650 * Helper function to check whether the a binary is signed. |
|
1651 * |
|
1652 * @param aBinPath The path to the file to check if it is signed. |
|
1653 * @return true if the file is signed and false if it isn't. |
|
1654 */ |
|
1655 function isBinarySigned(aBinPath) { |
|
1656 let helperBin = getTestDirFile(FILE_HELPER_BIN); |
|
1657 let args = ["check-signature", aBinPath]; |
|
1658 let process = AUS_Cc["@mozilla.org/process/util;1"]. |
|
1659 createInstance(AUS_Ci.nsIProcess); |
|
1660 process.init(helperBin); |
|
1661 process.run(true, args, args.length); |
|
1662 if (process.exitValue != 0) { |
|
1663 logTestInfo("binary is not signed. " + FILE_HELPER_BIN + " returned " + |
|
1664 process.exitValue + " for file " + aBinPath); |
|
1665 return false; |
|
1666 } |
|
1667 return true; |
|
1668 } |
|
1669 |
|
1670 /** |
|
1671 * Helper function for asynchronously setting up the application files required |
|
1672 * to launch the application for the updater tests by either copying or creating |
|
1673 * symlinks for the files. This is needed for Windows debug builds which can |
|
1674 * lock a file that is being copied so that the tests can run in parallel. After |
|
1675 * the files have been copied the setupAppFilesFinished function will be called. |
|
1676 */ |
|
1677 function setupAppFilesAsync() { |
|
1678 gTimeoutRuns++; |
|
1679 try { |
|
1680 setupAppFiles(); |
|
1681 } catch (e) { |
|
1682 if (gTimeoutRuns > MAX_TIMEOUT_RUNS) { |
|
1683 do_throw("Exceeded MAX_TIMEOUT_RUNS while trying to setup application " + |
|
1684 "files. Exception: " + e); |
|
1685 } |
|
1686 do_timeout(TEST_CHECK_TIMEOUT, setupAppFilesAsync); |
|
1687 return; |
|
1688 } |
|
1689 |
|
1690 setupAppFilesFinished(); |
|
1691 } |
|
1692 |
|
1693 /** |
|
1694 * Helper function for setting up the application files required to launch the |
|
1695 * application for the updater tests by either copying or creating symlinks for |
|
1696 * the files. |
|
1697 */ |
|
1698 function setupAppFiles() { |
|
1699 logTestInfo("start - copying or creating symlinks for application files " + |
|
1700 "for the test"); |
|
1701 |
|
1702 let srcDir = getCurrentProcessDir(); |
|
1703 let destDir = getApplyDirFile(null, true); |
|
1704 if (!destDir.exists()) { |
|
1705 try { |
|
1706 destDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); |
|
1707 } catch (e) { |
|
1708 logTestInfo("unable to create directory, Path: " + destDir.path + |
|
1709 ", Exception: " + e); |
|
1710 do_throw(e); |
|
1711 } |
|
1712 } |
|
1713 |
|
1714 // Required files for the application or the test that aren't listed in the |
|
1715 // dependentlibs.list file. |
|
1716 let fileRelPaths = [FILE_APP_BIN, FILE_UPDATER_BIN, |
|
1717 "application.ini", "dependentlibs.list"]; |
|
1718 |
|
1719 // On Linux the updater.png must also be copied |
|
1720 if (IS_UNIX && !IS_MACOSX) { |
|
1721 fileRelPaths.push("icons/updater.png"); |
|
1722 } |
|
1723 |
|
1724 // Read the dependent libs file leafnames from the dependentlibs.list file |
|
1725 // into the array. |
|
1726 let deplibsFile = srcDir.clone(); |
|
1727 deplibsFile.append("dependentlibs.list"); |
|
1728 let istream = AUS_Cc["@mozilla.org/network/file-input-stream;1"]. |
|
1729 createInstance(AUS_Ci.nsIFileInputStream); |
|
1730 istream.init(deplibsFile, 0x01, 0o444, 0); |
|
1731 istream.QueryInterface(AUS_Ci.nsILineInputStream); |
|
1732 |
|
1733 let hasMore; |
|
1734 let line = {}; |
|
1735 do { |
|
1736 hasMore = istream.readLine(line); |
|
1737 fileRelPaths.push(line.value); |
|
1738 } while(hasMore); |
|
1739 |
|
1740 istream.close(); |
|
1741 |
|
1742 fileRelPaths.forEach(function CMAF_FLN_FE(aFileRelPath) { |
|
1743 copyFileToTestAppDir(aFileRelPath); |
|
1744 }); |
|
1745 |
|
1746 logTestInfo("finish - copying or creating symlinks for application files " + |
|
1747 "for the test"); |
|
1748 } |
|
1749 |
|
1750 /** |
|
1751 * Copies the specified files from the dist/bin directory into the test's |
|
1752 * application directory. |
|
1753 * |
|
1754 * @param aFileRelPath |
|
1755 * The relative path of the file to copy. |
|
1756 */ |
|
1757 function copyFileToTestAppDir(aFileRelPath) { |
|
1758 let fileRelPath = aFileRelPath; |
|
1759 let srcFile = gGREDirOrig.clone(); |
|
1760 let pathParts = fileRelPath.split("/"); |
|
1761 for (let i = 0; i < pathParts.length; i++) { |
|
1762 if (pathParts[i]) { |
|
1763 srcFile.append(pathParts[i]); |
|
1764 } |
|
1765 } |
|
1766 |
|
1767 if (IS_MACOSX && !srcFile.exists()) { |
|
1768 logTestInfo("unable to copy file since it doesn't exist! Checking if " + |
|
1769 fileRelPath + ".app exists. Path: " + |
|
1770 srcFile.path); |
|
1771 srcFile = gGREDirOrig.clone(); |
|
1772 for (let i = 0; i < pathParts.length; i++) { |
|
1773 if (pathParts[i]) { |
|
1774 srcFile.append(pathParts[i] + (pathParts.length - 1 == i ? ".app" : "")); |
|
1775 } |
|
1776 } |
|
1777 fileRelPath = fileRelPath + ".app"; |
|
1778 } |
|
1779 |
|
1780 if (!srcFile.exists()) { |
|
1781 do_throw("Unable to copy file since it doesn't exist! Path: " + |
|
1782 srcFile.path); |
|
1783 } |
|
1784 |
|
1785 // Symlink libraries. Note that the XUL library on Mac OS X doesn't have a |
|
1786 // file extension and this will always be false on Windows. |
|
1787 let shouldSymlink = (pathParts[pathParts.length - 1] == "XUL" || |
|
1788 fileRelPath.substr(fileRelPath.length - 3) == ".so" || |
|
1789 fileRelPath.substr(fileRelPath.length - 6) == ".dylib"); |
|
1790 let destFile = getApplyDirFile(DIR_BIN_REL_PATH + fileRelPath, true); |
|
1791 if (!shouldSymlink) { |
|
1792 if (!destFile.exists()) { |
|
1793 try { |
|
1794 srcFile.copyToFollowingLinks(destFile.parent, destFile.leafName); |
|
1795 } catch (e) { |
|
1796 // Just in case it is partially copied |
|
1797 if (destFile.exists()) { |
|
1798 try { |
|
1799 destFile.remove(true); |
|
1800 } catch (e) { |
|
1801 logTestInfo("unable to remove file that failed to copy! Path: " + |
|
1802 destFile.path); |
|
1803 } |
|
1804 } |
|
1805 do_throw("Unable to copy file! Path: " + srcFile.path + |
|
1806 ", Exception: " + e); |
|
1807 } |
|
1808 } |
|
1809 } else { |
|
1810 try { |
|
1811 if (destFile.exists()) { |
|
1812 destFile.remove(false); |
|
1813 } |
|
1814 let ln = AUS_Cc["@mozilla.org/file/local;1"].createInstance(AUS_Ci.nsILocalFile); |
|
1815 ln.initWithPath("/bin/ln"); |
|
1816 let process = AUS_Cc["@mozilla.org/process/util;1"].createInstance(AUS_Ci.nsIProcess); |
|
1817 process.init(ln); |
|
1818 let args = ["-s", srcFile.path, destFile.path]; |
|
1819 process.run(true, args, args.length); |
|
1820 logTestInfo("verifying symlink. Path: " + destFile.path); |
|
1821 do_check_true(destFile.isSymlink()); |
|
1822 } catch (e) { |
|
1823 do_throw("Unable to create symlink for file! Path: " + srcFile.path + |
|
1824 ", Exception: " + e); |
|
1825 } |
|
1826 } |
|
1827 } |
|
1828 |
|
1829 /** |
|
1830 * Attempts to upgrade the maintenance service if permissions are allowed. |
|
1831 * This is useful for XP where we have permission to upgrade in case an |
|
1832 * older service installer exists. Also if the user manually installed into |
|
1833 * a unprivileged location. |
|
1834 */ |
|
1835 function attemptServiceInstall() { |
|
1836 var version = AUS_Cc["@mozilla.org/system-info;1"] |
|
1837 .getService(AUS_Ci.nsIPropertyBag2) |
|
1838 .getProperty("version"); |
|
1839 var isVistaOrHigher = (parseFloat(version) >= 6.0); |
|
1840 if (isVistaOrHigher) { |
|
1841 return; |
|
1842 } |
|
1843 |
|
1844 let binDir = getGREDir(); |
|
1845 let installerFile = binDir.clone(); |
|
1846 installerFile.append(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN); |
|
1847 if (!installerFile.exists()) { |
|
1848 do_throw(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN + " not found."); |
|
1849 } |
|
1850 let installerProcess = AUS_Cc["@mozilla.org/process/util;1"]. |
|
1851 createInstance(AUS_Ci.nsIProcess); |
|
1852 installerProcess.init(installerFile); |
|
1853 logTestInfo("starting installer process..."); |
|
1854 installerProcess.run(true, [], 0); |
|
1855 } |
|
1856 |
|
1857 /** |
|
1858 * Helper function for updater tests for launching the updater using the |
|
1859 * maintenance service to apply a mar file. |
|
1860 * |
|
1861 * @param aInitialStatus |
|
1862 * The initial value of update.status. |
|
1863 * @param aExpectedStatus |
|
1864 * The expected value of update.status when the test finishes. |
|
1865 * @param aCheckSvcLog |
|
1866 * Whether the service log should be checked (optional). |
|
1867 */ |
|
1868 function runUpdateUsingService(aInitialStatus, aExpectedStatus, aCheckSvcLog) { |
|
1869 // Check the service logs for a successful update |
|
1870 function checkServiceLogs(aOriginalContents) { |
|
1871 let contents = readServiceLogFile(); |
|
1872 logTestInfo("the contents of maintenanceservice.log:\n" + contents + "\n"); |
|
1873 do_check_neq(contents, aOriginalContents); |
|
1874 do_check_neq(contents.indexOf(LOG_SVC_SUCCESSFUL_LAUNCH), -1); |
|
1875 } |
|
1876 function readServiceLogFile() { |
|
1877 let file = AUS_Cc["@mozilla.org/file/directory_service;1"]. |
|
1878 getService(AUS_Ci.nsIProperties). |
|
1879 get("CmAppData", AUS_Ci.nsIFile); |
|
1880 file.append("Mozilla"); |
|
1881 file.append("logs"); |
|
1882 file.append("maintenanceservice.log"); |
|
1883 return readFile(file); |
|
1884 } |
|
1885 function waitServiceApps() { |
|
1886 // maintenanceservice_installer.exe is started async during updates. |
|
1887 waitForApplicationStop("maintenanceservice_installer.exe"); |
|
1888 // maintenanceservice_tmp.exe is started async from the service installer. |
|
1889 waitForApplicationStop("maintenanceservice_tmp.exe"); |
|
1890 // In case the SCM thinks the service is stopped, but process still exists. |
|
1891 waitForApplicationStop("maintenanceservice.exe"); |
|
1892 } |
|
1893 function waitForServiceStop(aFailTest) { |
|
1894 waitServiceApps(); |
|
1895 logTestInfo("waiting for service to stop if necessary..."); |
|
1896 // Use the helper bin to ensure the service is stopped. If not |
|
1897 // stopped then wait for the service to be stopped (at most 120 seconds) |
|
1898 let helperBin = getTestDirFile(FILE_HELPER_BIN); |
|
1899 let helperBinArgs = ["wait-for-service-stop", |
|
1900 "MozillaMaintenance", |
|
1901 "120"]; |
|
1902 let helperBinProcess = AUS_Cc["@mozilla.org/process/util;1"]. |
|
1903 createInstance(AUS_Ci.nsIProcess); |
|
1904 helperBinProcess.init(helperBin); |
|
1905 logTestInfo("stopping service..."); |
|
1906 helperBinProcess.run(true, helperBinArgs, helperBinArgs.length); |
|
1907 if (helperBinProcess.exitValue == 0xEE) { |
|
1908 do_throw("The service does not exist on this machine. Return value: " + |
|
1909 helperBinProcess.exitValue); |
|
1910 } else if (helperBinProcess.exitValue != 0) { |
|
1911 if (aFailTest) { |
|
1912 do_throw("maintenance service did not stop, last state: " + |
|
1913 helperBinProcess.exitValue + ". Forcing test failure."); |
|
1914 } else { |
|
1915 logTestInfo("maintenance service did not stop, last state: " + |
|
1916 helperBinProcess.exitValue + ". May cause failures."); |
|
1917 } |
|
1918 } else { |
|
1919 logTestInfo("service stopped."); |
|
1920 } |
|
1921 waitServiceApps(); |
|
1922 } |
|
1923 function waitForApplicationStop(aApplication) { |
|
1924 logTestInfo("waiting for " + aApplication + " to stop if " + |
|
1925 "necessary..."); |
|
1926 // Use the helper bin to ensure the application is stopped. |
|
1927 // If not, then wait for it to be stopped (at most 120 seconds) |
|
1928 let helperBin = getTestDirFile(FILE_HELPER_BIN); |
|
1929 let helperBinArgs = ["wait-for-application-exit", |
|
1930 aApplication, |
|
1931 "120"]; |
|
1932 let helperBinProcess = AUS_Cc["@mozilla.org/process/util;1"]. |
|
1933 createInstance(AUS_Ci.nsIProcess); |
|
1934 helperBinProcess.init(helperBin); |
|
1935 helperBinProcess.run(true, helperBinArgs, helperBinArgs.length); |
|
1936 if (helperBinProcess.exitValue != 0) { |
|
1937 do_throw(aApplication + " did not stop, last state: " + |
|
1938 helperBinProcess.exitValue + ". Forcing test failure."); |
|
1939 } |
|
1940 } |
|
1941 |
|
1942 // Make sure the service from the previous test is already stopped. |
|
1943 waitForServiceStop(true); |
|
1944 |
|
1945 // Prevent the cleanup function from begin run more than once |
|
1946 if (gRegisteredServiceCleanup === undefined) { |
|
1947 gRegisteredServiceCleanup = true; |
|
1948 |
|
1949 do_register_cleanup(function RUUS_cleanup() { |
|
1950 resetEnvironment(); |
|
1951 |
|
1952 // This will delete the app arguments log file if it exists. |
|
1953 try { |
|
1954 getAppArgsLogPath(); |
|
1955 } catch (e) { |
|
1956 logTestInfo("unable to remove file during cleanup. Exception: " + e); |
|
1957 } |
|
1958 }); |
|
1959 } |
|
1960 |
|
1961 let svcOriginalLog; |
|
1962 // Default to checking the service log if the parameter is not specified. |
|
1963 if (aCheckSvcLog === undefined || aCheckSvcLog) { |
|
1964 svcOriginalLog = readServiceLogFile(); |
|
1965 } |
|
1966 |
|
1967 let appArgsLogPath = getAppArgsLogPath(); |
|
1968 gServiceLaunchedCallbackLog = appArgsLogPath.replace(/^"|"$/g, ""); |
|
1969 |
|
1970 let updatesDir = getUpdatesPatchDir(); |
|
1971 let file = updatesDir.clone(); |
|
1972 writeStatusFile(aInitialStatus); |
|
1973 |
|
1974 // sanity check |
|
1975 do_check_eq(readStatusState(), aInitialStatus); |
|
1976 |
|
1977 writeVersionFile(DEFAULT_UPDATE_VERSION); |
|
1978 |
|
1979 gServiceLaunchedCallbackArgs = [ |
|
1980 "-no-remote", |
|
1981 "-process-updates", |
|
1982 "-dump-args", |
|
1983 appArgsLogPath |
|
1984 ]; |
|
1985 |
|
1986 if (gSwitchApp) { |
|
1987 // We want to set the env vars again |
|
1988 gShouldResetEnv = undefined; |
|
1989 } |
|
1990 |
|
1991 setEnvironment(); |
|
1992 |
|
1993 // There is a security check done by the service to make sure the updater |
|
1994 // we are executing is the same as the one in the apply-to dir. |
|
1995 // To make sure they match from tests we copy updater.exe to the apply-to dir. |
|
1996 copyFileToTestAppDir(FILE_UPDATER_BIN); |
|
1997 |
|
1998 // The service will execute maintenanceservice_installer.exe and |
|
1999 // will copy maintenanceservice.exe out of the same directory from |
|
2000 // the installation directory. So we need to make sure both of those |
|
2001 // bins always exist in the installation directory. |
|
2002 copyFileToTestAppDir(FILE_MAINTENANCE_SERVICE_BIN); |
|
2003 copyFileToTestAppDir(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN); |
|
2004 |
|
2005 let launchBin = getLaunchBin(); |
|
2006 let args = getProcessArgs(["-dump-args", appArgsLogPath]); |
|
2007 |
|
2008 let process = AUS_Cc["@mozilla.org/process/util;1"]. |
|
2009 createInstance(AUS_Ci.nsIProcess); |
|
2010 process.init(launchBin); |
|
2011 logTestInfo("launching " + launchBin.path + " " + args.join(" ")); |
|
2012 // Firefox does not wait for the service command to finish, but |
|
2013 // we still launch the process sync to avoid intermittent failures with |
|
2014 // the log file not being written out yet. |
|
2015 // We will rely on watching the update.status file and waiting for the service |
|
2016 // to stop to know the service command is done. |
|
2017 process.run(true, args, args.length); |
|
2018 |
|
2019 resetEnvironment(); |
|
2020 |
|
2021 function timerCallback(aTimer) { |
|
2022 // Wait for the expected status |
|
2023 let status = readStatusState(); |
|
2024 // status will probably always be equal to STATE_APPLYING but there is a |
|
2025 // race condition where it would be possible on slower machines where status |
|
2026 // could be equal to STATE_PENDING_SVC. |
|
2027 if (status == STATE_APPLYING || |
|
2028 status == STATE_PENDING_SVC) { |
|
2029 logTestInfo("still waiting to see the " + aExpectedStatus + |
|
2030 " status, got " + status + " for now..."); |
|
2031 return; |
|
2032 } |
|
2033 |
|
2034 // Make sure all of the logs are written out. |
|
2035 waitForServiceStop(false); |
|
2036 |
|
2037 aTimer.cancel(); |
|
2038 aTimer = null; |
|
2039 |
|
2040 if (status != aExpectedStatus) { |
|
2041 logTestInfo("update status is not the expected status! Got: " + status + |
|
2042 ", Expected: " + aExpectedStatus); |
|
2043 logTestInfo("update.status contents: " + readStatusFile()); |
|
2044 let updateLog = getUpdatesPatchDir(); |
|
2045 updateLog.append(FILE_UPDATE_LOG); |
|
2046 logTestInfo("contents of " + updateLog.path + ":\n" + |
|
2047 readFileBytes(updateLog).replace(/\r\n/g, "\n")); |
|
2048 } |
|
2049 logTestInfo("testing update status against expected status"); |
|
2050 do_check_eq(status, aExpectedStatus); |
|
2051 |
|
2052 if (aCheckSvcLog) { |
|
2053 checkServiceLogs(svcOriginalLog); |
|
2054 } |
|
2055 |
|
2056 checkUpdateFinished(); |
|
2057 } |
|
2058 |
|
2059 let timer = AUS_Cc["@mozilla.org/timer;1"].createInstance(AUS_Ci.nsITimer); |
|
2060 timer.initWithCallback(timerCallback, 1000, timer.TYPE_REPEATING_SLACK); |
|
2061 } |
|
2062 |
|
2063 /** |
|
2064 * Gets the platform specific shell binary that is launched using nsIProcess and |
|
2065 * in turn launches a binary used for the test (e.g. application, updater, |
|
2066 * etc.). A shell is used so debug console output can be redirected to a file so |
|
2067 * it doesn't end up in the test log. |
|
2068 * |
|
2069 * @return nsIFile for the shell binary to launch using nsIProcess. |
|
2070 * @throws if the shell binary doesn't exist. |
|
2071 */ |
|
2072 function getLaunchBin() { |
|
2073 let launchBin; |
|
2074 if (IS_WIN) { |
|
2075 launchBin = Services.dirsvc.get("WinD", AUS_Ci.nsIFile); |
|
2076 launchBin.append("System32"); |
|
2077 launchBin.append("cmd.exe"); |
|
2078 } else { |
|
2079 launchBin = AUS_Cc["@mozilla.org/file/local;1"]. |
|
2080 createInstance(AUS_Ci.nsILocalFile); |
|
2081 launchBin.initWithPath("/bin/sh"); |
|
2082 } |
|
2083 |
|
2084 if (!launchBin.exists()) |
|
2085 do_throw(launchBin.path + " must exist to run this test!"); |
|
2086 |
|
2087 return launchBin; |
|
2088 } |
|
2089 |
|
2090 /** |
|
2091 * Helper function that waits until the helper has completed its operations and |
|
2092 * is in a sleep state before performing an update by calling doUpdate. |
|
2093 */ |
|
2094 function waitForHelperSleep() { |
|
2095 gTimeoutRuns++; |
|
2096 // Give the lock file process time to lock the file before updating otherwise |
|
2097 // this test can fail intermittently on Windows debug builds. |
|
2098 let output = getApplyDirFile("a/b/output", true); |
|
2099 if (readFile(output) != "sleeping\n") { |
|
2100 if (gTimeoutRuns > MAX_TIMEOUT_RUNS) { |
|
2101 do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the helper to " + |
|
2102 "finish its operation. Path: " + output.path); |
|
2103 } |
|
2104 do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep); |
|
2105 return; |
|
2106 } |
|
2107 try { |
|
2108 output.remove(false); |
|
2109 } |
|
2110 catch (e) { |
|
2111 if (gTimeoutRuns > MAX_TIMEOUT_RUNS) { |
|
2112 do_throw("Exceeded MAX_TIMEOUT_RUNS while waiting for the helper " + |
|
2113 "message file to no longer be in use. Path: " + output.path); |
|
2114 } |
|
2115 logTestInfo("failed to remove file. Path: " + output.path); |
|
2116 do_timeout(TEST_HELPER_TIMEOUT, waitForHelperSleep); |
|
2117 return; |
|
2118 } |
|
2119 doUpdate(); |
|
2120 } |
|
2121 |
|
2122 /** |
|
2123 * Helper function that waits until the helper has finished its operations |
|
2124 * before calling waitForHelperFinishFileUnlock to verify that the helper's |
|
2125 * input and output directories are no longer in use. |
|
2126 */ |
|
2127 function waitForHelperFinished() { |
|
2128 // Give the lock file process time to lock the file before updating otherwise |
|
2129 // this test can fail intermittently on Windows debug builds. |
|
2130 let output = getApplyDirFile("a/b/output", true); |
|
2131 if (readFile(output) != "finished\n") { |
|
2132 do_timeout(TEST_HELPER_TIMEOUT, waitForHelperFinished); |
|
2133 return; |
|
2134 } |
|
2135 // Give the lock file process time to unlock the file before deleting the |
|
2136 // input and output files. |
|
2137 waitForHelperFinishFileUnlock(); |
|
2138 } |
|
2139 |
|
2140 /** |
|
2141 * Helper function that waits until the helper's input and output files are no |
|
2142 * longer in use before calling checkUpdate. |
|
2143 */ |
|
2144 function waitForHelperFinishFileUnlock() { |
|
2145 try { |
|
2146 let output = getApplyDirFile("a/b/output", true); |
|
2147 if (output.exists()) { |
|
2148 output.remove(false); |
|
2149 } |
|
2150 let input = getApplyDirFile("a/b/input", true); |
|
2151 if (input.exists()) { |
|
2152 input.remove(false); |
|
2153 } |
|
2154 } catch (e) { |
|
2155 // Give the lock file process time to unlock the file before deleting the |
|
2156 // input and output files. |
|
2157 do_timeout(TEST_HELPER_TIMEOUT, waitForHelperFinishFileUnlock); |
|
2158 return; |
|
2159 } |
|
2160 checkUpdate(); |
|
2161 } |
|
2162 |
|
2163 /** |
|
2164 * Helper function to tell the helper to finish and exit its sleep state. |
|
2165 */ |
|
2166 function setupHelperFinish() { |
|
2167 let input = getApplyDirFile("a/b/input", true); |
|
2168 writeFile(input, "finish\n"); |
|
2169 waitForHelperFinished(); |
|
2170 } |
|
2171 |
|
2172 /** |
|
2173 * Helper function for updater binary tests that creates the files and |
|
2174 * directories used by the test. |
|
2175 * |
|
2176 * @param aMarFile |
|
2177 * The mar file for the update test. |
|
2178 */ |
|
2179 function setupUpdaterTest(aMarFile, aUpdatedDirExists, aToBeDeletedDirExists) { |
|
2180 let updatesPatchDir = getUpdatesPatchDir(); |
|
2181 if (!updatesPatchDir.exists()) { |
|
2182 updatesPatchDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); |
|
2183 } |
|
2184 // Copy the mar that will be applied |
|
2185 let mar = getTestDirFile(aMarFile); |
|
2186 mar.copyToFollowingLinks(updatesPatchDir, FILE_UPDATE_ARCHIVE); |
|
2187 |
|
2188 createUpdateSettingsINI(); |
|
2189 |
|
2190 let applyToDir = getApplyDirFile(null, true); |
|
2191 gTestFiles.forEach(function SUT_TF_FE(aTestFile) { |
|
2192 if (aTestFile.originalFile || aTestFile.originalContents) { |
|
2193 let testDir = getApplyDirFile(aTestFile.relPathDir, true); |
|
2194 if (!testDir.exists()) |
|
2195 testDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); |
|
2196 |
|
2197 let testFile; |
|
2198 if (aTestFile.originalFile) { |
|
2199 testFile = getTestDirFile(aTestFile.originalFile); |
|
2200 testFile.copyToFollowingLinks(testDir, aTestFile.fileName); |
|
2201 testFile = getApplyDirFile(aTestFile.relPathDir + aTestFile.fileName); |
|
2202 } else { |
|
2203 testFile = getApplyDirFile(aTestFile.relPathDir + aTestFile.fileName, |
|
2204 true); |
|
2205 writeFile(testFile, aTestFile.originalContents); |
|
2206 } |
|
2207 |
|
2208 // Skip these tests on Windows and OS/2 since their |
|
2209 // implementaions of chmod doesn't really set permissions. |
|
2210 if (!IS_WIN && aTestFile.originalPerms) { |
|
2211 testFile.permissions = aTestFile.originalPerms; |
|
2212 // Store the actual permissions on the file for reference later after |
|
2213 // setting the permissions. |
|
2214 if (!aTestFile.comparePerms) { |
|
2215 aTestFile.comparePerms = testFile.permissions; |
|
2216 } |
|
2217 } |
|
2218 } |
|
2219 }); |
|
2220 |
|
2221 let helperBin = getTestDirFile(FILE_HELPER_BIN); |
|
2222 let afterApplyBinDir = getApplyDirFile("a/b/", true); |
|
2223 helperBin.copyToFollowingLinks(afterApplyBinDir, gCallbackBinFile); |
|
2224 |
|
2225 // Add the test directory that will be updated for a successful update or left |
|
2226 // in the initial state for a failed update. |
|
2227 gTestDirs.forEach(function SUT_TD_FE(aTestDir) { |
|
2228 let testDir = getApplyDirFile(aTestDir.relPathDir, true); |
|
2229 if (!testDir.exists()) { |
|
2230 testDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); |
|
2231 } |
|
2232 |
|
2233 if (aTestDir.files) { |
|
2234 aTestDir.files.forEach(function SUT_TD_F_FE(aTestFile) { |
|
2235 let testFile = getApplyDirFile(aTestDir.relPathDir + aTestFile, true); |
|
2236 if (!testFile.exists()) { |
|
2237 testFile.create(AUS_Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE); |
|
2238 } |
|
2239 }); |
|
2240 } |
|
2241 |
|
2242 if (aTestDir.subDirs) { |
|
2243 aTestDir.subDirs.forEach(function SUT_TD_SD_FE(aSubDir) { |
|
2244 let testSubDir = getApplyDirFile(aTestDir.relPathDir + aSubDir, true); |
|
2245 if (!testSubDir.exists()) { |
|
2246 testSubDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); |
|
2247 } |
|
2248 |
|
2249 if (aTestDir.subDirFiles) { |
|
2250 aTestDir.subDirFiles.forEach(function SUT_TD_SDF_FE(aTestFile) { |
|
2251 let testFile = getApplyDirFile(aTestDir.relPathDir + aSubDir + aTestFile, true); |
|
2252 if (!testFile.exists()) { |
|
2253 testFile.create(AUS_Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE); |
|
2254 } |
|
2255 }); |
|
2256 } |
|
2257 }); |
|
2258 } |
|
2259 }); |
|
2260 |
|
2261 gTestExtraDirs[0].dirExists = aUpdatedDirExists; |
|
2262 gTestExtraDirs[1].dirExists = IS_WIN ? aToBeDeletedDirExists : false; |
|
2263 } |
|
2264 |
|
2265 /** |
|
2266 * Helper function for updater binary tests that creates the update-settings.ini |
|
2267 * file. |
|
2268 */ |
|
2269 function createUpdateSettingsINI() { |
|
2270 updateSettingsIni = getApplyDirFile(null, true); |
|
2271 if (IS_MACOSX) { |
|
2272 updateSettingsIni.append("Contents"); |
|
2273 updateSettingsIni.append("MacOS"); |
|
2274 } |
|
2275 updateSettingsIni.append(FILE_UPDATE_SETTINGS_INI); |
|
2276 writeFile(updateSettingsIni, UPDATE_SETTINGS_CONTENTS); |
|
2277 } |
|
2278 |
|
2279 /** |
|
2280 * Helper function for updater binary tests for verifying the contents of the |
|
2281 * update log after a successful update. |
|
2282 * |
|
2283 * @param aCompareLogFile |
|
2284 * The log file to compare the update log with. |
|
2285 */ |
|
2286 function checkUpdateLogContents(aCompareLogFile) { |
|
2287 let updateLog = getUpdatesPatchDir(); |
|
2288 updateLog.append(FILE_UPDATE_LOG); |
|
2289 let updateLogContents = readFileBytes(updateLog); |
|
2290 |
|
2291 // The channel-prefs.js is defined in gTestFilesCommon which will always be |
|
2292 // located to the end of gTestFiles. |
|
2293 if (gTestFiles.length > 1 && |
|
2294 gTestFiles[gTestFiles.length - 1].fileName == "channel-prefs.js" && |
|
2295 !gTestFiles[gTestFiles.length - 1].originalContents) { |
|
2296 updateLogContents = updateLogContents.replace(/.* a\/b\/defaults\/.*/g, ""); |
|
2297 } |
|
2298 if (gTestFiles.length > 2 && |
|
2299 gTestFiles[gTestFiles.length - 2].fileName == FILE_UPDATE_SETTINGS_INI && |
|
2300 !gTestFiles[gTestFiles.length - 2].originalContents) { |
|
2301 updateLogContents = updateLogContents.replace(/.* a\/b\/update-settings.ini.*/g, ""); |
|
2302 } |
|
2303 if (gStageUpdate) { |
|
2304 // Skip the staged update messages |
|
2305 updateLogContents = updateLogContents.replace(/Performing a staged update/, ""); |
|
2306 } else if (gSwitchApp) { |
|
2307 // Skip the switch app request messages |
|
2308 updateLogContents = updateLogContents.replace(/Performing a staged update/, ""); |
|
2309 updateLogContents = updateLogContents.replace(/Performing a replace request/, ""); |
|
2310 } |
|
2311 // Skip the source/destination lines since they contain absolute paths. |
|
2312 updateLogContents = updateLogContents.replace(/SOURCE DIRECTORY.*/g, ""); |
|
2313 updateLogContents = updateLogContents.replace(/DESTINATION DIRECTORY.*/g, ""); |
|
2314 // Skip lines that log failed attempts to open the callback executable. |
|
2315 updateLogContents = updateLogContents.replace(/NS_main: callback app file .*/g, ""); |
|
2316 if (gSwitchApp) { |
|
2317 // Remove the lines which contain absolute paths |
|
2318 updateLogContents = updateLogContents.replace(/^Begin moving.*$/mg, ""); |
|
2319 if (IS_MACOSX) { |
|
2320 // Remove the entire section about moving the precomplete file as it contains |
|
2321 // absolute paths. |
|
2322 updateLogContents = updateLogContents.replace(/\n/g, "%%%EOL%%%"); |
|
2323 updateLogContents = updateLogContents.replace(/Moving the precomplete file.*Finished moving the precomplete file/, ""); |
|
2324 updateLogContents = updateLogContents.replace(/%%%EOL%%%/g, "\n"); |
|
2325 } |
|
2326 } |
|
2327 updateLogContents = updateLogContents.replace(/\r/g, ""); |
|
2328 // Replace error codes since they are different on each platform. |
|
2329 updateLogContents = updateLogContents.replace(/, err:.*\n/g, "\n"); |
|
2330 // Replace to make the log parsing happy. |
|
2331 updateLogContents = updateLogContents.replace(/non-fatal error /g, ""); |
|
2332 // The FindFile results when enumerating the filesystem on Windows is not |
|
2333 // determistic so the results matching the following need to be ignored. |
|
2334 updateLogContents = updateLogContents.replace(/.* a\/b\/7\/7text.*\n/g, ""); |
|
2335 // Remove consecutive newlines |
|
2336 updateLogContents = updateLogContents.replace(/\n+/g, "\n"); |
|
2337 // Remove leading and trailing newlines |
|
2338 updateLogContents = updateLogContents.replace(/^\n|\n$/g, ""); |
|
2339 // The update log when running the service tests sometimes starts with data |
|
2340 // from the previous launch of the updater. |
|
2341 updateLogContents = updateLogContents.replace(/^calling QuitProgressUI\n[^\n]*\nUPDATE TYPE/g, "UPDATE TYPE"); |
|
2342 |
|
2343 let compareLogContents = ""; |
|
2344 if (aCompareLogFile) { |
|
2345 compareLogContents = readFileBytes(getTestDirFile(aCompareLogFile)); |
|
2346 } |
|
2347 if (gSwitchApp) { |
|
2348 compareLogContents += LOG_SWITCH_SUCCESS; |
|
2349 } |
|
2350 // The channel-prefs.js is defined in gTestFilesCommon which will always be |
|
2351 // located to the end of gTestFiles. |
|
2352 if (gTestFiles.length > 1 && |
|
2353 gTestFiles[gTestFiles.length - 1].fileName == "channel-prefs.js" && |
|
2354 !gTestFiles[gTestFiles.length - 1].originalContents) { |
|
2355 compareLogContents = compareLogContents.replace(/.* a\/b\/defaults\/.*/g, ""); |
|
2356 } |
|
2357 if (gTestFiles.length > 2 && |
|
2358 gTestFiles[gTestFiles.length - 2].fileName == FILE_UPDATE_SETTINGS_INI && |
|
2359 !gTestFiles[gTestFiles.length - 2].originalContents) { |
|
2360 compareLogContents = compareLogContents.replace(/.* a\/b\/update-settings.ini.*/g, ""); |
|
2361 } |
|
2362 // Remove leading and trailing newlines |
|
2363 compareLogContents = compareLogContents.replace(/\n+/g, "\n"); |
|
2364 // Remove leading and trailing newlines |
|
2365 compareLogContents = compareLogContents.replace(/^\n|\n$/g, ""); |
|
2366 |
|
2367 // Don't write the contents of the file to the log to reduce log spam |
|
2368 // unless there is a failure. |
|
2369 if (compareLogContents == updateLogContents) { |
|
2370 logTestInfo("log contents are correct"); |
|
2371 do_check_true(true); |
|
2372 } else { |
|
2373 logTestInfo("log contents are not correct"); |
|
2374 do_check_eq(compareLogContents, updateLogContents); |
|
2375 } |
|
2376 } |
|
2377 |
|
2378 /** |
|
2379 * Helper function to check if the update log contains a string. |
|
2380 * |
|
2381 * @param aCheckString |
|
2382 * The string to check if the update log contains. |
|
2383 */ |
|
2384 function checkUpdateLogContains(aCheckString) { |
|
2385 let updateLog = getUpdatesPatchDir(); |
|
2386 updateLog.append(FILE_UPDATE_LOG); |
|
2387 let updateLogContents = readFileBytes(updateLog); |
|
2388 if (updateLogContents.indexOf(aCheckString) != -1) { |
|
2389 logTestInfo("log file does contain: " + aCheckString); |
|
2390 do_check_true(true); |
|
2391 } else { |
|
2392 logTestInfo("log file does not contain: " + aCheckString); |
|
2393 logTestInfo("log file contents:\n" + updateLogContents); |
|
2394 do_check_true(false); |
|
2395 } |
|
2396 } |
|
2397 |
|
2398 /** |
|
2399 * Helper function for updater binary tests for verifying the state of files and |
|
2400 * directories after a successful update. |
|
2401 */ |
|
2402 function checkFilesAfterUpdateSuccess() { |
|
2403 logTestInfo("testing contents of files after a successful update"); |
|
2404 gTestFiles.forEach(function CFAUS_TF_FE(aTestFile) { |
|
2405 let testFile = getTargetDirFile(aTestFile.relPathDir + aTestFile.fileName, |
|
2406 true); |
|
2407 logTestInfo("testing file: " + testFile.path); |
|
2408 if (aTestFile.compareFile || aTestFile.compareContents) { |
|
2409 do_check_true(testFile.exists()); |
|
2410 |
|
2411 // Skip these tests on Windows and OS/2 since their |
|
2412 // implementaions of chmod doesn't really set permissions. |
|
2413 if (!IS_WIN && aTestFile.comparePerms) { |
|
2414 // Check if the permssions as set in the complete mar file are correct. |
|
2415 let logPerms = "testing file permissions - "; |
|
2416 if (aTestFile.originalPerms) { |
|
2417 logPerms += "original permissions: " + |
|
2418 aTestFile.originalPerms.toString(8) + ", "; |
|
2419 } |
|
2420 logPerms += "compare permissions : " + |
|
2421 aTestFile.comparePerms.toString(8) + ", "; |
|
2422 logPerms += "updated permissions : " + testFile.permissions.toString(8); |
|
2423 logTestInfo(logPerms); |
|
2424 do_check_eq(testFile.permissions & 0xfff, aTestFile.comparePerms & 0xfff); |
|
2425 } |
|
2426 |
|
2427 let fileContents1 = readFileBytes(testFile); |
|
2428 let fileContents2 = aTestFile.compareFile ? |
|
2429 readFileBytes(getTestDirFile(aTestFile.compareFile)) : |
|
2430 aTestFile.compareContents; |
|
2431 // Don't write the contents of the file to the log to reduce log spam |
|
2432 // unless there is a failure. |
|
2433 if (fileContents1 == fileContents2) { |
|
2434 logTestInfo("file contents are correct"); |
|
2435 do_check_true(true); |
|
2436 } else { |
|
2437 logTestInfo("file contents are not correct"); |
|
2438 do_check_eq(fileContents1, fileContents2); |
|
2439 } |
|
2440 } else { |
|
2441 do_check_false(testFile.exists()); |
|
2442 } |
|
2443 }); |
|
2444 |
|
2445 logTestInfo("testing operations specified in removed-files were performed " + |
|
2446 "after a successful update"); |
|
2447 gTestDirs.forEach(function CFAUS_TD_FE(aTestDir) { |
|
2448 let testDir = getTargetDirFile(aTestDir.relPathDir, true); |
|
2449 logTestInfo("testing directory: " + testDir.path); |
|
2450 if (aTestDir.dirRemoved) { |
|
2451 do_check_false(testDir.exists()); |
|
2452 } else { |
|
2453 do_check_true(testDir.exists()); |
|
2454 |
|
2455 if (aTestDir.files) { |
|
2456 aTestDir.files.forEach(function CFAUS_TD_F_FE(aTestFile) { |
|
2457 let testFile = getTargetDirFile(aTestDir.relPathDir + aTestFile, true); |
|
2458 logTestInfo("testing directory file: " + testFile.path); |
|
2459 if (aTestDir.filesRemoved) { |
|
2460 do_check_false(testFile.exists()); |
|
2461 } else { |
|
2462 do_check_true(testFile.exists()); |
|
2463 } |
|
2464 }); |
|
2465 } |
|
2466 |
|
2467 if (aTestDir.subDirs) { |
|
2468 aTestDir.subDirs.forEach(function CFAUS_TD_SD_FE(aSubDir) { |
|
2469 let testSubDir = getTargetDirFile(aTestDir.relPathDir + aSubDir, true); |
|
2470 logTestInfo("testing sub-directory: " + testSubDir.path); |
|
2471 do_check_true(testSubDir.exists()); |
|
2472 if (aTestDir.subDirFiles) { |
|
2473 aTestDir.subDirFiles.forEach(function CFAUS_TD_SDF_FE(aTestFile) { |
|
2474 let testFile = getTargetDirFile(aTestDir.relPathDir + aSubDir + aTestFile, true); |
|
2475 logTestInfo("testing sub-directory file: " + testFile.path); |
|
2476 do_check_true(testFile.exists()); |
|
2477 }); |
|
2478 } |
|
2479 }); |
|
2480 } |
|
2481 } |
|
2482 }); |
|
2483 |
|
2484 checkFilesAfterUpdateCommon(); |
|
2485 } |
|
2486 |
|
2487 /** |
|
2488 * Helper function for updater binary tests for verifying the state of files and |
|
2489 * directories after a failed update. |
|
2490 * |
|
2491 * @param aGetDirectory: the function used to get the files in the target directory. |
|
2492 * Pass getApplyDirFile if you want to test the case of a failed switch request. |
|
2493 */ |
|
2494 function checkFilesAfterUpdateFailure(aGetDirectory) { |
|
2495 let getdir = aGetDirectory || getTargetDirFile; |
|
2496 logTestInfo("testing contents of files after a failed update"); |
|
2497 gTestFiles.forEach(function CFAUF_TF_FE(aTestFile) { |
|
2498 let testFile = getdir(aTestFile.relPathDir + aTestFile.fileName, true); |
|
2499 logTestInfo("testing file: " + testFile.path); |
|
2500 if (aTestFile.compareFile || aTestFile.compareContents) { |
|
2501 do_check_true(testFile.exists()); |
|
2502 |
|
2503 // Skip these tests on Windows and OS/2 since their |
|
2504 // implementaions of chmod doesn't really set permissions. |
|
2505 if (!IS_WIN && aTestFile.comparePerms) { |
|
2506 // Check the original permssions are retained on the file. |
|
2507 let logPerms = "testing file permissions - "; |
|
2508 if (aTestFile.originalPerms) { |
|
2509 logPerms += "original permissions: " + |
|
2510 aTestFile.originalPerms.toString(8) + ", "; |
|
2511 } |
|
2512 logPerms += "compare permissions : " + |
|
2513 aTestFile.comparePerms.toString(8) + ", "; |
|
2514 logPerms += "updated permissions : " + testFile.permissions.toString(8); |
|
2515 logTestInfo(logPerms); |
|
2516 do_check_eq(testFile.permissions & 0xfff, aTestFile.comparePerms & 0xfff); |
|
2517 } |
|
2518 |
|
2519 let fileContents1 = readFileBytes(testFile); |
|
2520 let fileContents2 = aTestFile.compareFile ? |
|
2521 readFileBytes(getTestDirFile(aTestFile.compareFile)) : |
|
2522 aTestFile.compareContents; |
|
2523 // Don't write the contents of the file to the log to reduce log spam |
|
2524 // unless there is a failure. |
|
2525 if (fileContents1 == fileContents2) { |
|
2526 logTestInfo("file contents are correct"); |
|
2527 do_check_true(true); |
|
2528 } else { |
|
2529 logTestInfo("file contents are not correct"); |
|
2530 do_check_eq(fileContents1, fileContents2); |
|
2531 } |
|
2532 } else { |
|
2533 do_check_false(testFile.exists()); |
|
2534 } |
|
2535 }); |
|
2536 |
|
2537 logTestInfo("testing operations specified in removed-files were not " + |
|
2538 "performed after a failed update"); |
|
2539 gTestDirs.forEach(function CFAUF_TD_FE(aTestDir) { |
|
2540 let testDir = getdir(aTestDir.relPathDir, true); |
|
2541 logTestInfo("testing directory: " + testDir.path); |
|
2542 do_check_true(testDir.exists()); |
|
2543 |
|
2544 if (aTestDir.files) { |
|
2545 aTestDir.files.forEach(function CFAUS_TD_F_FE(aTestFile) { |
|
2546 let testFile = getdir(aTestDir.relPathDir + aTestFile, true); |
|
2547 logTestInfo("testing directory file: " + testFile.path); |
|
2548 do_check_true(testFile.exists()); |
|
2549 }); |
|
2550 } |
|
2551 |
|
2552 if (aTestDir.subDirs) { |
|
2553 aTestDir.subDirs.forEach(function CFAUS_TD_SD_FE(aSubDir) { |
|
2554 let testSubDir = getdir(aTestDir.relPathDir + aSubDir, true); |
|
2555 logTestInfo("testing sub-directory: " + testSubDir.path); |
|
2556 do_check_true(testSubDir.exists()); |
|
2557 if (aTestDir.subDirFiles) { |
|
2558 aTestDir.subDirFiles.forEach(function CFAUS_TD_SDF_FE(aTestFile) { |
|
2559 let testFile = getdir(aTestDir.relPathDir + aSubDir + aTestFile, |
|
2560 true); |
|
2561 logTestInfo("testing sub-directory file: " + testFile.path); |
|
2562 do_check_true(testFile.exists()); |
|
2563 }); |
|
2564 } |
|
2565 }); |
|
2566 } |
|
2567 }); |
|
2568 |
|
2569 checkFilesAfterUpdateCommon(); |
|
2570 } |
|
2571 |
|
2572 /** |
|
2573 * Helper function for updater binary tests for verifying the state of common |
|
2574 * files and directories after a successful or failed update. |
|
2575 */ |
|
2576 function checkFilesAfterUpdateCommon() { |
|
2577 logTestInfo("testing extra directories"); |
|
2578 gTestExtraDirs.forEach(function CFAUC_TED_FE(aTestExtraDir) { |
|
2579 let testDir = getTargetDirFile(aTestExtraDir.relPathDir, true); |
|
2580 logTestInfo("testing directory: " + testDir.path); |
|
2581 if (aTestExtraDir.dirExists) { |
|
2582 do_check_true(testDir.exists()); |
|
2583 } else { |
|
2584 do_check_false(testDir.exists()); |
|
2585 } |
|
2586 }); |
|
2587 |
|
2588 logTestInfo("testing updating directory doesn't exist in the application " + |
|
2589 "directory"); |
|
2590 let updatingDir = getTargetDirFile("updating", true); |
|
2591 do_check_false(updatingDir.exists()); |
|
2592 |
|
2593 if (gStageUpdate) { |
|
2594 logTestInfo("testing updating directory doesn't exist in the updated " + |
|
2595 "directory"); |
|
2596 updatingDir = getApplyDirFile("updating", true); |
|
2597 do_check_false(updatingDir.exists()); |
|
2598 |
|
2599 // This should never exist since the update was applied to the updated |
|
2600 // directory and the files should never be in use. |
|
2601 logTestInfo("testing tobedeleted directory doesn't exist in the updated " + |
|
2602 "directory"); |
|
2603 let toBeDeletedDir = getApplyDirFile(DIR_TOBEDELETED, true); |
|
2604 do_check_false(toBeDeletedDir.exists()); |
|
2605 } |
|
2606 |
|
2607 logTestInfo("testing patch files should not be left behind"); |
|
2608 let updatesDir = getUpdatesPatchDir(); |
|
2609 let entries = updatesDir.QueryInterface(AUS_Ci.nsIFile).directoryEntries; |
|
2610 while (entries.hasMoreElements()) { |
|
2611 let entry = entries.getNext().QueryInterface(AUS_Ci.nsIFile); |
|
2612 do_check_neq(getFileExtension(entry), "patch"); |
|
2613 } |
|
2614 |
|
2615 logTestInfo("testing backup files should not be left behind"); |
|
2616 let applyToDir = getTargetDirFile(null, true); |
|
2617 checkFilesInDirRecursive(applyToDir, checkForBackupFiles); |
|
2618 } |
|
2619 |
|
2620 /** |
|
2621 * Helper function for updater binary tests for verifying the contents of the |
|
2622 * updater callback application log which should contain the arguments passed to |
|
2623 * the callback application. |
|
2624 */ |
|
2625 function checkCallbackAppLog() { |
|
2626 let appLaunchLog = getApplyDirFile("a/b/" + gCallbackArgs[1], true); |
|
2627 if (!appLaunchLog.exists()) { |
|
2628 do_timeout(TEST_HELPER_TIMEOUT, checkCallbackAppLog); |
|
2629 return; |
|
2630 } |
|
2631 |
|
2632 let expectedLogContents = gCallbackArgs.join("\n") + "\n"; |
|
2633 let logContents = readFile(appLaunchLog); |
|
2634 // It is possible for the log file contents check to occur before the log file |
|
2635 // contents are completely written so wait until the contents are the expected |
|
2636 // value. If the contents are never the expected value then the test will |
|
2637 // fail by timing out. |
|
2638 if (logContents != expectedLogContents) { |
|
2639 do_timeout(TEST_HELPER_TIMEOUT, checkCallbackAppLog); |
|
2640 return; |
|
2641 } |
|
2642 |
|
2643 if (logContents == expectedLogContents) { |
|
2644 logTestInfo("callback log file contents are correct"); |
|
2645 do_check_true(true); |
|
2646 } else { |
|
2647 logTestInfo("callback log file contents are not correct"); |
|
2648 do_check_eq(logContents, expectedLogContents); |
|
2649 } |
|
2650 |
|
2651 waitForFilesInUse(); |
|
2652 } |
|
2653 |
|
2654 /** |
|
2655 * Helper function for updater service tests for verifying the contents of the |
|
2656 * updater callback application log which should contain the arguments passed to |
|
2657 * the callback application. |
|
2658 */ |
|
2659 function checkCallbackServiceLog() { |
|
2660 do_check_neq(gServiceLaunchedCallbackLog, null); |
|
2661 |
|
2662 let expectedLogContents = gServiceLaunchedCallbackArgs.join("\n") + "\n"; |
|
2663 let logFile = AUS_Cc["@mozilla.org/file/local;1"].createInstance(AUS_Ci.nsILocalFile); |
|
2664 logFile.initWithPath(gServiceLaunchedCallbackLog); |
|
2665 let logContents = readFile(logFile); |
|
2666 // It is possible for the log file contents check to occur before the log file |
|
2667 // contents are completely written so wait until the contents are the expected |
|
2668 // value. If the contents are never the expected value then the test will |
|
2669 // fail by timing out. |
|
2670 if (logContents != expectedLogContents) { |
|
2671 logTestInfo("callback service log not expected value, waiting longer"); |
|
2672 do_timeout(TEST_HELPER_TIMEOUT, checkCallbackServiceLog); |
|
2673 return; |
|
2674 } |
|
2675 |
|
2676 logTestInfo("testing that the callback application successfully launched " + |
|
2677 "and the expected command line arguments were passed to it"); |
|
2678 do_check_eq(logContents, expectedLogContents); |
|
2679 |
|
2680 waitForFilesInUse(); |
|
2681 } |
|
2682 |
|
2683 // Waits until files that are in use that break tests are no longer in use and |
|
2684 // then calls do_test_finished. |
|
2685 function waitForFilesInUse() { |
|
2686 if (IS_WIN) { |
|
2687 let appBin = getApplyDirFile(FILE_APP_BIN, true); |
|
2688 let maintSvcInstaller = getApplyDirFile(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN, true); |
|
2689 let helper = getApplyDirFile("uninstall/helper.exe", true); |
|
2690 let updater = getUpdatesPatchDir(); |
|
2691 updater.append(FILE_UPDATER_BIN); |
|
2692 |
|
2693 let files = [appBin, updater, maintSvcInstaller, helper]; |
|
2694 |
|
2695 for (var i = 0; i < files.length; ++i) { |
|
2696 let file = files[i]; |
|
2697 let fileBak = file.parent.clone(); |
|
2698 if (file.exists()) { |
|
2699 fileBak.append(file.leafName + ".bak"); |
|
2700 try { |
|
2701 if (fileBak.exists()) { |
|
2702 fileBak.remove(false); |
|
2703 } |
|
2704 file.copyTo(fileBak.parent, fileBak.leafName); |
|
2705 file.remove(false); |
|
2706 fileBak.moveTo(file.parent, file.leafName); |
|
2707 logTestInfo("file is not in use. Path: " + file.path); |
|
2708 } catch (e) { |
|
2709 logTestInfo("file in use, will try again after " + TEST_CHECK_TIMEOUT + |
|
2710 " ms, Path: " + file.path + ", Exception: " + e); |
|
2711 try { |
|
2712 if (fileBak.exists()) { |
|
2713 fileBak.remove(false); |
|
2714 } |
|
2715 } catch (e) { |
|
2716 logTestInfo("unable to remove file, this should never happen! " + |
|
2717 "Path: " + fileBak.path + ", Exception: " + e); |
|
2718 } |
|
2719 do_timeout(TEST_CHECK_TIMEOUT, waitForFilesInUse); |
|
2720 return; |
|
2721 } |
|
2722 } |
|
2723 } |
|
2724 } |
|
2725 |
|
2726 logTestInfo("calling doTestFinish"); |
|
2727 doTestFinish(); |
|
2728 } |
|
2729 |
|
2730 /** |
|
2731 * Helper function for updater binary tests for verifying there are no update |
|
2732 * backup files left behind after an update. |
|
2733 * |
|
2734 * @param aFile |
|
2735 * An nsIFile to check if it has moz-backup for its extension. |
|
2736 */ |
|
2737 function checkForBackupFiles(aFile) { |
|
2738 do_check_neq(getFileExtension(aFile), "moz-backup"); |
|
2739 } |
|
2740 |
|
2741 /** |
|
2742 * Helper function for updater binary tests for recursively enumerating a |
|
2743 * directory and calling a callback function with the file as a parameter for |
|
2744 * each file found. |
|
2745 * |
|
2746 * @param aDir |
|
2747 * A nsIFile for the directory to be deleted |
|
2748 * @param aCallback |
|
2749 * A callback function that will be called with the file as a |
|
2750 * parameter for each file found. |
|
2751 */ |
|
2752 function checkFilesInDirRecursive(aDir, aCallback) { |
|
2753 if (!aDir.exists()) |
|
2754 do_throw("Directory must exist!"); |
|
2755 |
|
2756 let dirEntries = aDir.directoryEntries; |
|
2757 while (dirEntries.hasMoreElements()) { |
|
2758 let entry = dirEntries.getNext().QueryInterface(AUS_Ci.nsIFile); |
|
2759 |
|
2760 if (entry.isDirectory()) { |
|
2761 checkFilesInDirRecursive(entry, aCallback); |
|
2762 } else { |
|
2763 aCallback(entry); |
|
2764 } |
|
2765 } |
|
2766 } |
|
2767 |
|
2768 /** |
|
2769 * Sets up the bare bones XMLHttpRequest implementation below. |
|
2770 * |
|
2771 * @param aCallback |
|
2772 * The callback function that will call the nsIDomEventListener's |
|
2773 * handleEvent method. |
|
2774 * |
|
2775 * Example of the callback function |
|
2776 * |
|
2777 * function callHandleEvent() { |
|
2778 * gXHR.status = gExpectedStatus; |
|
2779 * var e = { target: gXHR }; |
|
2780 * gXHR.onload.handleEvent(e); |
|
2781 * } |
|
2782 */ |
|
2783 function overrideXHR(aCallback) { |
|
2784 gXHRCallback = aCallback; |
|
2785 gXHR = new xhr(); |
|
2786 var registrar = Components.manager.QueryInterface(AUS_Ci.nsIComponentRegistrar); |
|
2787 registrar.registerFactory(gXHR.classID, gXHR.classDescription, |
|
2788 gXHR.contractID, gXHR); |
|
2789 } |
|
2790 |
|
2791 |
|
2792 /** |
|
2793 * Bare bones XMLHttpRequest implementation for testing onprogress, onerror, |
|
2794 * and onload nsIDomEventListener handleEvent. |
|
2795 */ |
|
2796 function makeHandler(aVal) { |
|
2797 if (typeof aVal == "function") |
|
2798 return { handleEvent: aVal }; |
|
2799 return aVal; |
|
2800 } |
|
2801 function xhr() { |
|
2802 } |
|
2803 xhr.prototype = { |
|
2804 overrideMimeType: function(aMimetype) { }, |
|
2805 setRequestHeader: function(aHeader, aValue) { }, |
|
2806 status: null, |
|
2807 channel: { set notificationCallbacks(aVal) { } }, |
|
2808 _url: null, |
|
2809 _method: null, |
|
2810 open: function(aMethod, aUrl) { |
|
2811 gXHR.channel.originalURI = Services.io.newURI(aUrl, null, null); |
|
2812 gXHR._method = aMethod; gXHR._url = aUrl; |
|
2813 }, |
|
2814 responseXML: null, |
|
2815 responseText: null, |
|
2816 send: function(aBody) { |
|
2817 do_execute_soon(gXHRCallback); // Use a timeout so the XHR completes |
|
2818 }, |
|
2819 _onprogress: null, |
|
2820 set onprogress(aValue) { gXHR._onprogress = makeHandler(aValue); }, |
|
2821 get onprogress() { return gXHR._onprogress; }, |
|
2822 _onerror: null, |
|
2823 set onerror(aValue) { gXHR._onerror = makeHandler(aValue); }, |
|
2824 get onerror() { return gXHR._onerror; }, |
|
2825 _onload: null, |
|
2826 set onload(aValue) { gXHR._onload = makeHandler(aValue); }, |
|
2827 get onload() { return gXHR._onload; }, |
|
2828 addEventListener: function(aEvent, aValue, aCapturing) { |
|
2829 eval("gXHR._on" + aEvent + " = aValue"); |
|
2830 }, |
|
2831 flags: AUS_Ci.nsIClassInfo.SINGLETON, |
|
2832 implementationLanguage: AUS_Ci.nsIProgrammingLanguage.JAVASCRIPT, |
|
2833 getHelperForLanguage: function(aLanguage) null, |
|
2834 getInterfaces: function(aCount) { |
|
2835 var interfaces = [AUS_Ci.nsISupports]; |
|
2836 aCount.value = interfaces.length; |
|
2837 return interfaces; |
|
2838 }, |
|
2839 classDescription: "XMLHttpRequest", |
|
2840 contractID: "@mozilla.org/xmlextras/xmlhttprequest;1", |
|
2841 classID: Components.ID("{c9b37f43-4278-4304-a5e0-600991ab08cb}"), |
|
2842 createInstance: function(aOuter, aIID) { |
|
2843 if (aOuter == null) |
|
2844 return gXHR.QueryInterface(aIID); |
|
2845 throw AUS_Cr.NS_ERROR_NO_AGGREGATION; |
|
2846 }, |
|
2847 QueryInterface: function(aIID) { |
|
2848 if (aIID.equals(AUS_Ci.nsIClassInfo) || |
|
2849 aIID.equals(AUS_Ci.nsISupports)) |
|
2850 return gXHR; |
|
2851 throw AUS_Cr.NS_ERROR_NO_INTERFACE; |
|
2852 }, |
|
2853 get wrappedJSObject() { return this; } |
|
2854 }; |
|
2855 |
|
2856 /** |
|
2857 * Helper function to override the update prompt component to verify whether it |
|
2858 * is called or not. |
|
2859 * |
|
2860 * @param aCallback |
|
2861 * The callback to call if the update prompt component is called. |
|
2862 */ |
|
2863 function overrideUpdatePrompt(aCallback) { |
|
2864 var registrar = Components.manager.QueryInterface(AUS_Ci.nsIComponentRegistrar); |
|
2865 gUpdatePrompt = new UpdatePrompt(); |
|
2866 gUpdatePromptCallback = aCallback; |
|
2867 registrar.registerFactory(gUpdatePrompt.classID, gUpdatePrompt.classDescription, |
|
2868 gUpdatePrompt.contractID, gUpdatePrompt); |
|
2869 } |
|
2870 |
|
2871 function UpdatePrompt() { |
|
2872 var fns = ["checkForUpdates", "showUpdateAvailable", "showUpdateDownloaded", |
|
2873 "showUpdateError", "showUpdateHistory", "showUpdateInstalled"]; |
|
2874 |
|
2875 fns.forEach(function(aPromptFn) { |
|
2876 UpdatePrompt.prototype[aPromptFn] = function() { |
|
2877 if (!gUpdatePromptCallback) { |
|
2878 return; |
|
2879 } |
|
2880 |
|
2881 var callback = gUpdatePromptCallback[aPromptFn]; |
|
2882 if (!callback) { |
|
2883 return; |
|
2884 } |
|
2885 |
|
2886 callback.apply(gUpdatePromptCallback, |
|
2887 Array.prototype.slice.call(arguments)); |
|
2888 } |
|
2889 }); |
|
2890 } |
|
2891 |
|
2892 UpdatePrompt.prototype = { |
|
2893 flags: AUS_Ci.nsIClassInfo.SINGLETON, |
|
2894 implementationLanguage: AUS_Ci.nsIProgrammingLanguage.JAVASCRIPT, |
|
2895 getHelperForLanguage: function(aLanguage) null, |
|
2896 getInterfaces: function(aCount) { |
|
2897 var interfaces = [AUS_Ci.nsISupports, AUS_Ci.nsIUpdatePrompt]; |
|
2898 aCount.value = interfaces.length; |
|
2899 return interfaces; |
|
2900 }, |
|
2901 classDescription: "UpdatePrompt", |
|
2902 contractID: "@mozilla.org/updates/update-prompt;1", |
|
2903 classID: Components.ID("{8c350a15-9b90-4622-93a1-4d320308664b}"), |
|
2904 createInstance: function(aOuter, aIID) { |
|
2905 if (aOuter == null) |
|
2906 return gUpdatePrompt.QueryInterface(aIID); |
|
2907 throw AUS_Cr.NS_ERROR_NO_AGGREGATION; |
|
2908 }, |
|
2909 QueryInterface: function(aIID) { |
|
2910 if (aIID.equals(AUS_Ci.nsIClassInfo) || |
|
2911 aIID.equals(AUS_Ci.nsISupports) || |
|
2912 aIID.equals(AUS_Ci.nsIUpdatePrompt)) |
|
2913 return gUpdatePrompt; |
|
2914 throw AUS_Cr.NS_ERROR_NO_INTERFACE; |
|
2915 }, |
|
2916 }; |
|
2917 |
|
2918 /* Update check listener */ |
|
2919 const updateCheckListener = { |
|
2920 onProgress: function UCL_onProgress(aRequest, aPosition, aTotalSize) { |
|
2921 }, |
|
2922 |
|
2923 onCheckComplete: function UCL_onCheckComplete(aRequest, aUpdates, aUpdateCount) { |
|
2924 gRequestURL = aRequest.channel.originalURI.spec; |
|
2925 gUpdateCount = aUpdateCount; |
|
2926 gUpdates = aUpdates; |
|
2927 logTestInfo("url = " + gRequestURL + ", " + |
|
2928 "request.status = " + aRequest.status + ", " + |
|
2929 "update.statusText = " + aRequest.statusText + ", " + |
|
2930 "updateCount = " + aUpdateCount); |
|
2931 // Use a timeout to allow the XHR to complete |
|
2932 do_execute_soon(gCheckFunc); |
|
2933 }, |
|
2934 |
|
2935 onError: function UCL_onError(aRequest, aUpdate) { |
|
2936 gRequestURL = aRequest.channel.originalURI.spec; |
|
2937 gStatusCode = aRequest.status; |
|
2938 |
|
2939 gStatusText = aUpdate.statusText; |
|
2940 logTestInfo("url = " + gRequestURL + ", " + |
|
2941 "request.status = " + gStatusCode + ", " + |
|
2942 "update.statusText = " + gStatusText); |
|
2943 // Use a timeout to allow the XHR to complete |
|
2944 do_execute_soon(gCheckFunc.bind(null, aRequest, aUpdate)); |
|
2945 }, |
|
2946 |
|
2947 QueryInterface: function(aIID) { |
|
2948 if (!aIID.equals(AUS_Ci.nsIUpdateCheckListener) && |
|
2949 !aIID.equals(AUS_Ci.nsISupports)) |
|
2950 throw AUS_Cr.NS_ERROR_NO_INTERFACE; |
|
2951 return this; |
|
2952 } |
|
2953 }; |
|
2954 |
|
2955 /* Update download listener - nsIRequestObserver */ |
|
2956 const downloadListener = { |
|
2957 onStartRequest: function DL_onStartRequest(aRequest, aContext) { |
|
2958 }, |
|
2959 |
|
2960 onProgress: function DL_onProgress(aRequest, aContext, aProgress, aMaxProgress) { |
|
2961 }, |
|
2962 |
|
2963 onStatus: function DL_onStatus(aRequest, aContext, aStatus, aStatusText) { |
|
2964 }, |
|
2965 |
|
2966 onStopRequest: function DL_onStopRequest(aRequest, aContext, aStatus) { |
|
2967 gStatusResult = aStatus; |
|
2968 // Use a timeout to allow the request to complete |
|
2969 do_execute_soon(gCheckFunc); |
|
2970 }, |
|
2971 |
|
2972 QueryInterface: function DL_QueryInterface(aIID) { |
|
2973 if (!aIID.equals(AUS_Ci.nsIRequestObserver) && |
|
2974 !aIID.equals(AUS_Ci.nsIProgressEventSink) && |
|
2975 !aIID.equals(AUS_Ci.nsISupports)) |
|
2976 throw AUS_Cr.NS_ERROR_NO_INTERFACE; |
|
2977 return this; |
|
2978 } |
|
2979 }; |
|
2980 |
|
2981 /** |
|
2982 * Helper for starting the http server used by the tests |
|
2983 */ |
|
2984 function start_httpserver() { |
|
2985 let dir = getTestDirFile(); |
|
2986 logTestInfo("http server directory path: " + dir.path); |
|
2987 |
|
2988 if (!dir.isDirectory()) { |
|
2989 do_throw("A file instead of a directory was specified for HttpServer " + |
|
2990 "registerDirectory! Path: " + dir.path + "\n"); |
|
2991 } |
|
2992 |
|
2993 AUS_Cu.import("resource://testing-common/httpd.js"); |
|
2994 gTestserver = new HttpServer(); |
|
2995 gTestserver.registerDirectory("/", dir); |
|
2996 gTestserver.start(-1); |
|
2997 let testserverPort = gTestserver.identity.primaryPort; |
|
2998 gURLData = URL_HOST + ":" + testserverPort + "/"; |
|
2999 logTestInfo("http server port = " + testserverPort); |
|
3000 } |
|
3001 |
|
3002 /** |
|
3003 * Helper for stopping the http server used by the tests |
|
3004 * |
|
3005 * @param aCallback |
|
3006 * The callback to call after stopping the http server. |
|
3007 */ |
|
3008 function stop_httpserver(aCallback) { |
|
3009 do_check_true(!!aCallback); |
|
3010 gTestserver.stop(aCallback); |
|
3011 } |
|
3012 |
|
3013 /** |
|
3014 * Creates an nsIXULAppInfo |
|
3015 * |
|
3016 * @param aID |
|
3017 * The ID of the test application |
|
3018 * @param aName |
|
3019 * A name for the test application |
|
3020 * @param aVersion |
|
3021 * The version of the application |
|
3022 * @param aPlatformVersion |
|
3023 * The gecko version of the application |
|
3024 */ |
|
3025 function createAppInfo(aID, aName, aVersion, aPlatformVersion) { |
|
3026 const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1"; |
|
3027 const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}"); |
|
3028 var XULAppInfo = { |
|
3029 vendor: APP_INFO_VENDOR, |
|
3030 name: aName, |
|
3031 ID: aID, |
|
3032 version: aVersion, |
|
3033 appBuildID: "2007010101", |
|
3034 platformVersion: aPlatformVersion, |
|
3035 platformBuildID: "2007010101", |
|
3036 inSafeMode: false, |
|
3037 logConsoleErrors: true, |
|
3038 OS: "XPCShell", |
|
3039 XPCOMABI: "noarch-spidermonkey", |
|
3040 |
|
3041 QueryInterface: function QueryInterface(aIID) { |
|
3042 if (aIID.equals(AUS_Ci.nsIXULAppInfo) || |
|
3043 aIID.equals(AUS_Ci.nsIXULRuntime) || |
|
3044 #ifdef XP_WIN |
|
3045 aIID.equals(AUS_Ci.nsIWinAppHelper) || |
|
3046 #endif |
|
3047 aIID.equals(AUS_Ci.nsISupports)) |
|
3048 return this; |
|
3049 throw AUS_Cr.NS_ERROR_NO_INTERFACE; |
|
3050 } |
|
3051 }; |
|
3052 |
|
3053 var XULAppInfoFactory = { |
|
3054 createInstance: function (aOuter, aIID) { |
|
3055 if (aOuter == null) |
|
3056 return XULAppInfo.QueryInterface(aIID); |
|
3057 throw AUS_Cr.NS_ERROR_NO_AGGREGATION; |
|
3058 } |
|
3059 }; |
|
3060 |
|
3061 var registrar = Components.manager.QueryInterface(AUS_Ci.nsIComponentRegistrar); |
|
3062 registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo", |
|
3063 XULAPPINFO_CONTRACTID, XULAppInfoFactory); |
|
3064 } |
|
3065 |
|
3066 /** |
|
3067 * Returns the platform specific arguments used by nsIProcess when launching |
|
3068 * the application. |
|
3069 * |
|
3070 * @param aExtraArgs (optional) |
|
3071 * An array of extra arguments to append to the default arguments. |
|
3072 * @return an array of arguments to be passed to nsIProcess. |
|
3073 * |
|
3074 * Note: a shell is necessary to pipe the application's console output which |
|
3075 * would otherwise pollute the xpcshell log. |
|
3076 * |
|
3077 * Command line arguments used when launching the application: |
|
3078 * -no-remote prevents shell integration from being affected by an existing |
|
3079 * application process. |
|
3080 * -process-updates makes the application exits after being relaunched by the |
|
3081 * updater. |
|
3082 * the platform specific string defined by PIPE_TO_NULL to output both stdout |
|
3083 * and stderr to null. This is needed to prevent output from the application |
|
3084 * from ending up in the xpchsell log. |
|
3085 */ |
|
3086 function getProcessArgs(aExtraArgs) { |
|
3087 if (!aExtraArgs) { |
|
3088 aExtraArgs = []; |
|
3089 } |
|
3090 |
|
3091 let appBinPath = getApplyDirFile(DIR_BIN_REL_PATH + FILE_APP_BIN, false).path; |
|
3092 if (/ /.test(appBinPath)) { |
|
3093 appBinPath = '"' + appBinPath + '"'; |
|
3094 } |
|
3095 |
|
3096 let args; |
|
3097 if (IS_UNIX) { |
|
3098 let launchScript = getLaunchScript(); |
|
3099 // Precreate the script with executable permissions |
|
3100 launchScript.create(AUS_Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_DIRECTORY); |
|
3101 |
|
3102 let scriptContents = "#! /bin/sh\n"; |
|
3103 scriptContents += appBinPath + " -no-remote -process-updates " + |
|
3104 aExtraArgs.join(" ") + " " + PIPE_TO_NULL; |
|
3105 writeFile(launchScript, scriptContents); |
|
3106 logTestInfo("created " + launchScript.path + " containing:\n" + |
|
3107 scriptContents); |
|
3108 args = [launchScript.path]; |
|
3109 } else { |
|
3110 args = ["/D", "/Q", "/C", appBinPath, "-no-remote", "-process-updates"]. |
|
3111 concat(aExtraArgs).concat([PIPE_TO_NULL]); |
|
3112 } |
|
3113 return args; |
|
3114 } |
|
3115 |
|
3116 /** |
|
3117 * Gets a file path for the application to dump its arguments into. This is used |
|
3118 * to verify that a callback application is launched. |
|
3119 * |
|
3120 * @return the file for the application to dump its arguments into. |
|
3121 */ |
|
3122 function getAppArgsLogPath() { |
|
3123 let appArgsLog = do_get_file("/", true); |
|
3124 appArgsLog.append(gTestID + "_app_args_log"); |
|
3125 if (appArgsLog.exists()) { |
|
3126 appArgsLog.remove(false); |
|
3127 } |
|
3128 let appArgsLogPath = appArgsLog.path; |
|
3129 if (/ /.test(appArgsLogPath)) { |
|
3130 appArgsLogPath = '"' + appArgsLogPath + '"'; |
|
3131 } |
|
3132 return appArgsLogPath; |
|
3133 } |
|
3134 |
|
3135 /** |
|
3136 * Gets the nsIFile reference for the shell script to launch the application. If |
|
3137 * the file exists it will be removed by this function. |
|
3138 * |
|
3139 * @return the nsIFile for the shell script to launch the application. |
|
3140 */ |
|
3141 function getLaunchScript() { |
|
3142 let launchScript = do_get_file("/", true); |
|
3143 launchScript.append(gTestID + "_launch.sh"); |
|
3144 if (launchScript.exists()) { |
|
3145 launchScript.remove(false); |
|
3146 } |
|
3147 return launchScript; |
|
3148 } |
|
3149 |
|
3150 /** |
|
3151 * Makes GreD, XREExeF, and UpdRootD point to unique file system locations so |
|
3152 * xpcshell tests can run in parallel and to keep the environment clean. |
|
3153 */ |
|
3154 function adjustGeneralPaths() { |
|
3155 let dirProvider = { |
|
3156 getFile: function AGP_DP_getFile(aProp, aPersistent) { |
|
3157 aPersistent.value = true; |
|
3158 switch (aProp) { |
|
3159 case NS_GRE_DIR: |
|
3160 if (gUseTestAppDir) { |
|
3161 return getApplyDirFile(DIR_BIN_REL_PATH, true); |
|
3162 } |
|
3163 break; |
|
3164 case XRE_EXECUTABLE_FILE: |
|
3165 if (gUseTestAppDir) { |
|
3166 return getApplyDirFile(DIR_BIN_REL_PATH + FILE_APP_BIN, true); |
|
3167 } |
|
3168 break; |
|
3169 case XRE_UPDATE_ROOT_DIR: |
|
3170 return getMockUpdRootD(); |
|
3171 } |
|
3172 return null; |
|
3173 }, |
|
3174 QueryInterface: function(aIID) { |
|
3175 if (aIID.equals(AUS_Ci.nsIDirectoryServiceProvider) || |
|
3176 aIID.equals(AUS_Ci.nsISupports)) |
|
3177 return this; |
|
3178 throw AUS_Cr.NS_ERROR_NO_INTERFACE; |
|
3179 } |
|
3180 }; |
|
3181 let ds = Services.dirsvc.QueryInterface(AUS_Ci.nsIDirectoryService); |
|
3182 ds.QueryInterface(AUS_Ci.nsIProperties).undefine(NS_GRE_DIR); |
|
3183 ds.QueryInterface(AUS_Ci.nsIProperties).undefine(XRE_EXECUTABLE_FILE); |
|
3184 ds.registerProvider(dirProvider); |
|
3185 do_register_cleanup(function AGP_cleanup() { |
|
3186 logTestInfo("start - unregistering directory provider"); |
|
3187 |
|
3188 if (gAppTimer) { |
|
3189 logTestInfo("start - cancel app timer"); |
|
3190 gAppTimer.cancel(); |
|
3191 gAppTimer = null; |
|
3192 logTestInfo("finish - cancel app timer"); |
|
3193 } |
|
3194 |
|
3195 if (gProcess && gProcess.isRunning) { |
|
3196 logTestInfo("start - kill process"); |
|
3197 try { |
|
3198 gProcess.kill(); |
|
3199 } catch (e) { |
|
3200 logTestInfo("kill process failed. Exception: " + e); |
|
3201 } |
|
3202 gProcess = null; |
|
3203 logTestInfo("finish - kill process"); |
|
3204 } |
|
3205 |
|
3206 if (gHandle) { |
|
3207 try { |
|
3208 logTestInfo("start - closing handle"); |
|
3209 let kernel32 = ctypes.open("kernel32"); |
|
3210 let CloseHandle = kernel32.declare("CloseHandle", ctypes.default_abi, |
|
3211 ctypes.bool, /*return*/ |
|
3212 ctypes.voidptr_t /*handle*/); |
|
3213 if (!CloseHandle(gHandle)) { |
|
3214 logTestInfo("call to CloseHandle failed"); |
|
3215 } |
|
3216 kernel32.close(); |
|
3217 gHandle = null; |
|
3218 logTestInfo("finish - closing handle"); |
|
3219 } catch (e) { |
|
3220 logTestInfo("call to CloseHandle failed. Exception: " + e); |
|
3221 } |
|
3222 } |
|
3223 |
|
3224 // Call end_test first before the directory provider is unregistered |
|
3225 if (typeof(end_test) == typeof(Function)) { |
|
3226 logTestInfo("calling end_test"); |
|
3227 end_test(); |
|
3228 } |
|
3229 |
|
3230 ds.unregisterProvider(dirProvider); |
|
3231 cleanupTestCommon(); |
|
3232 |
|
3233 logTestInfo("finish - unregistering directory provider"); |
|
3234 }); |
|
3235 } |
|
3236 |
|
3237 |
|
3238 /** |
|
3239 * Helper function for launching the application to apply an update. |
|
3240 */ |
|
3241 function launchAppToApplyUpdate() { |
|
3242 logTestInfo("start - launching application to apply update"); |
|
3243 |
|
3244 let appBin = getApplyDirFile(DIR_BIN_REL_PATH + FILE_APP_BIN, false); |
|
3245 |
|
3246 if (typeof(customLaunchAppToApplyUpdate) == typeof(Function)) { |
|
3247 customLaunchAppToApplyUpdate(); |
|
3248 } |
|
3249 |
|
3250 let launchBin = getLaunchBin(); |
|
3251 let args = getProcessArgs(); |
|
3252 logTestInfo("launching " + launchBin.path + " " + args.join(" ")); |
|
3253 |
|
3254 gProcess = AUS_Cc["@mozilla.org/process/util;1"]. |
|
3255 createInstance(AUS_Ci.nsIProcess); |
|
3256 gProcess.init(launchBin); |
|
3257 |
|
3258 gAppTimer = AUS_Cc["@mozilla.org/timer;1"].createInstance(AUS_Ci.nsITimer); |
|
3259 gAppTimer.initWithCallback(gTimerCallback, APP_TIMER_TIMEOUT, |
|
3260 AUS_Ci.nsITimer.TYPE_ONE_SHOT); |
|
3261 |
|
3262 setEnvironment(); |
|
3263 logTestInfo("launching application"); |
|
3264 gProcess.runAsync(args, args.length, gProcessObserver); |
|
3265 resetEnvironment(); |
|
3266 |
|
3267 logTestInfo("finish - launching application to apply update"); |
|
3268 } |
|
3269 |
|
3270 /** |
|
3271 * The observer for the call to nsIProcess:runAsync. |
|
3272 */ |
|
3273 let gProcessObserver = { |
|
3274 observe: function PO_observe(aSubject, aTopic, aData) { |
|
3275 logTestInfo("topic: " + aTopic + ", process exitValue: " + |
|
3276 gProcess.exitValue); |
|
3277 if (gAppTimer) { |
|
3278 gAppTimer.cancel(); |
|
3279 gAppTimer = null; |
|
3280 } |
|
3281 if (aTopic != "process-finished" || gProcess.exitValue != 0) { |
|
3282 do_throw("Failed to launch application"); |
|
3283 } |
|
3284 do_timeout(TEST_CHECK_TIMEOUT, checkUpdateFinished); |
|
3285 }, |
|
3286 QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsIObserver]) |
|
3287 }; |
|
3288 |
|
3289 /** |
|
3290 * The timer callback to kill the process if it takes too long. |
|
3291 */ |
|
3292 let gTimerCallback = { |
|
3293 notify: function TC_notify(aTimer) { |
|
3294 gAppTimer = null; |
|
3295 if (gProcess.isRunning) { |
|
3296 logTestInfo("attempt to kill process"); |
|
3297 gProcess.kill(); |
|
3298 } |
|
3299 do_throw("launch application timer expired"); |
|
3300 }, |
|
3301 QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsITimerCallback]) |
|
3302 }; |
|
3303 |
|
3304 /** |
|
3305 * The update-staged observer for the call to nsIUpdateProcessor:processUpdate. |
|
3306 */ |
|
3307 let gUpdateStagedObserver = { |
|
3308 observe: function(aSubject, aTopic, aData) { |
|
3309 if (aTopic == "update-staged") { |
|
3310 Services.obs.removeObserver(gUpdateStagedObserver, "update-staged"); |
|
3311 checkUpdateApplied(); |
|
3312 } |
|
3313 }, |
|
3314 QueryInterface: XPCOMUtils.generateQI([AUS_Ci.nsIObserver]) |
|
3315 }; |
|
3316 |
|
3317 /** |
|
3318 * Sets the environment that will be used by the application process when it is |
|
3319 * launched. |
|
3320 */ |
|
3321 function setEnvironment() { |
|
3322 // Prevent setting the environment more than once. |
|
3323 if (gShouldResetEnv !== undefined) |
|
3324 return; |
|
3325 |
|
3326 gShouldResetEnv = true; |
|
3327 |
|
3328 let env = AUS_Cc["@mozilla.org/process/environment;1"]. |
|
3329 getService(AUS_Ci.nsIEnvironment); |
|
3330 if (IS_WIN && !env.exists("XRE_NO_WINDOWS_CRASH_DIALOG")) { |
|
3331 gAddedEnvXRENoWindowsCrashDialog = true; |
|
3332 logTestInfo("setting the XRE_NO_WINDOWS_CRASH_DIALOG environment " + |
|
3333 "variable to 1... previously it didn't exist"); |
|
3334 env.set("XRE_NO_WINDOWS_CRASH_DIALOG", "1"); |
|
3335 } |
|
3336 |
|
3337 if (IS_UNIX) { |
|
3338 let appGreDir = gGREDirOrig.clone(); |
|
3339 let envGreDir = AUS_Cc["@mozilla.org/file/local;1"]. |
|
3340 createInstance(AUS_Ci.nsILocalFile); |
|
3341 let shouldSetEnv = true; |
|
3342 if (IS_MACOSX) { |
|
3343 if (env.exists("DYLD_LIBRARY_PATH")) { |
|
3344 gEnvDyldLibraryPath = env.get("DYLD_LIBRARY_PATH"); |
|
3345 envGreDir.initWithPath(gEnvDyldLibraryPath); |
|
3346 if (envGreDir.path == appGreDir.path) { |
|
3347 gEnvDyldLibraryPath = null; |
|
3348 shouldSetEnv = false; |
|
3349 } |
|
3350 } |
|
3351 |
|
3352 if (shouldSetEnv) { |
|
3353 logTestInfo("setting DYLD_LIBRARY_PATH environment variable value to " + |
|
3354 appGreDir.path); |
|
3355 env.set("DYLD_LIBRARY_PATH", appGreDir.path); |
|
3356 } |
|
3357 } else { |
|
3358 if (env.exists("LD_LIBRARY_PATH")) { |
|
3359 gEnvLdLibraryPath = env.get("LD_LIBRARY_PATH"); |
|
3360 envGreDir.initWithPath(gEnvLdLibraryPath); |
|
3361 if (envGreDir.path == appGreDir.path) { |
|
3362 gEnvLdLibraryPath = null; |
|
3363 shouldSetEnv = false; |
|
3364 } |
|
3365 } |
|
3366 |
|
3367 if (shouldSetEnv) { |
|
3368 logTestInfo("setting LD_LIBRARY_PATH environment variable value to " + |
|
3369 appGreDir.path); |
|
3370 env.set("LD_LIBRARY_PATH", appGreDir.path); |
|
3371 } |
|
3372 } |
|
3373 } |
|
3374 |
|
3375 if (env.exists("XPCOM_MEM_LEAK_LOG")) { |
|
3376 gEnvXPCOMMemLeakLog = env.get("XPCOM_MEM_LEAK_LOG"); |
|
3377 logTestInfo("removing the XPCOM_MEM_LEAK_LOG environment variable... " + |
|
3378 "previous value " + gEnvXPCOMMemLeakLog); |
|
3379 env.set("XPCOM_MEM_LEAK_LOG", ""); |
|
3380 } |
|
3381 |
|
3382 if (env.exists("XPCOM_DEBUG_BREAK")) { |
|
3383 gEnvXPCOMDebugBreak = env.get("XPCOM_DEBUG_BREAK"); |
|
3384 logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " + |
|
3385 "warn... previous value " + gEnvXPCOMDebugBreak); |
|
3386 } else { |
|
3387 logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable to " + |
|
3388 "warn... previously it didn't exist"); |
|
3389 } |
|
3390 |
|
3391 env.set("XPCOM_DEBUG_BREAK", "warn"); |
|
3392 |
|
3393 if (gStageUpdate) { |
|
3394 logTestInfo("setting the MOZ_UPDATE_STAGING environment variable to 1\n"); |
|
3395 env.set("MOZ_UPDATE_STAGING", "1"); |
|
3396 } |
|
3397 |
|
3398 logTestInfo("setting MOZ_NO_SERVICE_FALLBACK environment variable to 1"); |
|
3399 env.set("MOZ_NO_SERVICE_FALLBACK", "1"); |
|
3400 } |
|
3401 |
|
3402 /** |
|
3403 * Sets the environment back to the original values after launching the |
|
3404 * application. |
|
3405 */ |
|
3406 function resetEnvironment() { |
|
3407 // Prevent resetting the environment more than once. |
|
3408 if (gShouldResetEnv !== true) |
|
3409 return; |
|
3410 |
|
3411 gShouldResetEnv = false; |
|
3412 |
|
3413 let env = AUS_Cc["@mozilla.org/process/environment;1"]. |
|
3414 getService(AUS_Ci.nsIEnvironment); |
|
3415 |
|
3416 if (gEnvXPCOMMemLeakLog) { |
|
3417 logTestInfo("setting the XPCOM_MEM_LEAK_LOG environment variable back to " + |
|
3418 gEnvXPCOMMemLeakLog); |
|
3419 env.set("XPCOM_MEM_LEAK_LOG", gEnvXPCOMMemLeakLog); |
|
3420 } |
|
3421 |
|
3422 if (gEnvXPCOMDebugBreak) { |
|
3423 logTestInfo("setting the XPCOM_DEBUG_BREAK environment variable back to " + |
|
3424 gEnvXPCOMDebugBreak); |
|
3425 env.set("XPCOM_DEBUG_BREAK", gEnvXPCOMDebugBreak); |
|
3426 } else { |
|
3427 logTestInfo("clearing the XPCOM_DEBUG_BREAK environment variable"); |
|
3428 env.set("XPCOM_DEBUG_BREAK", ""); |
|
3429 } |
|
3430 |
|
3431 if (IS_UNIX) { |
|
3432 if (IS_MACOSX) { |
|
3433 if (gEnvDyldLibraryPath) { |
|
3434 logTestInfo("setting DYLD_LIBRARY_PATH environment variable value " + |
|
3435 "back to " + gEnvDyldLibraryPath); |
|
3436 env.set("DYLD_LIBRARY_PATH", gEnvDyldLibraryPath); |
|
3437 } else { |
|
3438 logTestInfo("removing DYLD_LIBRARY_PATH environment variable"); |
|
3439 env.set("DYLD_LIBRARY_PATH", ""); |
|
3440 } |
|
3441 } else { |
|
3442 if (gEnvLdLibraryPath) { |
|
3443 logTestInfo("setting LD_LIBRARY_PATH environment variable value back " + |
|
3444 "to " + gEnvLdLibraryPath); |
|
3445 env.set("LD_LIBRARY_PATH", gEnvLdLibraryPath); |
|
3446 } else { |
|
3447 logTestInfo("removing LD_LIBRARY_PATH environment variable"); |
|
3448 env.set("LD_LIBRARY_PATH", ""); |
|
3449 } |
|
3450 } |
|
3451 } |
|
3452 |
|
3453 if (IS_WIN && gAddedEnvXRENoWindowsCrashDialog) { |
|
3454 logTestInfo("removing the XRE_NO_WINDOWS_CRASH_DIALOG environment " + |
|
3455 "variable"); |
|
3456 env.set("XRE_NO_WINDOWS_CRASH_DIALOG", ""); |
|
3457 } |
|
3458 |
|
3459 if (gStageUpdate) { |
|
3460 logTestInfo("removing the MOZ_UPDATE_STAGING environment variable\n"); |
|
3461 env.set("MOZ_UPDATE_STAGING", ""); |
|
3462 } |
|
3463 |
|
3464 logTestInfo("removing MOZ_NO_SERVICE_FALLBACK environment variable"); |
|
3465 env.set("MOZ_NO_SERVICE_FALLBACK", ""); |
|
3466 } |