Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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/. */
5 const INSTALL_LOCALE = "@AB_CD@";
6 const MOZ_APP_NAME = "@MOZ_APP_NAME@";
7 const BIN_SUFFIX = "@BIN_SUFFIX@";
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
19 // MOZ_APP_BASENAME is not optional for tests.
20 const MOZ_APP_BASENAME = "@MOZ_APP_BASENAME@";
21 #endif // XP_WIN
23 const APP_INFO_NAME = "XPCShell";
24 const APP_INFO_VENDOR = "Mozilla";
26 #ifdef XP_UNIX
27 const APP_BIN_SUFFIX = "-bin";
28 #else
29 const APP_BIN_SUFFIX = "@BIN_SUFFIX@";
30 #endif
32 #ifdef XP_WIN
33 const IS_WIN = true;
34 #else
35 const IS_WIN = false;
36 #endif
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
49 #ifdef XP_UNIX
50 const IS_UNIX = true;
51 #else
52 const IS_UNIX = false;
53 #endif
55 #ifdef ANDROID
56 const IS_ANDROID = true;
57 #else
58 const IS_ANDROID = false;
59 #endif
61 #ifdef MOZ_WIDGET_GONK
62 const IS_TOOLKIT_GONK = true;
63 #else
64 const IS_TOOLKIT_GONK = false;
65 #endif
67 const USE_EXECV = IS_UNIX && !IS_MACOSX;
69 #ifdef MOZ_VERIFY_MAR_SIGNATURE
70 const IS_MAR_CHECKS_ENABLED = true;
71 #else
72 const IS_MAR_CHECKS_ENABLED = false;
73 #endif
75 const URL_HOST = "http://localhost";
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";
90 const LOG_COMPLETE_SUCCESS = "complete_log_success";
91 const LOG_PARTIAL_SUCCESS = "partial_log_success";
92 const LOG_PARTIAL_FAILURE = "partial_log_failure";
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";
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";
104 const LOG_SVC_SUCCESSFUL_LAUNCH = "Process was started... waiting on result.";
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;
111 // Time to wait for the test helper process before continuing the test
112 const TEST_HELPER_TIMEOUT = 100;
114 // Time to wait for a check in the test before continuing the test
115 const TEST_CHECK_TIMEOUT = 100;
117 // How many of TEST_CHECK_TIMEOUT to wait before we abort the test.
118 const MAX_TIMEOUT_RUNS = 2000;
120 // Time in seconds the helper application should sleep.the helper's input and output files
121 const HELPER_SLEEP_TIMEOUT = 180;
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;
127 #ifdef XP_WIN
128 const PIPE_TO_NULL = ">nul";
129 #else
130 const PIPE_TO_NULL = "> /dev/null 2>&1";
131 #endif
133 // This default value will be overridden when using the http server.
134 var gURLData = URL_HOST + "/";
136 var gTestID;
138 var gTestserver;
140 var gRegisteredServiceCleanup;
142 var gXHR;
143 var gXHRCallback;
145 var gUpdatePrompt;
146 var gUpdatePromptCallback;
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;
158 var gProcess;
159 var gAppTimer;
160 var gHandle;
162 var gGREDirOrig;
163 var gAppDirOrig;
165 var gServiceLaunchedCallbackLog = null;
166 var gServiceLaunchedCallbackArgs = null;
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;
177 var gTimeoutRuns = 0;
179 // Environment related globals
180 var gShouldResetEnv = undefined;
181 var gAddedEnvXRENoWindowsCrashDialog = false;
182 var gEnvXPCOMDebugBreak;
183 var gEnvXPCOMMemLeakLog;
184 var gEnvDyldLibraryPath;
185 var gEnvLdLibraryPath;
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;
202 #include ../shared.js
204 var gTestFiles = [];
205 var gTestDirs = [];
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 }];
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 }];
432 // Concatenate the common files to the end of the array.
433 gTestFilesCompleteSuccess = gTestFilesCompleteSuccess.concat(gTestFilesCommon);
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 }];
638 // Concatenate the common files to the end of the array.
639 gTestFilesPartialSuccess = gTestFilesPartialSuccess.concat(gTestFilesCommon);
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 }];
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 }];
809 // Concatenate the common files to the beginning of the array.
810 gTestDirsCompleteSuccess = gTestDirsCommon.concat(gTestDirsCompleteSuccess);
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 }];
825 // Concatenate the common files to the beginning of the array.
826 gTestDirsPartialSuccess = gTestDirsCommon.concat(gTestDirsPartialSuccess);
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 }];
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 }
849 /**
850 * Helper function for setting up the test environment.
851 */
852 function setupTestCommon() {
853 logTestInfo("start - general test setup");
855 do_test_pending();
857 if (gTestID) {
858 do_throw("setupTestCommon should only be called once!");
859 }
861 let caller = Components.stack.caller;
862 gTestID = caller.filename.toString().split("/").pop().split(".")[0];
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 }
883 // Don't attempt to show a prompt when an update finishes.
884 Services.prefs.setBoolPref(PREF_APP_UPDATE_SILENT, true);
886 gGREDirOrig = getGREDir();
887 gAppDirOrig = getAppBaseDir();
889 let applyDir = getApplyDirFile(null, true).parent;
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 }
904 // adjustGeneralPaths registers a cleanup function that calls end_test when
905 // it is defined as a function.
906 adjustGeneralPaths();
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 }
925 logTestInfo("finish - general test setup");
926 }
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");
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();
939 if (gChannel) {
940 gPrefRoot.removeObserver(PREF_APP_UPDATE_CHANNEL, observer);
941 }
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", "");
950 if (gXHR) {
951 gXHRCallback = null;
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;
959 gXHR = null;
960 }
962 gTestserver = null;
964 if (IS_UNIX) {
965 // This will delete the launch script if it exists.
966 getLaunchScript();
967 }
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 }
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 }
1011 let applyDir = getApplyDirFile(null, true).parent;
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 }
1025 resetEnvironment();
1027 logTestInfo("finish - general test cleanup");
1029 if (gRealDump) {
1030 dump = gRealDump;
1031 gRealDump = null;
1032 }
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 }
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 }
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 }
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 }
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 }
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 });
1102 gTestDirs.forEach(function STFADFF_Dirs(aTestDir) {
1103 aTestDir.dirRemoved = false;
1104 if (aTestDir.filesRemoved) {
1105 aTestDir.filesRemoved = false;
1106 }
1107 });
1108 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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;
1243 let vendor = MOZ_APP_VENDOR ? MOZ_APP_VENDOR : "Mozilla";
1244 let appDir = getApplyDirFile(null, true);
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 }
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 });
1271 XPCOMUtils.defineLazyGetter(this, "gLocalAppDataDir",
1272 function test_gLocalAppDataDir() {
1273 const CSIDL_LOCAL_APPDATA = 0x1c;
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 */);
1285 let aryPathLocalAppData = ctypes.jschar.array()(260);
1286 let rv = SHGetSpecialFolderPath(0, aryPathLocalAppData, CSIDL_LOCAL_APPDATA, false);
1287 lib.close();
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 });
1296 XPCOMUtils.defineLazyGetter(this, "gProgFilesDir",
1297 function test_gProgFilesDir() {
1298 const CSIDL_PROGRAM_FILES = 0x26;
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 */);
1310 let aryPathProgFiles = ctypes.jschar.array()(260);
1311 let rv = SHGetSpecialFolderPath(0, aryPathProgFiles, CSIDL_PROGRAM_FILES, false);
1312 lib.close();
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 });
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;
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 }
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 }
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 }
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
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 }
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 }
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 }
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 }
1480 let applyToDir = getApplyDirFile(null, true);
1481 let applyToDirPath = applyToDir.path;
1482 if (gStageUpdate || gSwitchApp) {
1483 applyToDirPath += "/" + DIR_UPDATED + "/";
1484 }
1486 if (IS_WIN) {
1487 // Convert to native path
1488 applyToDirPath = applyToDirPath.replace(/\//g, "\\");
1489 }
1491 let callbackApp = getApplyDirFile("a/b/" + gCallbackBinFile);
1492 callbackApp.permissions = PERMS_DIRECTORY;
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(" "));
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 }
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);
1517 if (gDisableReplaceFallback) {
1518 env.set("MOZ_NO_REPLACE_FALLBACK", "");
1519 }
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);
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);
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();
1564 logTestInfo("finish - staging update");
1565 }
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();
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 }
1588 let updaterBinPath = updaterBin.path;
1589 if (/ /.test(updaterBinPath)) {
1590 updaterBinPath = '"' + updaterBinPath + '"';
1591 }
1593 const REG_PATH = "SOFTWARE\\Mozilla\\MaintenanceService\\" +
1594 "3932ecacee736d366d6436db0f55bce4";
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
1610 logTestInfo("this test can only run on the buildbot build system at this " +
1611 "time.");
1612 return false;
1613 }
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 }
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 }
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 }
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 }
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 }
1690 setupAppFilesFinished();
1691 }
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");
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 }
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"];
1719 // On Linux the updater.png must also be copied
1720 if (IS_UNIX && !IS_MACOSX) {
1721 fileRelPaths.push("icons/updater.png");
1722 }
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);
1733 let hasMore;
1734 let line = {};
1735 do {
1736 hasMore = istream.readLine(line);
1737 fileRelPaths.push(line.value);
1738 } while(hasMore);
1740 istream.close();
1742 fileRelPaths.forEach(function CMAF_FLN_FE(aFileRelPath) {
1743 copyFileToTestAppDir(aFileRelPath);
1744 });
1746 logTestInfo("finish - copying or creating symlinks for application files " +
1747 "for the test");
1748 }
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 }
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 }
1780 if (!srcFile.exists()) {
1781 do_throw("Unable to copy file since it doesn't exist! Path: " +
1782 srcFile.path);
1783 }
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 }
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 }
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 }
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 }
1942 // Make sure the service from the previous test is already stopped.
1943 waitForServiceStop(true);
1945 // Prevent the cleanup function from begin run more than once
1946 if (gRegisteredServiceCleanup === undefined) {
1947 gRegisteredServiceCleanup = true;
1949 do_register_cleanup(function RUUS_cleanup() {
1950 resetEnvironment();
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 }
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 }
1967 let appArgsLogPath = getAppArgsLogPath();
1968 gServiceLaunchedCallbackLog = appArgsLogPath.replace(/^"|"$/g, "");
1970 let updatesDir = getUpdatesPatchDir();
1971 let file = updatesDir.clone();
1972 writeStatusFile(aInitialStatus);
1974 // sanity check
1975 do_check_eq(readStatusState(), aInitialStatus);
1977 writeVersionFile(DEFAULT_UPDATE_VERSION);
1979 gServiceLaunchedCallbackArgs = [
1980 "-no-remote",
1981 "-process-updates",
1982 "-dump-args",
1983 appArgsLogPath
1984 ];
1986 if (gSwitchApp) {
1987 // We want to set the env vars again
1988 gShouldResetEnv = undefined;
1989 }
1991 setEnvironment();
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);
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);
2005 let launchBin = getLaunchBin();
2006 let args = getProcessArgs(["-dump-args", appArgsLogPath]);
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);
2019 resetEnvironment();
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 }
2034 // Make sure all of the logs are written out.
2035 waitForServiceStop(false);
2037 aTimer.cancel();
2038 aTimer = null;
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);
2052 if (aCheckSvcLog) {
2053 checkServiceLogs(svcOriginalLog);
2054 }
2056 checkUpdateFinished();
2057 }
2059 let timer = AUS_Cc["@mozilla.org/timer;1"].createInstance(AUS_Ci.nsITimer);
2060 timer.initWithCallback(timerCallback, 1000, timer.TYPE_REPEATING_SLACK);
2061 }
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 }
2084 if (!launchBin.exists())
2085 do_throw(launchBin.path + " must exist to run this test!");
2087 return launchBin;
2088 }
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 }
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 }
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 }
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 }
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);
2188 createUpdateSettingsINI();
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);
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 }
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 });
2221 let helperBin = getTestDirFile(FILE_HELPER_BIN);
2222 let afterApplyBinDir = getApplyDirFile("a/b/", true);
2223 helperBin.copyToFollowingLinks(afterApplyBinDir, gCallbackBinFile);
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 }
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 }
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 }
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 });
2261 gTestExtraDirs[0].dirExists = aUpdatedDirExists;
2262 gTestExtraDirs[1].dirExists = IS_WIN ? aToBeDeletedDirExists : false;
2263 }
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 }
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);
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");
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, "");
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 }
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 }
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());
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 }
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 });
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());
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 }
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 });
2484 checkFilesAfterUpdateCommon();
2485 }
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());
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 }
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 });
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());
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 }
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 });
2569 checkFilesAfterUpdateCommon();
2570 }
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 });
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());
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());
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 }
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 }
2615 logTestInfo("testing backup files should not be left behind");
2616 let applyToDir = getTargetDirFile(null, true);
2617 checkFilesInDirRecursive(applyToDir, checkForBackupFiles);
2618 }
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 }
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 }
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 }
2651 waitForFilesInUse();
2652 }
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);
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 }
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);
2680 waitForFilesInUse();
2681 }
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);
2693 let files = [appBin, updater, maintSvcInstaller, helper];
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 }
2726 logTestInfo("calling doTestFinish");
2727 doTestFinish();
2728 }
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 }
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!");
2756 let dirEntries = aDir.directoryEntries;
2757 while (dirEntries.hasMoreElements()) {
2758 let entry = dirEntries.getNext().QueryInterface(AUS_Ci.nsIFile);
2760 if (entry.isDirectory()) {
2761 checkFilesInDirRecursive(entry, aCallback);
2762 } else {
2763 aCallback(entry);
2764 }
2765 }
2766 }
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 }
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 };
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 }
2871 function UpdatePrompt() {
2872 var fns = ["checkForUpdates", "showUpdateAvailable", "showUpdateDownloaded",
2873 "showUpdateError", "showUpdateHistory", "showUpdateInstalled"];
2875 fns.forEach(function(aPromptFn) {
2876 UpdatePrompt.prototype[aPromptFn] = function() {
2877 if (!gUpdatePromptCallback) {
2878 return;
2879 }
2881 var callback = gUpdatePromptCallback[aPromptFn];
2882 if (!callback) {
2883 return;
2884 }
2886 callback.apply(gUpdatePromptCallback,
2887 Array.prototype.slice.call(arguments));
2888 }
2889 });
2890 }
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 };
2918 /* Update check listener */
2919 const updateCheckListener = {
2920 onProgress: function UCL_onProgress(aRequest, aPosition, aTotalSize) {
2921 },
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 },
2935 onError: function UCL_onError(aRequest, aUpdate) {
2936 gRequestURL = aRequest.channel.originalURI.spec;
2937 gStatusCode = aRequest.status;
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 },
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 };
2955 /* Update download listener - nsIRequestObserver */
2956 const downloadListener = {
2957 onStartRequest: function DL_onStartRequest(aRequest, aContext) {
2958 },
2960 onProgress: function DL_onProgress(aRequest, aContext, aProgress, aMaxProgress) {
2961 },
2963 onStatus: function DL_onStatus(aRequest, aContext, aStatus, aStatusText) {
2964 },
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 },
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 };
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);
2988 if (!dir.isDirectory()) {
2989 do_throw("A file instead of a directory was specified for HttpServer " +
2990 "registerDirectory! Path: " + dir.path + "\n");
2991 }
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 }
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 }
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",
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 };
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 };
3061 var registrar = Components.manager.QueryInterface(AUS_Ci.nsIComponentRegistrar);
3062 registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo",
3063 XULAPPINFO_CONTRACTID, XULAppInfoFactory);
3064 }
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 }
3091 let appBinPath = getApplyDirFile(DIR_BIN_REL_PATH + FILE_APP_BIN, false).path;
3092 if (/ /.test(appBinPath)) {
3093 appBinPath = '"' + appBinPath + '"';
3094 }
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);
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 }
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 }
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 }
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");
3188 if (gAppTimer) {
3189 logTestInfo("start - cancel app timer");
3190 gAppTimer.cancel();
3191 gAppTimer = null;
3192 logTestInfo("finish - cancel app timer");
3193 }
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 }
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 }
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 }
3230 ds.unregisterProvider(dirProvider);
3231 cleanupTestCommon();
3233 logTestInfo("finish - unregistering directory provider");
3234 });
3235 }
3238 /**
3239 * Helper function for launching the application to apply an update.
3240 */
3241 function launchAppToApplyUpdate() {
3242 logTestInfo("start - launching application to apply update");
3244 let appBin = getApplyDirFile(DIR_BIN_REL_PATH + FILE_APP_BIN, false);
3246 if (typeof(customLaunchAppToApplyUpdate) == typeof(Function)) {
3247 customLaunchAppToApplyUpdate();
3248 }
3250 let launchBin = getLaunchBin();
3251 let args = getProcessArgs();
3252 logTestInfo("launching " + launchBin.path + " " + args.join(" "));
3254 gProcess = AUS_Cc["@mozilla.org/process/util;1"].
3255 createInstance(AUS_Ci.nsIProcess);
3256 gProcess.init(launchBin);
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);
3262 setEnvironment();
3263 logTestInfo("launching application");
3264 gProcess.runAsync(args, args.length, gProcessObserver);
3265 resetEnvironment();
3267 logTestInfo("finish - launching application to apply update");
3268 }
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 };
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 };
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 };
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;
3326 gShouldResetEnv = true;
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 }
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 }
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 }
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 }
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 }
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 }
3391 env.set("XPCOM_DEBUG_BREAK", "warn");
3393 if (gStageUpdate) {
3394 logTestInfo("setting the MOZ_UPDATE_STAGING environment variable to 1\n");
3395 env.set("MOZ_UPDATE_STAGING", "1");
3396 }
3398 logTestInfo("setting MOZ_NO_SERVICE_FALLBACK environment variable to 1");
3399 env.set("MOZ_NO_SERVICE_FALLBACK", "1");
3400 }
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;
3411 gShouldResetEnv = false;
3413 let env = AUS_Cc["@mozilla.org/process/environment;1"].
3414 getService(AUS_Ci.nsIEnvironment);
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 }
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 }
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 }
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 }
3459 if (gStageUpdate) {
3460 logTestInfo("removing the MOZ_UPDATE_STAGING environment variable\n");
3461 env.set("MOZ_UPDATE_STAGING", "");
3462 }
3464 logTestInfo("removing MOZ_NO_SERVICE_FALLBACK environment variable");
3465 env.set("MOZ_NO_SERVICE_FALLBACK", "");
3466 }