1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/testing/mozbase/mozprofile/tests/test_addons.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,425 @@ 1.4 +#!/usr/bin/env python 1.5 + 1.6 +# This Source Code Form is subject to the terms of the Mozilla Public 1.7 +# License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 +# You can obtain one at http://mozilla.org/MPL/2.0/. 1.9 + 1.10 +import os 1.11 +import shutil 1.12 +import tempfile 1.13 +import unittest 1.14 +import urllib2 1.15 + 1.16 +from manifestparser import ManifestParser 1.17 +import mozfile 1.18 +import mozhttpd 1.19 +import mozlog 1.20 +import mozprofile 1.21 + 1.22 +from addon_stubs import generate_addon, generate_manifest 1.23 + 1.24 + 1.25 +here = os.path.dirname(os.path.abspath(__file__)) 1.26 + 1.27 + 1.28 +class TestAddonsManager(unittest.TestCase): 1.29 + """ Class to test mozprofile.addons.AddonManager """ 1.30 + 1.31 + def setUp(self): 1.32 + self.logger = mozlog.getLogger('mozprofile.addons') 1.33 + self.logger.setLevel(mozlog.ERROR) 1.34 + 1.35 + self.profile = mozprofile.profile.Profile() 1.36 + self.am = self.profile.addon_manager 1.37 + 1.38 + self.profile_path = self.profile.profile 1.39 + self.tmpdir = tempfile.mkdtemp() 1.40 + 1.41 + def tearDown(self): 1.42 + mozfile.rmtree(self.tmpdir) 1.43 + 1.44 + self.am = None 1.45 + self.profile = None 1.46 + 1.47 + # Bug 934484 1.48 + # Sometimes the profile folder gets recreated at the end and will be left 1.49 + # behind. So we should ensure that we clean it up correctly. 1.50 + mozfile.rmtree(self.profile_path) 1.51 + 1.52 + def test_install_addons_multiple_same_source(self): 1.53 + # Generate installer stubs for all possible types of addons 1.54 + addon_xpi = generate_addon('test-addon-1@mozilla.org', 1.55 + path=self.tmpdir) 1.56 + addon_folder = generate_addon('test-addon-1@mozilla.org', 1.57 + path=self.tmpdir, 1.58 + xpi=False) 1.59 + 1.60 + # The same folder should not be installed twice 1.61 + self.am.install_addons([addon_folder, addon_folder]) 1.62 + self.assertEqual(self.am.installed_addons, [addon_folder]) 1.63 + self.am.clean() 1.64 + 1.65 + # The same XPI file should not be installed twice 1.66 + self.am.install_addons([addon_xpi, addon_xpi]) 1.67 + self.assertEqual(self.am.installed_addons, [addon_xpi]) 1.68 + self.am.clean() 1.69 + 1.70 + # Even if it is the same id the add-on should be installed twice, if 1.71 + # specified via XPI and folder 1.72 + self.am.install_addons([addon_folder, addon_xpi]) 1.73 + self.assertEqual(len(self.am.installed_addons), 2) 1.74 + self.assertIn(addon_folder, self.am.installed_addons) 1.75 + self.assertIn(addon_xpi, self.am.installed_addons) 1.76 + self.am.clean() 1.77 + 1.78 + def test_download(self): 1.79 + server = mozhttpd.MozHttpd(docroot=os.path.join(here, 'addons')) 1.80 + server.start() 1.81 + 1.82 + # Download a valid add-on without a class instance to the general 1.83 + # tmp folder and clean-up 1.84 + try: 1.85 + addon = server.get_url() + 'empty.xpi' 1.86 + xpi_file = mozprofile.addons.AddonManager.download(addon) 1.87 + self.assertTrue(os.path.isfile(xpi_file)) 1.88 + self.assertIn('test-empty@quality.mozilla.org.xpi', 1.89 + os.path.basename(xpi_file)) 1.90 + self.assertNotIn(self.tmpdir, os.path.dirname(xpi_file)) 1.91 + finally: 1.92 + # Given that the file is stored outside of the created tmp dir 1.93 + # we have to ensure to explicitely remove it 1.94 + if os.path.isfile(xpi_file): 1.95 + os.remove(xpi_file) 1.96 + 1.97 + # Download an valid add-on to a special folder 1.98 + addon = server.get_url() + 'empty.xpi' 1.99 + xpi_file = self.am.download(addon, self.tmpdir) 1.100 + self.assertTrue(os.path.isfile(xpi_file)) 1.101 + self.assertIn('test-empty@quality.mozilla.org.xpi', 1.102 + os.path.basename(xpi_file)) 1.103 + self.assertIn(self.tmpdir, os.path.dirname(xpi_file)) 1.104 + self.assertEqual(self.am.downloaded_addons, []) 1.105 + os.remove(xpi_file) 1.106 + 1.107 + # Download an invalid add-on to a special folder 1.108 + addon = server.get_url() + 'invalid.xpi' 1.109 + self.assertRaises(mozprofile.addons.AddonFormatError, 1.110 + self.am.download, addon, self.tmpdir) 1.111 + self.assertEqual(os.listdir(self.tmpdir), []) 1.112 + 1.113 + # Download from an invalid URL 1.114 + addon = server.get_url() + 'not_existent.xpi' 1.115 + self.assertRaises(urllib2.HTTPError, 1.116 + self.am.download, addon, self.tmpdir) 1.117 + self.assertEqual(os.listdir(self.tmpdir), []) 1.118 + 1.119 + # Download from an invalid URL 1.120 + addon = 'not_existent.xpi' 1.121 + self.assertRaises(ValueError, 1.122 + self.am.download, addon, self.tmpdir) 1.123 + self.assertEqual(os.listdir(self.tmpdir), []) 1.124 + 1.125 + server.stop() 1.126 + 1.127 + def test_install_from_path_xpi(self): 1.128 + addons_to_install = [] 1.129 + addons_installed = [] 1.130 + 1.131 + # Generate installer stubs and install them 1.132 + for ext in ['test-addon-1@mozilla.org', 'test-addon-2@mozilla.org']: 1.133 + temp_addon = generate_addon(ext, path=self.tmpdir) 1.134 + addons_to_install.append(self.am.addon_details(temp_addon)['id']) 1.135 + self.am.install_from_path(temp_addon) 1.136 + 1.137 + # Generate a list of addons installed in the profile 1.138 + addons_installed = [unicode(x[:-len('.xpi')]) for x in os.listdir(os.path.join( 1.139 + self.profile.profile, 'extensions', 'staged'))] 1.140 + self.assertEqual(addons_to_install.sort(), addons_installed.sort()) 1.141 + 1.142 + def test_install_from_path_folder(self): 1.143 + # Generate installer stubs for all possible types of addons 1.144 + addons = [] 1.145 + addons.append(generate_addon('test-addon-1@mozilla.org', 1.146 + path=self.tmpdir)) 1.147 + addons.append(generate_addon('test-addon-2@mozilla.org', 1.148 + path=self.tmpdir, 1.149 + xpi=False)) 1.150 + addons.append(generate_addon('test-addon-3@mozilla.org', 1.151 + path=self.tmpdir, 1.152 + name='addon-3')) 1.153 + addons.append(generate_addon('test-addon-4@mozilla.org', 1.154 + path=self.tmpdir, 1.155 + name='addon-4', 1.156 + xpi=False)) 1.157 + addons.sort() 1.158 + 1.159 + self.am.install_from_path(self.tmpdir) 1.160 + 1.161 + self.assertEqual(self.am.installed_addons, addons) 1.162 + 1.163 + def test_install_from_path_unpack(self): 1.164 + # Generate installer stubs for all possible types of addons 1.165 + addon_xpi = generate_addon('test-addon-unpack@mozilla.org', 1.166 + path=self.tmpdir) 1.167 + addon_folder = generate_addon('test-addon-unpack@mozilla.org', 1.168 + path=self.tmpdir, 1.169 + xpi=False) 1.170 + addon_no_unpack = generate_addon('test-addon-1@mozilla.org', 1.171 + path=self.tmpdir) 1.172 + 1.173 + # Test unpack flag for add-on as XPI 1.174 + self.am.install_from_path(addon_xpi) 1.175 + self.assertEqual(self.am.installed_addons, [addon_xpi]) 1.176 + self.am.clean() 1.177 + 1.178 + # Test unpack flag for add-on as folder 1.179 + self.am.install_from_path(addon_folder) 1.180 + self.assertEqual(self.am.installed_addons, [addon_folder]) 1.181 + self.am.clean() 1.182 + 1.183 + # Test forcing unpack an add-on 1.184 + self.am.install_from_path(addon_no_unpack, unpack=True) 1.185 + self.assertEqual(self.am.installed_addons, [addon_no_unpack]) 1.186 + self.am.clean() 1.187 + 1.188 + def test_install_from_path_url(self): 1.189 + server = mozhttpd.MozHttpd(docroot=os.path.join(here, 'addons')) 1.190 + server.start() 1.191 + 1.192 + addon = server.get_url() + 'empty.xpi' 1.193 + self.am.install_from_path(addon) 1.194 + 1.195 + server.stop() 1.196 + 1.197 + self.assertEqual(len(self.am.downloaded_addons), 1) 1.198 + self.assertTrue(os.path.isfile(self.am.downloaded_addons[0])) 1.199 + self.assertIn('test-empty@quality.mozilla.org.xpi', 1.200 + os.path.basename(self.am.downloaded_addons[0])) 1.201 + 1.202 + def test_install_from_path_after_reset(self): 1.203 + # Installing the same add-on after a reset should not cause a failure 1.204 + addon = generate_addon('test-addon-1@mozilla.org', 1.205 + path=self.tmpdir, xpi=False) 1.206 + 1.207 + # We cannot use self.am because profile.reset() creates a new instance 1.208 + self.profile.addon_manager.install_from_path(addon) 1.209 + 1.210 + self.profile.reset() 1.211 + 1.212 + self.profile.addon_manager.install_from_path(addon) 1.213 + self.assertEqual(self.profile.addon_manager.installed_addons, [addon]) 1.214 + 1.215 + def test_install_from_path_backup(self): 1.216 + staged_path = os.path.join(self.profile_path, 'extensions', 'staged') 1.217 + 1.218 + # Generate installer stubs for all possible types of addons 1.219 + addon_xpi = generate_addon('test-addon-1@mozilla.org', 1.220 + path=self.tmpdir) 1.221 + addon_folder = generate_addon('test-addon-1@mozilla.org', 1.222 + path=self.tmpdir, 1.223 + xpi=False) 1.224 + addon_name = generate_addon('test-addon-1@mozilla.org', 1.225 + path=self.tmpdir, 1.226 + name='test-addon-1-dupe@mozilla.org') 1.227 + 1.228 + # Test backup of xpi files 1.229 + self.am.install_from_path(addon_xpi) 1.230 + self.assertIsNone(self.am.backup_dir) 1.231 + 1.232 + self.am.install_from_path(addon_xpi) 1.233 + self.assertIsNotNone(self.am.backup_dir) 1.234 + self.assertEqual(os.listdir(self.am.backup_dir), 1.235 + ['test-addon-1@mozilla.org.xpi']) 1.236 + 1.237 + self.am.clean() 1.238 + self.assertEqual(os.listdir(staged_path), 1.239 + ['test-addon-1@mozilla.org.xpi']) 1.240 + self.am.clean() 1.241 + 1.242 + # Test backup of folders 1.243 + self.am.install_from_path(addon_folder) 1.244 + self.assertIsNone(self.am.backup_dir) 1.245 + 1.246 + self.am.install_from_path(addon_folder) 1.247 + self.assertIsNotNone(self.am.backup_dir) 1.248 + self.assertEqual(os.listdir(self.am.backup_dir), 1.249 + ['test-addon-1@mozilla.org']) 1.250 + 1.251 + self.am.clean() 1.252 + self.assertEqual(os.listdir(staged_path), 1.253 + ['test-addon-1@mozilla.org']) 1.254 + self.am.clean() 1.255 + 1.256 + # Test backup of xpi files with another file name 1.257 + self.am.install_from_path(addon_name) 1.258 + self.assertIsNone(self.am.backup_dir) 1.259 + 1.260 + self.am.install_from_path(addon_xpi) 1.261 + self.assertIsNotNone(self.am.backup_dir) 1.262 + self.assertEqual(os.listdir(self.am.backup_dir), 1.263 + ['test-addon-1@mozilla.org.xpi']) 1.264 + 1.265 + self.am.clean() 1.266 + self.assertEqual(os.listdir(staged_path), 1.267 + ['test-addon-1@mozilla.org.xpi']) 1.268 + self.am.clean() 1.269 + 1.270 + def test_install_from_path_invalid_addons(self): 1.271 + # Generate installer stubs for all possible types of addons 1.272 + addons = [] 1.273 + addons.append(generate_addon('test-addon-invalid-no-manifest@mozilla.org', 1.274 + path=self.tmpdir, 1.275 + xpi=False)) 1.276 + addons.append(generate_addon('test-addon-invalid-no-id@mozilla.org', 1.277 + path=self.tmpdir)) 1.278 + 1.279 + self.am.install_from_path(self.tmpdir) 1.280 + 1.281 + self.assertEqual(self.am.installed_addons, []) 1.282 + 1.283 + @unittest.skip("Feature not implemented as part of AddonManger") 1.284 + def test_install_from_path_error(self): 1.285 + """ Check install_from_path raises an error with an invalid addon""" 1.286 + 1.287 + temp_addon = generate_addon('test-addon-invalid-version@mozilla.org') 1.288 + # This should raise an error here 1.289 + self.am.install_from_path(temp_addon) 1.290 + 1.291 + def test_install_from_manifest(self): 1.292 + temp_manifest = generate_manifest(['test-addon-1@mozilla.org', 1.293 + 'test-addon-2@mozilla.org']) 1.294 + m = ManifestParser() 1.295 + m.read(temp_manifest) 1.296 + addons = m.get() 1.297 + 1.298 + # Obtain details of addons to install from the manifest 1.299 + addons_to_install = [self.am.addon_details(x['path']).get('id') for x in addons] 1.300 + 1.301 + self.am.install_from_manifest(temp_manifest) 1.302 + # Generate a list of addons installed in the profile 1.303 + addons_installed = [unicode(x[:-len('.xpi')]) for x in os.listdir(os.path.join( 1.304 + self.profile.profile, 'extensions', 'staged'))] 1.305 + self.assertEqual(addons_installed.sort(), addons_to_install.sort()) 1.306 + 1.307 + # Cleanup the temporary addon and manifest directories 1.308 + mozfile.rmtree(os.path.dirname(temp_manifest)) 1.309 + 1.310 + def test_addon_details(self): 1.311 + # Generate installer stubs for a valid and invalid add-on manifest 1.312 + valid_addon = generate_addon('test-addon-1@mozilla.org', 1.313 + path=self.tmpdir) 1.314 + invalid_addon = generate_addon('test-addon-invalid-not-wellformed@mozilla.org', 1.315 + path=self.tmpdir) 1.316 + 1.317 + # Check valid add-on 1.318 + details = self.am.addon_details(valid_addon) 1.319 + self.assertEqual(details['id'], 'test-addon-1@mozilla.org') 1.320 + self.assertEqual(details['name'], 'Test Add-on 1') 1.321 + self.assertEqual(details['unpack'], False) 1.322 + self.assertEqual(details['version'], '0.1') 1.323 + 1.324 + # Check invalid add-on 1.325 + self.assertRaises(mozprofile.addons.AddonFormatError, 1.326 + self.am.addon_details, invalid_addon) 1.327 + 1.328 + # Check invalid path 1.329 + self.assertRaises(IOError, 1.330 + self.am.addon_details, '') 1.331 + 1.332 + # Check invalid add-on format 1.333 + addon_path = os.path.join(os.path.join(here, 'files'), 'not_an_addon.txt') 1.334 + self.assertRaises(mozprofile.addons.AddonFormatError, 1.335 + self.am.addon_details, addon_path) 1.336 + 1.337 + @unittest.skip("Bug 900154") 1.338 + def test_clean_addons(self): 1.339 + addon_one = generate_addon('test-addon-1@mozilla.org') 1.340 + addon_two = generate_addon('test-addon-2@mozilla.org') 1.341 + 1.342 + self.am.install_addons(addon_one) 1.343 + installed_addons = [unicode(x[:-len('.xpi')]) for x in os.listdir(os.path.join( 1.344 + self.profile.profile, 'extensions', 'staged'))] 1.345 + 1.346 + # Create a new profile based on an existing profile 1.347 + # Install an extra addon in the new profile 1.348 + # Cleanup addons 1.349 + duplicate_profile = mozprofile.profile.Profile(profile=self.profile.profile, 1.350 + addons=addon_two) 1.351 + duplicate_profile.addon_manager.clean() 1.352 + 1.353 + addons_after_cleanup = [unicode(x[:-len('.xpi')]) for x in os.listdir(os.path.join( 1.354 + duplicate_profile.profile, 'extensions', 'staged'))] 1.355 + # New addons installed should be removed by clean_addons() 1.356 + self.assertEqual(installed_addons, addons_after_cleanup) 1.357 + 1.358 + def test_noclean(self): 1.359 + """test `restore=True/False` functionality""" 1.360 + 1.361 + server = mozhttpd.MozHttpd(docroot=os.path.join(here, 'addons')) 1.362 + server.start() 1.363 + 1.364 + profile = tempfile.mkdtemp() 1.365 + tmpdir = tempfile.mkdtemp() 1.366 + 1.367 + try: 1.368 + # empty initially 1.369 + self.assertFalse(bool(os.listdir(profile))) 1.370 + 1.371 + # make an addon 1.372 + addons = [] 1.373 + addons.append(generate_addon('test-addon-1@mozilla.org', 1.374 + path=tmpdir)) 1.375 + addons.append(server.get_url() + 'empty.xpi') 1.376 + 1.377 + # install it with a restore=True AddonManager 1.378 + am = mozprofile.addons.AddonManager(profile, restore=True) 1.379 + 1.380 + for addon in addons: 1.381 + am.install_from_path(addon) 1.382 + 1.383 + # now its there 1.384 + self.assertEqual(os.listdir(profile), ['extensions']) 1.385 + staging_folder = os.path.join(profile, 'extensions', 'staged') 1.386 + self.assertTrue(os.path.exists(staging_folder)) 1.387 + self.assertEqual(len(os.listdir(staging_folder)), 2) 1.388 + 1.389 + # del addons; now its gone though the directory tree exists 1.390 + downloaded_addons = am.downloaded_addons 1.391 + del am 1.392 + 1.393 + self.assertEqual(os.listdir(profile), ['extensions']) 1.394 + self.assertTrue(os.path.exists(staging_folder)) 1.395 + self.assertEqual(os.listdir(staging_folder), []) 1.396 + 1.397 + for addon in downloaded_addons: 1.398 + self.assertFalse(os.path.isfile(addon)) 1.399 + 1.400 + finally: 1.401 + mozfile.rmtree(tmpdir) 1.402 + mozfile.rmtree(profile) 1.403 + 1.404 + def test_remove_addon(self): 1.405 + addons = [] 1.406 + addons.append(generate_addon('test-addon-1@mozilla.org', 1.407 + path=self.tmpdir)) 1.408 + addons.append(generate_addon('test-addon-2@mozilla.org', 1.409 + path=self.tmpdir)) 1.410 + 1.411 + self.am.install_from_path(self.tmpdir) 1.412 + 1.413 + extensions_path = os.path.join(self.profile_path, 'extensions') 1.414 + staging_path = os.path.join(extensions_path, 'staged') 1.415 + 1.416 + # Fake a run by virtually installing one of the staged add-ons 1.417 + shutil.move(os.path.join(staging_path, 'test-addon-1@mozilla.org.xpi'), 1.418 + extensions_path) 1.419 + 1.420 + for addon in self.am._addons: 1.421 + self.am.remove_addon(addon) 1.422 + 1.423 + self.assertEqual(os.listdir(staging_path), []) 1.424 + self.assertEqual(os.listdir(extensions_path), ['staged']) 1.425 + 1.426 + 1.427 +if __name__ == '__main__': 1.428 + unittest.main()