From 1f078596361cdca5f65b30979b350585f87bef70 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Sat, 19 Nov 2016 15:25:14 -0500 Subject: [PATCH] Bug 1318879 - Remove the app related code from mozprofile; r=ahal --- .../mozbase/mozprofile/mozprofile/__init__.py | 1 - .../mozbase/mozprofile/mozprofile/profile.py | 11 +- .../mozbase/mozprofile/mozprofile/webapps.py | 281 ------------------ .../mozprofile/tests/files/webapps1.json | 50 ---- .../mozprofile/tests/files/webapps2.json | 37 --- testing/mozbase/mozprofile/tests/manifest.ini | 1 - .../mozbase/mozprofile/tests/test_webapps.py | 202 ------------- 7 files changed, 1 insertion(+), 582 deletions(-) delete mode 100644 testing/mozbase/mozprofile/mozprofile/webapps.py delete mode 100644 testing/mozbase/mozprofile/tests/files/webapps1.json delete mode 100644 testing/mozbase/mozprofile/tests/files/webapps2.json delete mode 100755 testing/mozbase/mozprofile/tests/test_webapps.py diff --git a/testing/mozbase/mozprofile/mozprofile/__init__.py b/testing/mozbase/mozprofile/mozprofile/__init__.py index 96bf1020bc02..3c5af4a673a8 100644 --- a/testing/mozbase/mozprofile/mozprofile/__init__.py +++ b/testing/mozbase/mozprofile/mozprofile/__init__.py @@ -18,4 +18,3 @@ from permissions import * from prefs import * from profile import * from view import * -from webapps import * diff --git a/testing/mozbase/mozprofile/mozprofile/profile.py b/testing/mozbase/mozprofile/mozprofile/profile.py index b07b114492e1..6bae0e8f1e51 100644 --- a/testing/mozbase/mozprofile/mozprofile/profile.py +++ b/testing/mozbase/mozprofile/mozprofile/profile.py @@ -12,7 +12,6 @@ import mozfile from permissions import Permissions from prefs import Preferences from shutil import copytree -from webapps import WebappCollection __all__ = ['Profile', 'FirefoxProfile', @@ -44,13 +43,12 @@ class Profile(object): # profile.cleanup() has been called here """ - def __init__(self, profile=None, addons=None, addon_manifests=None, apps=None, + def __init__(self, profile=None, addons=None, addon_manifests=None, preferences=None, locations=None, proxy=None, restore=True): """ :param profile: Path to the profile :param addons: String of one or list of addons to install :param addon_manifests: Manifest for addons (see http://bit.ly/17jQ7i6) - :param apps: Dictionary or class of webapps to install :param preferences: Dictionary or class of preferences :param locations: ServerLocations object :param proxy: Setup a proxy @@ -58,7 +56,6 @@ class Profile(object): """ self._addons = addons self._addon_manifests = addon_manifests - self._apps = apps self._locations = locations self._proxy = proxy @@ -116,10 +113,6 @@ class Profile(object): self.addon_manager = AddonManager(self.profile, restore=self.restore) self.addon_manager.install_addons(self._addons, self._addon_manifests) - # handle webapps - self.webapps = WebappCollection(profile=self.profile, apps=self._apps) - self.webapps.update_manifests() - def __enter__(self): return self @@ -142,8 +135,6 @@ class Profile(object): self.addon_manager.clean() if getattr(self, 'permissions', None) is not None: self.permissions.clean_db() - if getattr(self, 'webapps', None) is not None: - self.webapps.clean() # If it's a temporary profile we have to remove it if self.create_new: diff --git a/testing/mozbase/mozprofile/mozprofile/webapps.py b/testing/mozbase/mozprofile/mozprofile/webapps.py deleted file mode 100644 index 4daf9ef06acc..000000000000 --- a/testing/mozbase/mozprofile/mozprofile/webapps.py +++ /dev/null @@ -1,281 +0,0 @@ -# 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/. - -""" -Handles installing open webapps (https://developer.mozilla.org/en-US/docs/Apps) -to a profile. A webapp object is a dict that contains some metadata about -the webapp and must at least include a name, description and manifestURL. - -Each webapp has a manifest (https://developer.mozilla.org/en-US/docs/Apps/Manifest). -Additionally there is a separate json manifest that keeps track of the installed -webapps, their manifestURLs and their permissions. -""" - -from string import Template -import json -import os -import shutil - -import mozfile - -__all__ = ["Webapp", "WebappCollection", "WebappFormatException", "APP_STATUS_NOT_INSTALLED", - "APP_STATUS_INSTALLED", "APP_STATUS_PRIVILEGED", "APP_STATUS_CERTIFIED"] - - -# from http://hg.mozilla.org/mozilla-central/file/add0b94c2c0b/caps/idl/nsIPrincipal.idl#l163 -APP_STATUS_NOT_INSTALLED = 0 -APP_STATUS_INSTALLED = 1 -APP_STATUS_PRIVILEGED = 2 -APP_STATUS_CERTIFIED = 3 - - -class WebappFormatException(Exception): - """thrown for invalid webapp objects""" - - -class Webapp(dict): - """A webapp definition""" - - required_keys = ('name', 'description', 'manifestURL') - - def __init__(self, *args, **kwargs): - try: - dict.__init__(self, *args, **kwargs) - except (TypeError, ValueError): - raise WebappFormatException("Webapp object should be an instance of type 'dict'") - self.validate() - - def __eq__(self, other): - """Webapps are considered equal if they have the same name""" - if not isinstance(other, self.__class__): - return False - return self['name'] == other['name'] - - def __ne__(self, other): - """Webapps are considered not equal if they have different names""" - return not self.__eq__(other) - - def validate(self): - # TODO some keys are required if another key has a certain value - for key in self.required_keys: - if key not in self: - raise WebappFormatException("Webapp object missing required key '%s'" % key) - - -class WebappCollection(object): - """A list-like object that collects webapps and updates the webapp manifests""" - - json_template = Template(""""$name": { - "name": "$name", - "origin": "$origin", - "installOrigin": "$origin", - "receipt": null, - "installTime": 132333986000, - "manifestURL": "$manifestURL", - "localId": $localId, - "id": "$name", - "appStatus": $appStatus, - "csp": "$csp" -}""") - - manifest_template = Template("""{ - "name": "$name", - "csp": "$csp", - "description": "$description", - "launch_path": "/", - "developer": { - "name": "Mozilla", - "url": "https://mozilla.org/" - }, - "permissions": [ - ], - "locales": { - "en-US": { - "name": "$name", - "description": "$description" - } - }, - "default_locale": "en-US", - "icons": { - } -} -""") - - def __init__(self, profile, apps=None, json_template=None, manifest_template=None): - """ - :param profile: the file path to a profile - :param apps: [optional] a list of webapp objects or file paths to json files describing - webapps - :param json_template: [optional] string template describing the webapp json format - :param manifest_template: [optional] string template describing the webapp manifest format - """ - if not isinstance(profile, basestring): - raise TypeError("Must provide path to a profile, received '%s'" % type(profile)) - self.profile = profile - self.webapps_dir = os.path.join(self.profile, 'webapps') - self.backup_dir = os.path.join(self.profile, '.mozprofile_backup', 'webapps') - - self._apps = [] - self._installed_apps = [] - if apps: - if not isinstance(apps, (list, set, tuple)): - apps = [apps] - - for app in apps: - if isinstance(app, basestring) and os.path.isfile(app): - self.extend(self.read_json(app)) - else: - self.append(app) - - self.json_template = json_template or self.json_template - self.manifest_template = manifest_template or self.manifest_template - - def __getitem__(self, index): - return self._apps.__getitem__(index) - - def __setitem__(self, index, value): - return self._apps.__setitem__(index, Webapp(value)) - - def __delitem__(self, index): - return self._apps.__delitem__(index) - - def __len__(self): - return self._apps.__len__() - - def __contains__(self, value): - return self._apps.__contains__(Webapp(value)) - - def append(self, value): - return self._apps.append(Webapp(value)) - - def insert(self, index, value): - return self._apps.insert(index, Webapp(value)) - - def extend(self, values): - return self._apps.extend([Webapp(v) for v in values]) - - def remove(self, value): - return self._apps.remove(Webapp(value)) - - def _write_webapps_json(self, apps): - contents = [] - for app in apps: - contents.append(self.json_template.substitute(app)) - contents = '{\n' + ',\n'.join(contents) + '\n}\n' - webapps_json_path = os.path.join(self.webapps_dir, 'webapps.json') - webapps_json_file = open(webapps_json_path, "w") - webapps_json_file.write(contents) - webapps_json_file.close() - - def _write_webapp_manifests(self, write_apps=[], remove_apps=[]): - # Write manifests for installed apps - for app in write_apps: - manifest_dir = os.path.join(self.webapps_dir, app['name']) - manifest_path = os.path.join(manifest_dir, 'manifest.webapp') - if not os.path.isfile(manifest_path): - if not os.path.isdir(manifest_dir): - os.mkdir(manifest_dir) - manifest = self.manifest_template.substitute(app) - manifest_file = open(manifest_path, "a") - manifest_file.write(manifest) - manifest_file.close() - # Remove manifests for removed apps - for app in remove_apps: - self._installed_apps.remove(app) - manifest_dir = os.path.join(self.webapps_dir, app['name']) - mozfile.remove(manifest_dir) - - def update_manifests(self): - """Updates the webapp manifests with the webapps represented in this collection - - If update_manifests is called a subsequent time, there could have been apps added or - removed to the collection in the interim. The manifests will be adjusted accordingly - """ - apps_to_install = [app for app in self._apps if app not in self._installed_apps] - apps_to_remove = [app for app in self._installed_apps if app not in self._apps] - if apps_to_install == apps_to_remove == []: - # nothing to do - return - - if not os.path.isdir(self.webapps_dir): - os.makedirs(self.webapps_dir) - elif not self._installed_apps: - shutil.copytree(self.webapps_dir, self.backup_dir) - - webapps_json_path = os.path.join(self.webapps_dir, 'webapps.json') - webapps_json = [] - if os.path.isfile(webapps_json_path): - webapps_json = self.read_json(webapps_json_path, description="description") - webapps_json = [a for a in webapps_json if a not in apps_to_remove] - - # Iterate over apps already in webapps.json to determine the starting local - # id and to ensure apps are properly formatted - start_id = 1 - for local_id, app in enumerate(webapps_json): - app['localId'] = local_id + 1 - start_id += 1 - if not app.get('csp'): - app['csp'] = '' - if not app.get('appStatus'): - app['appStatus'] = 3 - - # Append apps_to_install to the pre-existent apps - for local_id, app in enumerate(apps_to_install): - app['localId'] = local_id + start_id - # ignore if it's already installed - if app in webapps_json: - start_id -= 1 - continue - webapps_json.append(app) - self._installed_apps.append(app) - - # Write the full contents to webapps.json - self._write_webapps_json(webapps_json) - - # Create/remove manifest file for each app. - self._write_webapp_manifests(apps_to_install, apps_to_remove) - - def clean(self): - """Remove all webapps that were installed and restore profile to previous state""" - if self._installed_apps: - mozfile.remove(self.webapps_dir) - - if os.path.isdir(self.backup_dir): - shutil.copytree(self.backup_dir, self.webapps_dir) - mozfile.remove(self.backup_dir) - - self._apps = [] - self._installed_apps = [] - - @classmethod - def read_json(cls, path, **defaults): - """Reads a json file which describes a set of webapps. The json format is either a - dictionary where each key represents the name of a webapp (e.g B2G format) or a list - of webapp objects. - - :param path: Path to a json file defining webapps - :param defaults: Default key value pairs added to each webapp object if key doesn't exist - - Returns a list of Webapp objects - """ - f = open(path, 'r') - app_json = json.load(f) - f.close() - - apps = [] - if isinstance(app_json, dict): - for k, v in app_json.iteritems(): - v['name'] = k - apps.append(v) - else: - apps = app_json - if not isinstance(apps, list): - apps = [apps] - - ret = [] - for app in apps: - d = defaults.copy() - d.update(app) - ret.append(Webapp(**d)) - return ret diff --git a/testing/mozbase/mozprofile/tests/files/webapps1.json b/testing/mozbase/mozprofile/tests/files/webapps1.json deleted file mode 100644 index 00220a3d1345..000000000000 --- a/testing/mozbase/mozprofile/tests/files/webapps1.json +++ /dev/null @@ -1,50 +0,0 @@ -[{ "name": "http_example_org", - "csp": "", - "origin": "http://example.org", - "manifestURL": "http://example.org/manifest.webapp", - "description": "http://example.org App", - "appStatus": 1 - }, - { "name": "https_example_com", - "csp": "", - "origin": "https://example.com", - "manifestURL": "https://example.com/manifest.webapp", - "description": "https://example.com App", - "appStatus": 1 - }, - { "name": "http_test1_example_org", - "csp": "", - "origin": "http://test1.example.org", - "manifestURL": "http://test1.example.org/manifest.webapp", - "description": "http://test1.example.org App", - "appStatus": 1 - }, - { "name": "http_test1_example_org_8000", - "csp": "", - "origin": "http://test1.example.org:8000", - "manifestURL": "http://test1.example.org:8000/manifest.webapp", - "description": "http://test1.example.org:8000 App", - "appStatus": 1 - }, - { "name": "http_sub1_test1_example_org", - "csp": "", - "origin": "http://sub1.test1.example.org", - "manifestURL": "http://sub1.test1.example.org/manifest.webapp", - "description": "http://sub1.test1.example.org App", - "appStatus": 1 - }, - { "name": "https_example_com_privileged", - "csp": "", - "origin": "https://example.com", - "manifestURL": "https://example.com/manifest_priv.webapp", - "description": "https://example.com Privileged App", - "appStatus": 2 - }, - { "name": "https_example_com_certified", - "csp": "", - "origin": "https://example.com", - "manifestURL": "https://example.com/manifest_cert.webapp", - "description": "https://example.com Certified App", - "appStatus": 3 - } -] diff --git a/testing/mozbase/mozprofile/tests/files/webapps2.json b/testing/mozbase/mozprofile/tests/files/webapps2.json deleted file mode 100644 index 03e84a0419ac..000000000000 --- a/testing/mozbase/mozprofile/tests/files/webapps2.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "https_example_csp_certified": { - "csp": "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'", - "origin": "https://example.com", - "manifestURL": "https://example.com/manifest_csp_cert.webapp", - "description": "https://example.com certified app with manifest policy", - "appStatus": 3 - }, - "https_example_csp_installed": { - "csp": "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'", - "origin": "https://example.com", - "manifestURL": "https://example.com/manifest_csp_inst.webapp", - "description": "https://example.com installed app with manifest policy", - "appStatus": 1 - }, - "https_example_csp_privileged": { - "csp": "default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'", - "origin": "https://example.com", - "manifestURL": "https://example.com/manifest_csp_priv.webapp", - "description": "https://example.com privileged app with manifest policy", - "appStatus": 2 - }, - "https_a_domain_certified": { - "csp": "", - "origin": "https://acertified.com", - "manifestURL": "https://acertified.com/manifest.webapp", - "description": "https://acertified.com certified app", - "appStatus": 3 - }, - "https_a_domain_privileged": { - "csp": "", - "origin": "https://aprivileged.com", - "manifestURL": "https://aprivileged.com/manifest.webapp", - "description": "https://aprivileged.com privileged app ", - "appStatus": 2 - } -} diff --git a/testing/mozbase/mozprofile/tests/manifest.ini b/testing/mozbase/mozprofile/tests/manifest.ini index 3e5ea50d67f8..594e1b0dad97 100644 --- a/testing/mozbase/mozprofile/tests/manifest.ini +++ b/testing/mozbase/mozprofile/tests/manifest.ini @@ -6,7 +6,6 @@ [test_nonce.py] [bug785146.py] [test_clone_cleanup.py] -[test_webapps.py] [test_profile.py] [test_profile_view.py] [test_addons.py] diff --git a/testing/mozbase/mozprofile/tests/test_webapps.py b/testing/mozbase/mozprofile/tests/test_webapps.py deleted file mode 100755 index 4db992d6964c..000000000000 --- a/testing/mozbase/mozprofile/tests/test_webapps.py +++ /dev/null @@ -1,202 +0,0 @@ -#!/usr/bin/env python - -""" -test installing and managing webapps in a profile -""" - -import os -import shutil -import unittest -from tempfile import mkdtemp - -from mozprofile.webapps import WebappCollection, Webapp, WebappFormatException - -here = os.path.dirname(os.path.abspath(__file__)) - - -class WebappTest(unittest.TestCase): - """Tests reading, installing and cleaning webapps - from a profile. - """ - manifest_path_1 = os.path.join(here, 'files', 'webapps1.json') - manifest_path_2 = os.path.join(here, 'files', 'webapps2.json') - - def setUp(self): - self.profile = mkdtemp(prefix='test_webapp') - self.webapps_dir = os.path.join(self.profile, 'webapps') - self.webapps_json_path = os.path.join(self.webapps_dir, 'webapps.json') - - def tearDown(self): - shutil.rmtree(self.profile) - - def test_read_json_manifest(self): - """Tests WebappCollection.read_json""" - # Parse a list of webapp objects and verify it worked - manifest_json_1 = WebappCollection.read_json(self.manifest_path_1) - self.assertEqual(len(manifest_json_1), 7) - for app in manifest_json_1: - self.assertIsInstance(app, Webapp) - for key in Webapp.required_keys: - self.assertIn(key, app) - - # Parse a dictionary of webapp objects and verify it worked - manifest_json_2 = WebappCollection.read_json(self.manifest_path_2) - self.assertEqual(len(manifest_json_2), 5) - for app in manifest_json_2: - self.assertIsInstance(app, Webapp) - for key in Webapp.required_keys: - self.assertIn(key, app) - - def test_invalid_webapp(self): - """Tests a webapp with a missing required key""" - webapps = WebappCollection(self.profile) - # Missing the required key "description", exception should be raised - self.assertRaises(WebappFormatException, webapps.append, {'name': 'foo'}) - - def test_webapp_collection(self): - """Tests the methods of the WebappCollection object""" - webapp_1 = {'name': 'test_app_1', - 'description': 'a description', - 'manifestURL': 'http://example.com/1/manifest.webapp', - 'appStatus': 1} - - webapp_2 = {'name': 'test_app_2', - 'description': 'another description', - 'manifestURL': 'http://example.com/2/manifest.webapp', - 'appStatus': 2} - - webapp_3 = {'name': 'test_app_2', - 'description': 'a third description', - 'manifestURL': 'http://example.com/3/manifest.webapp', - 'appStatus': 3} - - webapps = WebappCollection(self.profile) - self.assertEqual(len(webapps), 0) - - # WebappCollection should behave like a list - def invalid_index(): - webapps[0] - self.assertRaises(IndexError, invalid_index) - - # Append a webapp object - webapps.append(webapp_1) - self.assertTrue(len(webapps), 1) - self.assertIsInstance(webapps[0], Webapp) - self.assertEqual(len(webapps[0]), len(webapp_1)) - self.assertEqual(len(set(webapps[0].items()) & set(webapp_1.items())), len(webapp_1)) - - # Remove a webapp object - webapps.remove(webapp_1) - self.assertEqual(len(webapps), 0) - - # Extend a list of webapp objects - webapps.extend([webapp_1, webapp_2]) - self.assertEqual(len(webapps), 2) - self.assertTrue(webapp_1 in webapps) - self.assertTrue(webapp_2 in webapps) - self.assertNotEquals(webapps[0], webapps[1]) - - # Insert a webapp object - webapps.insert(1, webapp_3) - self.assertEqual(len(webapps), 3) - self.assertEqual(webapps[1], webapps[2]) - for app in webapps: - self.assertIsInstance(app, Webapp) - - # Assigning an invalid type (must be accepted by the dict() constructor) should throw - def invalid_type(): - webapps[2] = 1 - self.assertRaises(WebappFormatException, invalid_type) - - def test_install_webapps(self): - """Test installing webapps into a profile that has no prior webapps""" - webapps = WebappCollection(self.profile, apps=self.manifest_path_1) - self.assertFalse(os.path.exists(self.webapps_dir)) - - # update the webapp manifests for the first time - webapps.update_manifests() - self.assertFalse(os.path.isdir(os.path.join(self.profile, webapps.backup_dir))) - self.assertTrue(os.path.isfile(self.webapps_json_path)) - - webapps_json = webapps.read_json(self.webapps_json_path, description="fake description") - self.assertEqual(len(webapps_json), 7) - for app in webapps_json: - self.assertIsInstance(app, Webapp) - - manifest_json_1 = webapps.read_json(self.manifest_path_1) - manifest_json_2 = webapps.read_json(self.manifest_path_2) - self.assertEqual(len(webapps_json), len(manifest_json_1)) - for app in webapps_json: - self.assertTrue(app in manifest_json_1) - - # Remove one of the webapps from WebappCollection after it got installed - removed_app = manifest_json_1[2] - webapps.remove(removed_app) - # Add new webapps to the collection - webapps.extend(manifest_json_2) - - # update the webapp manifests a second time - webapps.update_manifests() - self.assertFalse(os.path.isdir(os.path.join(self.profile, webapps.backup_dir))) - self.assertTrue(os.path.isfile(self.webapps_json_path)) - - webapps_json = webapps.read_json(self.webapps_json_path, description="a description") - self.assertEqual(len(webapps_json), 11) - - # The new apps should be added - for app in webapps_json: - self.assertIsInstance(app, Webapp) - self.assertTrue(os.path.isfile(os.path.join(self.webapps_dir, app['name'], - 'manifest.webapp'))) - # The removed app should not exist in the manifest - self.assertNotIn(removed_app, webapps_json) - self.assertFalse(os.path.exists(os.path.join(self.webapps_dir, removed_app['name']))) - - # Cleaning should delete the webapps directory entirely - # since there was nothing there before - webapps.clean() - self.assertFalse(os.path.isdir(self.webapps_dir)) - - def test_install_webapps_preexisting(self): - """Tests installing webapps when the webapps directory already exists""" - manifest_json_2 = WebappCollection.read_json(self.manifest_path_2) - - # Synthesize a pre-existing webapps directory - os.mkdir(self.webapps_dir) - shutil.copyfile(self.manifest_path_2, self.webapps_json_path) - for app in manifest_json_2: - app_path = os.path.join(self.webapps_dir, app['name']) - os.mkdir(app_path) - f = open(os.path.join(app_path, 'manifest.webapp'), 'w') - f.close() - - webapps = WebappCollection(self.profile, apps=self.manifest_path_1) - self.assertTrue(os.path.exists(self.webapps_dir)) - - # update webapp manifests for the first time - webapps.update_manifests() - # A backup should be created - self.assertTrue(os.path.isdir(os.path.join(self.profile, webapps.backup_dir))) - - # Both manifests should remain installed - webapps_json = webapps.read_json(self.webapps_json_path, description='a fake description') - self.assertEqual(len(webapps_json), 12) - for app in webapps_json: - self.assertIsInstance(app, Webapp) - self.assertTrue(os.path.isfile(os.path.join(self.webapps_dir, app['name'], - 'manifest.webapp'))) - - # Upon cleaning the backup should be restored - webapps.clean() - self.assertFalse(os.path.isdir(os.path.join(self.profile, webapps.backup_dir))) - - # The original webapps should still be installed - webapps_json = webapps.read_json(self.webapps_json_path) - for app in webapps_json: - self.assertIsInstance(app, Webapp) - self.assertTrue(os.path.isfile(os.path.join(self.webapps_dir, app['name'], - 'manifest.webapp'))) - self.assertEqual(webapps_json, manifest_json_2) - -if __name__ == '__main__': - unittest.main()