Bug 981249 - Test app launch. r=myk
authorMarco Castelluccio <mar.castelluccio@studenti.unina.it>
Thu, 27 Mar 2014 12:56:00 +0100
changeset 195414 73e20031bb0f0eee16845af8b2bf74647e3f6925
parent 195413 f01dc3be26a7ccb624c9b7c72dfe611926249437
child 195415 6d43bb23f674cda5afa35b5123c26cb96faa601a
push id486
push userasasaki@mozilla.com
push dateMon, 14 Jul 2014 18:39:42 +0000
treeherdermozilla-release@d33428174ff1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmyk
bugs981249
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 981249 - Test app launch. r=myk
testing/profiles/webapps_mochitest.json
toolkit/webapps/tests/app.sjs
toolkit/webapps/tests/chrome.ini
toolkit/webapps/tests/data/app/index.html
toolkit/webapps/tests/data/app/manifest.webapp
toolkit/webapps/tests/head.js
toolkit/webapps/tests/test_hosted.xul
toolkit/webapps/tests/test_hosted_launch.xul
toolkit/webapps/tests/test_packaged.xul
toolkit/webapps/tests/test_packaged_launch.xul
webapprt/CommandLineHandler.js
webapprt/Startup.jsm
webapprt/WebappRT.jsm
--- a/testing/profiles/webapps_mochitest.json
+++ b/testing/profiles/webapps_mochitest.json
@@ -89,10 +89,26 @@
   },
   {
     "name": "https_a_domain_privileged",
     "csp": "",
     "origin": "https://aprivileged.com",
     "manifestURL": "https://aprivileged.com/manifest.webapp",
     "description": "https://aprivileged.com Privileged App ",
     "appStatus": 2
+  },
+  {
+    "name": "test_desktop_hosted_launch",
+    "csp": "",
+    "origin": "http://127.0.0.1:8888/",
+    "manifestURL": "http://127.0.0.1:8888/sample.manifest",
+    "description": "Hosted app",
+    "appStatus": 1
+  },
+  {
+    "name": "test_desktop_packaged_launch",
+    "csp": "",
+    "origin": "app://test_desktop_packaged_launch",
+    "manifestURL": "http://127.0.0.1:8888/sample.manifest",
+    "description": "Packaged App",
+    "appStatus": 2
   }
 ]
new file mode 100644
--- /dev/null
+++ b/toolkit/webapps/tests/app.sjs
@@ -0,0 +1,33 @@
+function getQuery(request) {
+  let query = {};
+
+  request.queryString.split('&').forEach(function(val) {
+    let [name, value] = val.split('=');
+    query[name] = unescape(value);
+  });
+
+  return query;
+}
+
+function handleRequest(request, response) {
+  response.setHeader("Cache-Control", "no-cache", false);
+
+  let query = getQuery(request);
+
+  if ("appreq" in query) {
+    response.setHeader("Content-Type", "text/plain", false);
+    response.write("Hello world!");
+
+    setState("appreq", "done");
+
+    return;
+  }
+
+  if ("testreq" in query) {
+    response.setHeader("Content-Type", "text/plain", false);
+
+    response.write(getState("appreq"));
+
+    return;
+  }
+}
--- a/toolkit/webapps/tests/chrome.ini
+++ b/toolkit/webapps/tests/chrome.ini
@@ -1,8 +1,15 @@
 [DEFAULT]
 support-files =
   head.js
+  app.sjs
+  data/app/index.html
+  data/app/manifest.webapp
 
 [test_hosted.xul]
 skip-if = os == "mac"
 [test_packaged.xul]
 skip-if = os == "mac"
+[test_hosted_launch.xul]
+skip-if = os == "mac" || asan
+[test_packaged_launch.xul]
+skip-if = os == "mac" || asan
new file mode 100644
--- /dev/null
+++ b/toolkit/webapps/tests/data/app/index.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test app</title>
+</head>
+<body>
+Test app:
+<iframe src="http://127.0.0.1:8888/chrome/toolkit/webapps/tests/app.sjs?appreq"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/toolkit/webapps/tests/data/app/manifest.webapp
@@ -0,0 +1,5 @@
+{
+  "name": "Test app",
+  "description": "A test application",
+  "launch_path": "/index.html"
+}
--- a/toolkit/webapps/tests/head.js
+++ b/toolkit/webapps/tests/head.js
@@ -43,8 +43,15 @@ function wait(time) {
   let deferred = Promise.defer();
 
   setTimeout(function() {
     deferred.resolve();
   }, time);
 
   return deferred.promise;
 }
+
+// Helper to create a nsIFile from a set of path components
+function getFile() {
+  let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+  file.initWithPath(OS.Path.join.apply(OS.Path, arguments));
+  return file;
+}
--- a/toolkit/webapps/tests/test_hosted.xul
+++ b/toolkit/webapps/tests/test_hosted.xul
@@ -244,16 +244,17 @@ Task.spawn(function() {
   // Install application
   info("Test installation");
   yield nativeApp.install(manifest);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
   ok((yield checkFiles(installedFiles)), "Files correctly written");
+  is(WebappOSUtils.getInstallPath(app), installPath, "getInstallPath == installPath");
 
   let stat = yield OS.File.stat(installPath);
   let installTime = stat.lastModificationDate;
 
   // Wait one second, otherwise the last modification date is the same.
   yield wait(1000);
 
   // Reinstall application
copy from toolkit/webapps/tests/test_hosted.xul
copy to toolkit/webapps/tests/test_hosted_launch.xul
--- a/toolkit/webapps/tests/test_hosted.xul
+++ b/toolkit/webapps/tests/test_hosted_launch.xul
@@ -1,148 +1,106 @@
 <?xml version="1.0"?>
 <?xml-stylesheet type="text/css" href="chrome://global/skin"?>
 <?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
 <!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=898647
+https://bugzilla.mozilla.org/show_bug.cgi?id=981249
 -->
-<window title="Mozilla Bug 898647"
+<window title="Mozilla Bug 981249"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="head.js"/>
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
-  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=898647"
-     target="_blank">Mozilla Bug 898647</a>
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=981249"
+     target="_blank">Mozilla Bug 981249</a>
   </body>
 
 <script type="application/javascript">
 <![CDATA[
 
-/** Test for Bug 898647 **/
+/** Test for Bug 981249 **/
 
 "use strict";
 
 SimpleTest.waitForExplicitFinish();
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NativeApp.jsm");
 Cu.import("resource://gre/modules/WebappOSUtils.jsm");
+Cu.import("resource://gre/modules/Promise.jsm");
 
 let manifest = {
-  name: "Sample hosted app",
+  name: "test_desktop_hosted_launch",
+  launch_path: "/chrome/toolkit/webapps/tests/app.sjs?appreq",
 };
 
 let app = {
-  name: "Sample hosted app",
-  manifestURL: "http://example.com/sample.manifest",
+  name: "test_desktop_hosted_launch",
+  manifestURL: "http://127.0.0.1:8888/sample.manifest",
   manifest: manifest,
-  origin: "http://example.com/",
+  origin: "http://127.0.0.1:8888/",
   categories: [],
-  installOrigin: "http://example.com/",
+  installOrigin: "http://127.0.0.1:8888/",
   receipts: [],
   installTime: Date.now(),
 };
 
 let profileDir;
-let profilesIni;
 let installPath;
-
-let installedFiles;
-let tempUpdatedFiles;
-let updatedFiles;
+let exePath;
+let appProcess = Cc["@mozilla.org/process/util;1"].
+                 createInstance(Ci.nsIProcess);
 
 let cleanup;
 
 if (navigator.platform.startsWith("Linux")) {
   installPath = OS.Path.join(OS.Constants.Path.homeDir, "." + WebappOSUtils.getUniqueName(app));
+  exePath = OS.Path.join(installPath, "webapprt-stub");
 
   let xdg_data_home = Cc["@mozilla.org/process/environment;1"].
                       getService(Ci.nsIEnvironment).
                       get("XDG_DATA_HOME");
   if (!xdg_data_home) {
     xdg_data_home = OS.Path.join(OS.Constants.Path.homeDir, ".local", "share");
   }
 
   let desktopINI = OS.Path.join(xdg_data_home, "applications",
                                 "owa-" + WebappOSUtils.getUniqueName(app) + ".desktop");
 
-  installedFiles = [
-    OS.Path.join(installPath, "icon.png"),
-    OS.Path.join(installPath, "webapprt-stub"),
-    OS.Path.join(installPath, "webapp.json"),
-    OS.Path.join(installPath, "webapp.ini"),
-    desktopINI,
-  ];
-  tempUpdatedFiles = [
-    OS.Path.join(installPath, "update", "icon.png"),
-    OS.Path.join(installPath, "update", "webapp.json"),
-    OS.Path.join(installPath, "update", "webapp.ini"),
-  ];
-  updatedFiles = [
-    OS.Path.join(installPath, "icon.png"),
-    OS.Path.join(installPath, "webapp.json"),
-    OS.Path.join(installPath, "webapp.ini"),
-    desktopINI,
-  ];
-
-  profilesIni = OS.Path.join(installPath, "profiles.ini");
-
   cleanup = function() {
     return Task.spawn(function*() {
+      if (appProcess.isRunning) {
+        appProcess.kill();
+      }
+
       if (profileDir) {
         yield OS.File.removeDir(profileDir.parent.path, { ignoreAbsent: true });
       }
 
       yield OS.File.removeDir(installPath, { ignoreAbsent: true });
 
       yield OS.File.remove(desktopINI, { ignoreAbsent: true });
     });
   };
 } else if (navigator.platform.startsWith("Win")) {
   installPath = OS.Path.join(OS.Constants.Path.winAppDataDir, WebappOSUtils.getUniqueName(app));
-
-  let desktopShortcut = OS.Path.join(OS.Constants.Path.desktopDir, "Sample hosted app.lnk");
-  let startMenuShortcut = OS.Path.join(OS.Constants.Path.winStartMenuProgsDir, "Sample hosted app.lnk");
+  exePath = OS.Path.join(installPath, "test_desktop_hosted_launch.exe");
 
-  installedFiles = [
-    OS.Path.join(installPath, "Sample hosted app.exe"),
-    OS.Path.join(installPath, "chrome", "icons", "default", "default.ico"),
-    OS.Path.join(installPath, "webapp.json"),
-    OS.Path.join(installPath, "webapp.ini"),
-    OS.Path.join(installPath, "uninstall", "shortcuts_log.ini"),
-    OS.Path.join(installPath, "uninstall", "uninstall.log"),
-    OS.Path.join(installPath, "uninstall", "webapp-uninstaller.exe"),
-    desktopShortcut,
-    startMenuShortcut,
-  ];
-  tempUpdatedFiles = [
-    OS.Path.join(installPath, "update", "chrome", "icons", "default", "default.ico"),
-    OS.Path.join(installPath, "update", "webapp.json"),
-    OS.Path.join(installPath, "update", "webapp.ini"),
-    OS.Path.join(installPath, "update", "uninstall", "shortcuts_log.ini"),
-    OS.Path.join(installPath, "update", "uninstall", "uninstall.log"),
-    OS.Path.join(installPath, "update", "uninstall", "webapp-uninstaller.exe"),
-  ];
-  updatedFiles = [
-    OS.Path.join(installPath, "chrome", "icons", "default", "default.ico"),
-    OS.Path.join(installPath, "webapp.json"),
-    OS.Path.join(installPath, "webapp.ini"),
-    OS.Path.join(installPath, "uninstall", "shortcuts_log.ini"),
-    OS.Path.join(installPath, "uninstall", "uninstall.log"),
-    desktopShortcut,
-    startMenuShortcut,
-  ];
-
-  profilesIni = OS.Path.join(installPath, "profiles.ini");
+  let desktopShortcut = OS.Path.join(OS.Constants.Path.desktopDir, "test_desktop_hosted_launch.lnk");
+  let startMenuShortcut = OS.Path.join(OS.Constants.Path.winStartMenuProgsDir, "test_desktop_hosted_launch.lnk");
 
   cleanup = function() {
     return Task.spawn(function*() {
+      if (appProcess.isRunning) {
+        appProcess.kill();
+      }
+
       let uninstallKey;
       try {
         uninstallKey = Cc["@mozilla.org/windows-registry-key;1"].
                        createInstance(Ci.nsIWindowsRegKey);
         uninstallKey.open(uninstallKey.ROOT_KEY_CURRENT_USER,
                           "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
                           uninstallKey.ACCESS_WRITE);
         if (uninstallKey.hasChild(WebappOSUtils.getUniqueName(app))) {
@@ -161,44 +119,28 @@ if (navigator.platform.startsWith("Linux
 
       yield OS.File.removeDir(installPath, { ignoreAbsent: true });
 
       yield OS.File.remove(desktopShortcut, { ignoreAbsent: true });
       yield OS.File.remove(startMenuShortcut, { ignoreAbsent: true });
     });
   };
 } else if (navigator.platform.startsWith("Mac")) {
-  installPath = OS.Path.join(OS.Constants.Path.macLocalApplicationsDir, "Sample hosted app.app");
+  installPath = OS.Path.join(OS.Constants.Path.macLocalApplicationsDir, "test_desktop_hosted_launch.app");
+  exePath = OS.Path.join(installPath, "Contents", "MacOS", "webapprt");
+
   let appProfileDir = OS.Path.join(OS.Constants.Path.macUserLibDir, "Application Support",
                                    WebappOSUtils.getUniqueName(app));
 
-  installedFiles = [
-    OS.Path.join(installPath, "Contents", "Info.plist"),
-    OS.Path.join(installPath, "Contents", "MacOS", "webapprt"),
-    OS.Path.join(installPath, "Contents", "MacOS", "webapp.ini"),
-    OS.Path.join(installPath, "Contents", "Resources", "appicon.icns"),
-    OS.Path.join(appProfileDir, "webapp.json"),
-  ];
-  tempUpdatedFiles = [
-    OS.Path.join(installPath, "update", "Contents", "Info.plist"),
-    OS.Path.join(installPath, "update", "Contents", "MacOS", "webapp.ini"),
-    OS.Path.join(installPath, "update", "Contents", "Resources", "appicon.icns"),
-    OS.Path.join(installPath, "update", "webapp.json")
-  ];
-  updatedFiles = [
-    OS.Path.join(installPath, "Contents", "Info.plist"),
-    OS.Path.join(installPath, "Contents", "MacOS", "webapp.ini"),
-    OS.Path.join(installPath, "Contents", "Resources", "appicon.icns"),
-    OS.Path.join(appProfileDir, "webapp.json"),
-  ];
-
-  profilesIni = OS.Path.join(appProfileDir, "profiles.ini");
-
   cleanup = function() {
     return Task.spawn(function*() {
+      if (appProcess.isRunning) {
+        appProcess.kill();
+      }
+
       if (profileDir) {
         yield OS.File.removeDir(profileDir.parent.path, { ignoreAbsent: true });
       }
 
       yield OS.File.removeDir(installPath, { ignoreAbsent: true });
 
       yield OS.File.removeDir(appProfileDir, { ignoreAbsent: true });
     });
@@ -217,93 +159,66 @@ SimpleTest.registerCleanupFunction(funct
     Services.prefs.clearUserPref("browser.mozApps.installer.dry_run");
   } else {
     Services.prefs.setBoolPref("browser.mozApps.installer.dry_run", old_dry_run);
   }
 
   cleanup();
 });
 
-Task.spawn(function() {
+function wasAppSJSAccessed() {
+  let deferred = Promise.defer();
+
+  var xhr = new XMLHttpRequest();
+
+  xhr.addEventListener("load", function() {
+    let ret = (xhr.responseText == "done") ? true : false;
+    deferred.resolve(ret);
+  });
+
+  xhr.addEventListener("error", aError => deferred.reject(aError));
+  xhr.addEventListener("abort", aError => deferred.reject(aError));
+
+  xhr.open('GET', 'http://test/chrome/toolkit/webapps/tests/app.sjs?testreq', true);
+  xhr.send();
+
+  return deferred.promise;
+}
+
+Task.spawn(function*() {
   // Get to a clean state before the test
   yield cleanup();
 
   let nativeApp = new NativeApp(app, manifest, app.categories);
   ok(nativeApp, "NativeApp object created");
 
-  info("Test update for an uninstalled application");
-  try {
-    yield nativeApp.prepareUpdate(manifest);
-    ok(false, "Didn't thrown");
-  } catch (ex) {
-    is(ex, "The application isn't installed", "Exception thrown");
-  }
-
   profileDir = nativeApp.createProfile();
   ok(profileDir && profileDir.exists(), "Profile directory created");
-  ok((yield OS.File.exists(profilesIni)), "profiles.ini file created");
 
   // Install application
   info("Test installation");
   yield nativeApp.install(manifest);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Files correctly written");
 
-  let stat = yield OS.File.stat(installPath);
-  let installTime = stat.lastModificationDate;
+  let exeFile = getFile(exePath);
 
-  // Wait one second, otherwise the last modification date is the same.
-  yield wait(1000);
+  ok(exeFile.isExecutable(), "webapprt executable is executable");
 
-  // Reinstall application
-  info("Test reinstallation");
-  yield nativeApp.install(manifest);
-  while (!WebappOSUtils.isLaunchable(app)) {
-    yield wait(1000);
-  }
-  ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Installation not corrupted");
-  ok((yield checkFiles(tempUpdatedFiles)), "Files correctly written in the update subdirectory");
+  let appClosed = false;
 
-  yield nativeApp.applyUpdate();
-  while (!WebappOSUtils.isLaunchable(app)) {
+  appProcess.init(exeFile)
+  appProcess.runAsync([], 0, () => appClosed = true);
+
+  while (!(yield wasAppSJSAccessed()) && !appClosed) {
     yield wait(1000);
   }
-  ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Installation not corrupted");
-  ok(!(yield OS.File.exists(OS.Path.join(installPath, "update"))), "Update directory removed");
-  ok((yield checkDateHigherThan(updatedFiles, installTime)), "Modification date higher");
-
-  stat = yield OS.File.stat(installPath);
-  installTime = stat.lastModificationDate;
-
-  // Wait one second, otherwise the last modification date is the same.
-  yield wait(1000);
-
-  // Update application
-  info("Test update");
-  yield nativeApp.prepareUpdate(manifest);
-  while (!WebappOSUtils.isLaunchable(app)) {
-    yield wait(1000);
-  }
-  ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Installation not corrupted");
-  ok((yield checkFiles(tempUpdatedFiles)), "Files correctly written in the update subdirectory");
-
-  yield nativeApp.applyUpdate();
-  while (!WebappOSUtils.isLaunchable(app)) {
-    yield wait(1000);
-  }
-  ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Installation not corrupted");
-  ok(!(yield OS.File.exists(OS.Path.join(installPath, "update"))), "Update directory removed");
-  ok((yield checkDateHigherThan(updatedFiles, installTime)), "Modification date higher");
+  ok(!appClosed, "App was launched and is still running");
 
   SimpleTest.finish();
 }).then(null, function(e) {
   ok(false, "Error during test: " + e);
   SimpleTest.finish();
 });
 
 ]]>
--- a/toolkit/webapps/tests/test_packaged.xul
+++ b/toolkit/webapps/tests/test_packaged.xul
@@ -262,16 +262,17 @@ Task.spawn(function() {
   // Install application
   info("Test installation");
   yield nativeApp.install(manifest, zipPath);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
   ok((yield checkFiles(installedFiles)), "Files correctly written");
+  is(WebappOSUtils.getInstallPath(app), installPath, "getInstallPath == installPath");
 
   let stat = yield OS.File.stat(installPath);
   let installTime = stat.lastModificationDate;
 
   // Wait one second, otherwise the last modification date is the same.
   yield wait(1000);
 
   // Reinstall application
copy from toolkit/webapps/tests/test_packaged.xul
copy to toolkit/webapps/tests/test_packaged_launch.xul
--- a/toolkit/webapps/tests/test_packaged.xul
+++ b/toolkit/webapps/tests/test_packaged_launch.xul
@@ -1,160 +1,116 @@
 <?xml version="1.0"?>
 <?xml-stylesheet type="text/css" href="chrome://global/skin"?>
 <?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
 <!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=898647
+https://bugzilla.mozilla.org/show_bug.cgi?id=981249
 -->
-<window title="Mozilla Bug 898647"
+<window title="Mozilla Bug 981249"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/chrome-harness.js"></script>
   <script type="application/javascript" src="head.js"/>
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
-  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=898647"
-     target="_blank">Mozilla Bug 898647</a>
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=981249"
+     target="_blank">Mozilla Bug 981249</a>
   </body>
 
 <script type="application/javascript">
 <![CDATA[
 
-/** Test for Bug 898647 **/
+/** Test for Bug 981249 **/
 
 "use strict";
 
 SimpleTest.waitForExplicitFinish();
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NativeApp.jsm");
 Cu.import("resource://gre/modules/WebappOSUtils.jsm");
+Cu.import("resource://gre/modules/Promise.jsm");
 
-let zipPath = OS.Path.join(OS.Constants.Path.profileDir, "sample.zip");
+const PR_RDWR        = 0x04;
+const PR_CREATE_FILE = 0x08;
+const PR_TRUNCATE    = 0x20;
 
 let manifest = {
-  name: "Sample packaged app",
+  name: "test_desktop_packaged_launch",
   version: "0.1a",
   size: 777,
-  package_path: "/sample.zip",
+  package_path: "/data/app.zip",
+  launch_path: "/index.html",
 };
 
 let app = {
-  name: "Sample packaged app",
-  manifestURL: "http://example.com/sample.manifest",
+  name: "test_desktop_packaged_launch",
+  manifestURL: "http://127.0.0.1:8888/sample.manifest",
   manifest: manifest,
   updateManifest: manifest,
-  origin: "http://example.com/",
+  origin: "app://test_desktop_packaged_launch/",
   categories: [],
-  installOrigin: "http://example.com/",
+  installOrigin: "http://127.0.0.1:8888/",
   receipts: [],
   installTime: Date.now(),
 };
 
 let profileDir;
-let profilesIni;
 let installPath;
-
-let installedFiles;
-let tempUpdatedFiles;
-let updatedFiles;
+let exePath;
+let appProcess = Cc["@mozilla.org/process/util;1"].
+                 createInstance(Ci.nsIProcess);
 
 let cleanup;
 
 if (navigator.platform.startsWith("Linux")) {
   installPath = OS.Path.join(OS.Constants.Path.homeDir, "." + WebappOSUtils.getUniqueName(app));
+  exePath = OS.Path.join(installPath, "webapprt-stub");
 
   let xdg_data_home = Cc["@mozilla.org/process/environment;1"].
                       getService(Ci.nsIEnvironment).
                       get("XDG_DATA_HOME");
   if (!xdg_data_home) {
     xdg_data_home = OS.Path.join(OS.Constants.Path.homeDir, ".local", "share");
   }
 
   let desktopINI = OS.Path.join(xdg_data_home, "applications",
                                 "owa-" + WebappOSUtils.getUniqueName(app) + ".desktop");
 
-  installedFiles = [
-    OS.Path.join(installPath, "icon.png"),
-    OS.Path.join(installPath, "webapprt-stub"),
-    OS.Path.join(installPath, "webapp.json"),
-    OS.Path.join(installPath, "webapp.ini"),
-    OS.Path.join(installPath, "application.zip"),
-    desktopINI,
-  ];
-  tempUpdatedFiles = [
-    OS.Path.join(installPath, "update", "icon.png"),
-    OS.Path.join(installPath, "update", "webapp.json"),
-    OS.Path.join(installPath, "update", "webapp.ini"),
-    OS.Path.join(installPath, "update", "application.zip"),
-  ];
-  updatedFiles = [
-    OS.Path.join(installPath, "icon.png"),
-    OS.Path.join(installPath, "webapp.json"),
-    OS.Path.join(installPath, "webapp.ini"),
-    OS.Path.join(installPath, "application.zip"),
-    desktopINI,
-  ];
-
-  profilesIni = OS.Path.join(installPath, "profiles.ini");
-
   cleanup = function() {
     return Task.spawn(function*() {
+      if (appProcess.isRunning) {
+        appProcess.kill();
+      }
+
       if (profileDir) {
         yield OS.File.removeDir(profileDir.parent.path, { ignoreAbsent: true });
       }
 
       yield OS.File.removeDir(installPath, { ignoreAbsent: true });
 
       yield OS.File.remove(desktopINI, { ignoreAbsent: true });
     });
   };
 } else if (navigator.platform.startsWith("Win")) {
   installPath = OS.Path.join(OS.Constants.Path.winAppDataDir, WebappOSUtils.getUniqueName(app));
-
-  let desktopShortcut = OS.Path.join(OS.Constants.Path.desktopDir, "Sample packaged app.lnk");
-  let startMenuShortcut = OS.Path.join(OS.Constants.Path.winStartMenuProgsDir, "Sample packaged app.lnk");
+  exePath = OS.Path.join(installPath, "test_desktop_packaged_launch.exe");
 
-  installedFiles = [
-    OS.Path.join(installPath, "Sample packaged app.exe"),
-    OS.Path.join(installPath, "chrome", "icons", "default", "default.ico"),
-    OS.Path.join(installPath, "webapp.json"),
-    OS.Path.join(installPath, "webapp.ini"),
-    OS.Path.join(installPath, "application.zip"),
-    OS.Path.join(installPath, "uninstall", "shortcuts_log.ini"),
-    OS.Path.join(installPath, "uninstall", "uninstall.log"),
-    OS.Path.join(installPath, "uninstall", "webapp-uninstaller.exe"),
-    desktopShortcut,
-    startMenuShortcut,
-  ];
-  tempUpdatedFiles = [
-    OS.Path.join(installPath, "update", "chrome", "icons", "default", "default.ico"),
-    OS.Path.join(installPath, "update", "webapp.json"),
-    OS.Path.join(installPath, "update", "webapp.ini"),
-    OS.Path.join(installPath, "update", "application.zip"),
-    OS.Path.join(installPath, "update", "uninstall", "shortcuts_log.ini"),
-    OS.Path.join(installPath, "update", "uninstall", "uninstall.log"),
-    OS.Path.join(installPath, "update", "uninstall", "webapp-uninstaller.exe"),
-  ];
-  updatedFiles = [
-    OS.Path.join(installPath, "chrome", "icons", "default", "default.ico"),
-    OS.Path.join(installPath, "webapp.json"),
-    OS.Path.join(installPath, "webapp.ini"),
-    OS.Path.join(installPath, "application.zip"),
-    OS.Path.join(installPath, "uninstall", "shortcuts_log.ini"),
-    OS.Path.join(installPath, "uninstall", "uninstall.log"),
-    desktopShortcut,
-    startMenuShortcut,
-  ];
-
-  profilesIni = OS.Path.join(installPath, "profiles.ini");
+  let desktopShortcut = OS.Path.join(OS.Constants.Path.desktopDir, "test_desktop_packaged_launch.lnk");
+  let startMenuShortcut = OS.Path.join(OS.Constants.Path.winStartMenuProgsDir, "test_desktop_packaged_launch.lnk");
 
   cleanup = function() {
     return Task.spawn(function*() {
+      if (appProcess.isRunning) {
+        appProcess.kill();
+      }
+
       let uninstallKey;
       try {
         uninstallKey = Cc["@mozilla.org/windows-registry-key;1"].
                        createInstance(Ci.nsIWindowsRegKey);
         uninstallKey.open(uninstallKey.ROOT_KEY_CURRENT_USER,
                           "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
                           uninstallKey.ACCESS_WRITE);
         if (uninstallKey.hasChild(WebappOSUtils.getUniqueName(app))) {
@@ -173,47 +129,28 @@ if (navigator.platform.startsWith("Linux
 
       yield OS.File.removeDir(installPath, { ignoreAbsent: true });
 
       yield OS.File.remove(desktopShortcut, { ignoreAbsent: true });
       yield OS.File.remove(startMenuShortcut, { ignoreAbsent: true });
     });
   };
 } else if (navigator.platform.startsWith("Mac")) {
-  installPath = OS.Path.join(OS.Constants.Path.macLocalApplicationsDir, "Sample packaged app.app");
+  installPath = OS.Path.join(OS.Constants.Path.macLocalApplicationsDir, "test_desktop_packaged_launch.app");
+  exePath = OS.Path.join(installPath, "Contents", "MacOS", "webapprt");
+
   let appProfileDir = OS.Path.join(OS.Constants.Path.macUserLibDir, "Application Support",
                                    WebappOSUtils.getUniqueName(app));
 
-  installedFiles = [
-    OS.Path.join(installPath, "Contents", "Info.plist"),
-    OS.Path.join(installPath, "Contents", "MacOS", "webapprt"),
-    OS.Path.join(installPath, "Contents", "MacOS", "webapp.ini"),
-    OS.Path.join(installPath, "Contents", "Resources", "appicon.icns"),
-    OS.Path.join(installPath, "Contents", "Resources", "application.zip"),
-    OS.Path.join(appProfileDir, "webapp.json"),
-  ];
-  tempUpdatedFiles = [
-    OS.Path.join(installPath, "update", "Contents", "Info.plist"),
-    OS.Path.join(installPath, "update", "Contents", "MacOS", "webapp.ini"),
-    OS.Path.join(installPath, "update", "Contents", "Resources", "appicon.icns"),
-    OS.Path.join(installPath, "update", "Contents", "Resources", "application.zip"),
-    OS.Path.join(installPath, "update", "webapp.json"),
-  ];
-  updatedFiles = [
-    OS.Path.join(installPath, "Contents", "Info.plist"),
-    OS.Path.join(installPath, "Contents", "MacOS", "webapp.ini"),
-    OS.Path.join(installPath, "Contents", "Resources", "appicon.icns"),
-    OS.Path.join(installPath, "Contents", "Resources", "application.zip"),
-    OS.Path.join(appProfileDir, "webapp.json"),
-  ];
-
-  profilesIni = OS.Path.join(appProfileDir, "profiles.ini");
-
   cleanup = function() {
     return Task.spawn(function*() {
+      if (appProcess.isRunning) {
+        appProcess.kill();
+      }
+
       if (profileDir) {
         yield OS.File.removeDir(profileDir.parent.path, { ignoreAbsent: true });
       }
 
       yield OS.File.removeDir(installPath, { ignoreAbsent: true });
 
       yield OS.File.removeDir(appProfileDir, { ignoreAbsent: true });
     });
@@ -232,104 +169,86 @@ SimpleTest.registerCleanupFunction(funct
     Services.prefs.clearUserPref("browser.mozApps.installer.dry_run");
   } else {
     Services.prefs.setBoolPref("browser.mozApps.installer.dry_run", old_dry_run);
   }
 
   cleanup();
 });
 
+function buildAppPackage() {
+  let zipFile = getFile(OS.Constants.Path.profileDir, "sample.zip");
+
+  let zipWriter = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter);
+  zipWriter.open(zipFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+  zipWriter.addEntryFile("index.html",
+                         Ci.nsIZipWriter.COMPRESSION_NONE,
+                         getFile(getTestFilePath("data/app/index.html")),
+                         false);
+  zipWriter.addEntryFile("manifest.webapp",
+                         Ci.nsIZipWriter.COMPRESSION_NONE,
+                         getFile(getTestFilePath("data/app/manifest.webapp")),
+                         false);
+  zipWriter.close();
+
+  return zipFile.path;
+}
+
+function wasAppSJSAccessed() {
+  let deferred = Promise.defer();
+
+  var xhr = new XMLHttpRequest();
+
+  xhr.addEventListener("load", function() {
+    let ret = (xhr.responseText == "done") ? true : false;
+    deferred.resolve(ret);
+  });
+
+  xhr.addEventListener("error", aError => deferred.reject(aError));
+  xhr.addEventListener("abort", aError => deferred.reject(aError));
+
+  xhr.open('GET', 'http://test/chrome/toolkit/webapps/tests/app.sjs?testreq', true);
+  xhr.send();
+
+  return deferred.promise;
+}
+
 Task.spawn(function() {
   // Get to a clean state before the test
   yield cleanup();
 
-  let zipFile = yield OS.File.open(zipPath, { create: true });
-  yield zipFile.close();
+  let zipPath = buildAppPackage();
 
   let nativeApp = new NativeApp(app, manifest, app.categories);
   ok(nativeApp, "NativeApp object created");
 
-  info("Test update for an application that isn't installed");
-  try {
-    yield nativeApp.prepareUpdate(manifest, zipPath);
-    ok(false, "Didn't thrown");
-  } catch (ex) {
-    is(ex, "The application isn't installed", "Exception thrown");
-  }
-
   profileDir = nativeApp.createProfile();
   ok(profileDir && profileDir.exists(), "Profile directory created");
-  ok((yield OS.File.exists(profilesIni)), "profiles.ini file created");
 
   // Install application
   info("Test installation");
   yield nativeApp.install(manifest, zipPath);
   while (!WebappOSUtils.isLaunchable(app)) {
     yield wait(1000);
   }
   ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Files correctly written");
-
-  let stat = yield OS.File.stat(installPath);
-  let installTime = stat.lastModificationDate;
 
-  // Wait one second, otherwise the last modification date is the same.
-  yield wait(1000);
+  let exeFile = getFile(exePath);
 
-  // Reinstall application
-  info("Test reinstallation");
+  ok(exeFile.isExecutable(), "webapprt executable is executable");
 
-  zipFile = yield OS.File.open(zipPath, { create: true });
-  yield zipFile.close();
+  let appClosed = false;
 
-  yield nativeApp.install(manifest, zipPath);
-  while (!WebappOSUtils.isLaunchable(app)) {
-    yield wait(1000);
-  }
-  ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Installation not corrupted");
-  ok((yield checkFiles(tempUpdatedFiles)), "Files correctly written in the update subdirectory");
+  appProcess.init(exeFile)
+  appProcess.runAsync([], 0, () => appClosed = true);
 
-  yield nativeApp.applyUpdate();
-  while (!WebappOSUtils.isLaunchable(app)) {
+  while (!(yield wasAppSJSAccessed()) && !appClosed) {
     yield wait(1000);
   }
-  ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Installation not corrupted");
-  ok(!(yield OS.File.exists(OS.Path.join(installPath, "update"))), "Update directory removed");
-  ok((yield checkDateHigherThan(updatedFiles, installTime)), "Modification date higher");
-
-  stat = yield OS.File.stat(installPath);
-  installTime = stat.lastModificationDate;
-
-  // Wait one second, otherwise the last modification date is the same.
-  yield wait(1000);
-
-  // Update application
-  info("Test update");
-
-  zipFile = yield OS.File.open(zipPath, { create: true });
-  yield zipFile.close();
-
-  yield nativeApp.prepareUpdate(manifest, zipPath);
-  while (!WebappOSUtils.isLaunchable(app)) {
-    yield wait(1000);
-  }
-  ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Installation not corrupted");
-  ok((yield checkFiles(tempUpdatedFiles)), "Files correctly written in the update subdirectory");
-
-  yield nativeApp.applyUpdate();
-  while (!WebappOSUtils.isLaunchable(app)) {
-    yield wait(1000);
-  }
-  ok(true, "App launchable");
-  ok((yield checkFiles(installedFiles)), "Installation not corrupted");
-  ok(!(yield OS.File.exists(OS.Path.join(installPath, "update"))), "Update directory removed");
-  ok((yield checkDateHigherThan(updatedFiles, installTime)), "Modification date higher");
+  ok(!appClosed, "App was launched and is still running");
 
   SimpleTest.finish();
 }).then(null, function(e) {
   ok(false, "Error during test: " + e);
   SimpleTest.finish();
 });
 
 ]]>
--- a/webapprt/CommandLineHandler.js
+++ b/webapprt/CommandLineHandler.js
@@ -38,17 +38,20 @@ CommandLineHandler.prototype = {
       // We're opening the window here in order to show it as soon as possible.
       let window = Services.ww.openWindow(null,
                                           "chrome://webapprt/content/webapp.xul",
                                           "_blank",
                                           "chrome,dialog=no,resizable,scrollbars,centerscreen",
                                           null);
       // Load the module to start up the app
       Cu.import("resource://webapprt/modules/Startup.jsm");
-      startup(window);
+      startup(window).then(null, function (aError) {
+        dump("Error: " + aError + "\n");
+        Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit);
+      });
     }
   },
 
   /**
    * Handle debug command line option.
    *
    * @param cmdLine A nsICommandLine object.
    *
--- a/webapprt/Startup.jsm
+++ b/webapprt/Startup.jsm
@@ -141,10 +141,10 @@ this.startup = function(window) {
       appBrowser.addEventListener("load", function onLoad() {
         appBrowser.removeEventListener("load", onLoad, true);
         appBrowser.contentDocument.
           documentElement.mozRequestFullScreen();
       }, true);
     }
 
     WebappRT.startUpdateService();
-  }).then(null, Cu.reportError.bind(Cu));
+  });
 }
--- a/webapprt/WebappRT.jsm
+++ b/webapprt/WebappRT.jsm
@@ -25,18 +25,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   'resource://gre/modules/NativeApp.jsm');
 
 XPCOMUtils.defineLazyServiceGetter(this, "appsService",
                                   "@mozilla.org/AppsService;1",
                                   "nsIAppsService");
 
 this.WebappRT = {
   get launchURI() {
-    let manifest = this.localeManifest;
-    return manifest.fullLaunchPath();
+    return this.localeManifest.fullLaunchPath();
   },
 
   get localeManifest() {
     return new ManifestHelper(this.config.app.manifest,
                               this.config.app.origin);
   },
 
   get appID() {