Bug 1239330 - Support AddonManager.installTemporaryAddon() in marionette_driver.addons, r?ato draft
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Wed, 13 Jan 2016 10:37:00 -0500
changeset 322390 5a628de63d2d49c0d628e1ae7d5f5711aeb147c1
parent 322000 02398f2be72b9bbf5ee79348b73ef122c915aae0
child 513098 bfc81f8efbd4c0e08e76e2809a851ee86d2c88b3
push id9601
push userahalberstadt@mozilla.com
push dateMon, 18 Jan 2016 14:45:42 +0000
reviewersato
bugs1239330
milestone46.0a1
Bug 1239330 - Support AddonManager.installTemporaryAddon() in marionette_driver.addons, r?ato
testing/marionette/client/marionette/tests/unit/mn-restartless-unsigned.xpi
testing/marionette/client/marionette/tests/unit/mn-restartless.xpi
testing/marionette/client/marionette/tests/unit/test_addons.py
testing/marionette/driver/marionette_driver/addons.py
testing/marionette/driver/marionette_driver/marionette.py
rename from testing/marionette/client/marionette/tests/unit/mn-restartless.xpi
rename to testing/marionette/client/marionette/tests/unit/mn-restartless-unsigned.xpi
--- a/testing/marionette/client/marionette/tests/unit/test_addons.py
+++ b/testing/marionette/client/marionette/tests/unit/test_addons.py
@@ -1,25 +1,25 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import os
+import unittest
 
 from marionette import MarionetteTestCase
 from marionette_driver.addons import Addons, AddonInstallException
 
 here = os.path.abspath(os.path.dirname(__file__))
 
 
 class TestAddons(MarionetteTestCase):
     def setUp(self):
         MarionetteTestCase.setUp(self)
         self.addons = Addons(self.marionette)
-        self.addon_path = os.path.join(here, 'mn-restartless.xpi')
 
 
     @property
     def all_addon_ids(self):
         with self.marionette.using_context('chrome'):
             addons = self.marionette.execute_async_script("""
               Components.utils.import("resource://gre/modules/AddonManager.jsm");
               AddonManager.getAllAddons(function(addons){
@@ -27,24 +27,32 @@ class TestAddons(MarionetteTestCase):
                   return x.id;
                 });
                 marionetteScriptFinished(ids);
               });
             """)
 
         return addons
 
-    def test_install_and_remove_unsigned_addon(self):
-        self.addons.signature_required = False
+    def test_install_and_remove_temporary_unsigned_addon(self):
+        addon_path = os.path.join(here, 'mn-restartless-unsigned.xpi')
 
-        addon_id = self.addons.install(self.addon_path)
+        addon_id = self.addons.install(addon_path, temp=True)
         self.assertIn(addon_id, self.all_addon_ids)
 
         self.addons.uninstall(addon_id)
         self.assertNotIn(addon_id, self.all_addon_ids)
 
-        self.addons.signature_required = True
-
-    def test_install_unsigned_addon_with_signature_required(self):
-        self.signature_required = True
+    def test_install_unsigned_addon(self):
+        addon_path = os.path.join(here, 'mn-restartless-unsigned.xpi')
 
         with self.assertRaises(AddonInstallException):
-            self.addons.install(self.addon_path)
+            self.addons.install(addon_path)
+
+    @unittest.skip("need to get the test extension signed")
+    def test_install_and_remove_signed_addon(self):
+        addon_path = os.path.join(here, 'mn-restartless-signed.xpi')
+
+        addon_id = self.addons.install(addon_path)
+        self.assertIn(addon_id, self.all_addon_ids)
+
+        self.addons.uninstall(addon_id)
+        self.assertNotIn(addon_id, self.all_addon_ids)
--- a/testing/marionette/driver/marionette_driver/addons.py
+++ b/testing/marionette/driver/marionette_driver/addons.py
@@ -32,68 +32,65 @@ class Addons(object):
         addons = Addons(marionette)
         addons.install('path/to/extension.xpi')
 
     .. _AddonManager API: https://developer.mozilla.org/en-US/Add-ons/Add-on_Manager
     """
 
     def __init__(self, marionette):
         self._mn = marionette
-        self._signature_required = True
 
-        def on_restart():
-            self.signature_required = self._signature_required
-        self._mn.restart_handlers.append(on_restart)
-
-    @property
-    def signature_required(self):
-        """
-        Whether or not addons must be signed.
-        """
-        return self._signature_required
-
-    @signature_required.setter
-    def signature_required(self, val):
-        self._mn.set_pref('xpinstall.signatures.required', val)
-        self._signature_required = val
-
-    def install(self, path):
+    def install(self, path, temp=False):
         """Install an addon.
 
         If the addon is restartless, it can be used right away. Otherwise
         a restart using :func:`~marionette_driver.marionette.Marionette.restart`
         will be needed.
 
         :param path: A file path to the extension to be installed.
+        :param temp: Install a temporary addon. Temporary addons will
+                     automatically be uninstalled on shutdown and do not need
+                     to be signed, though they must be restartless.
         :returns: The addon ID string of the newly installed addon.
         :raises: :exc:`AddonInstallException`
         """
         with self._mn.using_context('chrome'):
             addon_id, status = self._mn.execute_async_script("""
               let FileUtils = Components.utils.import("resource://gre/modules/FileUtils.jsm").FileUtils;
               Components.utils.import("resource://gre/modules/AddonManager.jsm");
               let listener = {
                 onInstallEnded: function(install, addon) {
                   marionetteScriptFinished([addon.id, 0]);
                 },
 
                 onInstallFailed: function(install) {
                   marionetteScriptFinished([null, install.error]);
+                },
+
+                onInstalled: function(addon) {
+                  marionetteScriptFinished([addon.id, 0]);
                 }
               }
 
               let file = new FileUtils.File(arguments[0]);
-              AddonManager.getInstallForFile(file, function(aInstall) {
-                if (aInstall.error != 0) {
-                  marionetteScriptFinished([null, aInstall.error]);
-                }
-                aInstall.addListener(listener);
-                aInstall.install();
-              });
-            """, script_args=[path], debug_script=True)
+              let temp = arguments[1];
+
+              if (!temp) {
+                AddonManager.getInstallForFile(file, function(aInstall) {
+                  if (aInstall.error != 0) {
+                    marionetteScriptFinished([null, aInstall.error]);
+                  }
+                  aInstall.addListener(listener);
+                  aInstall.install();
+                });
+              } else {
+                AddonManager.addAddonListener(listener);
+                AddonManager.installTemporaryAddon(file);
+              }
+            """, script_args=[path, temp], debug_script=True)
 
         if status:
             if status in ADDON_INSTALL_ERRORS:
                 raise AddonInstallException(ADDON_INSTALL_ERRORS[status])
             raise AddonInstallException("Addon failed to install with return code: %d" % status)
         return addon_id
 
     def uninstall(self, addon_id):
--- a/testing/marionette/driver/marionette_driver/marionette.py
+++ b/testing/marionette/driver/marionette_driver/marionette.py
@@ -559,17 +559,16 @@ class Marionette(object):
         self.baseurl = baseurl
         self.no_window = no_window
         self._test_name = None
         self.timeout = timeout
         self.socket_timeout = socket_timeout
         self.device_serial = device_serial
         self.adb_host = adb_host
         self.adb_port = adb_port
-        self.restart_handlers = []
 
         startup_timeout = startup_timeout or self.DEFAULT_STARTUP_TIMEOUT
 
         if bin:
             port = int(self.port)
             if not Marionette.is_port_available(port, host=self.host):
                 ex_msg = "%s:%d is unavailable." % (self.host, port)
                 raise errors.MarionetteException(message=ex_msg)
@@ -1140,21 +1139,16 @@ class Marionette(object):
             self.instance.detached = True
         else:
             self.delete_session()
             self.instance.restart(clean=clean)
         self.raise_for_port(self.wait_for_port())
         self.start_session(session_id=self.session_id)
         self._reset_timeouts()
 
-        # Give consumers who depended on the old session a
-        # chance to re-initialize and/or restore state.
-        for handler in self.restart_handlers:
-            handler()
-
     def absolute_url(self, relative_url):
         '''
         Returns an absolute url for files served from Marionette's www directory.
 
         :param relative_url: The url of a static file, relative to Marionette's www directory.
         '''
         return "%s%s" % (self.baseurl, relative_url)