1#!/usr/bin/env python
2
3"""
4test installing and managing webapps in a profile
5"""
6
7import os
8import shutil
9import unittest
10from tempfile import mkdtemp
11
12from mozprofile.webapps import WebappCollection, Webapp, WebappFormatException
13
14here = os.path.dirname(os.path.abspath(__file__))
15
16
17class WebappTest(unittest.TestCase):
18    """Tests reading, installing and cleaning webapps
19    from a profile.
20    """
21    manifest_path_1 = os.path.join(here, 'files', 'webapps1.json')
22    manifest_path_2 = os.path.join(here, 'files', 'webapps2.json')
23
24    def setUp(self):
25        self.profile = mkdtemp(prefix='test_webapp')
26        self.webapps_dir = os.path.join(self.profile, 'webapps')
27        self.webapps_json_path = os.path.join(self.webapps_dir, 'webapps.json')
28
29    def tearDown(self):
30        shutil.rmtree(self.profile)
31
32    def test_read_json_manifest(self):
33        """Tests WebappCollection.read_json"""
34        # Parse a list of webapp objects and verify it worked
35        manifest_json_1 = WebappCollection.read_json(self.manifest_path_1)
36        self.assertEqual(len(manifest_json_1), 7)
37        for app in manifest_json_1:
38            self.assertIsInstance(app, Webapp)
39            for key in Webapp.required_keys:
40                self.assertIn(key, app)
41
42        # Parse a dictionary of webapp objects and verify it worked
43        manifest_json_2 = WebappCollection.read_json(self.manifest_path_2)
44        self.assertEqual(len(manifest_json_2), 5)
45        for app in manifest_json_2:
46            self.assertIsInstance(app, Webapp)
47            for key in Webapp.required_keys:
48                self.assertIn(key, app)
49
50    def test_invalid_webapp(self):
51        """Tests a webapp with a missing required key"""
52        webapps = WebappCollection(self.profile)
53        # Missing the required key "description", exception should be raised
54        self.assertRaises(WebappFormatException, webapps.append, {'name': 'foo'})
55
56    def test_webapp_collection(self):
57        """Tests the methods of the WebappCollection object"""
58        webapp_1 = {'name': 'test_app_1',
59                    'description': 'a description',
60                    'manifestURL': 'http://example.com/1/manifest.webapp',
61                    'appStatus': 1}
62
63        webapp_2 = {'name': 'test_app_2',
64                    'description': 'another description',
65                    'manifestURL': 'http://example.com/2/manifest.webapp',
66                    'appStatus': 2}
67
68        webapp_3 = {'name': 'test_app_2',
69                    'description': 'a third description',
70                    'manifestURL': 'http://example.com/3/manifest.webapp',
71                    'appStatus': 3}
72
73        webapps = WebappCollection(self.profile)
74        self.assertEqual(len(webapps), 0)
75
76        # WebappCollection should behave like a list
77        def invalid_index():
78            webapps[0]
79        self.assertRaises(IndexError, invalid_index)
80
81        # Append a webapp object
82        webapps.append(webapp_1)
83        self.assertTrue(len(webapps), 1)
84        self.assertIsInstance(webapps[0], Webapp)
85        self.assertEqual(len(webapps[0]), len(webapp_1))
86        self.assertEqual(len(set(webapps[0].items()) & set(webapp_1.items())), len(webapp_1))
87
88        # Remove a webapp object
89        webapps.remove(webapp_1)
90        self.assertEqual(len(webapps), 0)
91
92        # Extend a list of webapp objects
93        webapps.extend([webapp_1, webapp_2])
94        self.assertEqual(len(webapps), 2)
95        self.assertTrue(webapp_1 in webapps)
96        self.assertTrue(webapp_2 in webapps)
97        self.assertNotEquals(webapps[0], webapps[1])
98
99        # Insert a webapp object
100        webapps.insert(1, webapp_3)
101        self.assertEqual(len(webapps), 3)
102        self.assertEqual(webapps[1], webapps[2])
103        for app in webapps:
104            self.assertIsInstance(app, Webapp)
105
106        # Assigning an invalid type (must be accepted by the dict() constructor) should throw
107        def invalid_type():
108            webapps[2] = 1
109        self.assertRaises(WebappFormatException, invalid_type)
110
111    def test_install_webapps(self):
112        """Test installing webapps into a profile that has no prior webapps"""
113        webapps = WebappCollection(self.profile, apps=self.manifest_path_1)
114        self.assertFalse(os.path.exists(self.webapps_dir))
115
116        # update the webapp manifests for the first time
117        webapps.update_manifests()
118        self.assertFalse(os.path.isdir(os.path.join(self.profile, webapps.backup_dir)))
119        self.assertTrue(os.path.isfile(self.webapps_json_path))
120
121        webapps_json = webapps.read_json(self.webapps_json_path, description="fake description")
122        self.assertEqual(len(webapps_json), 7)
123        for app in webapps_json:
124            self.assertIsInstance(app, Webapp)
125
126        manifest_json_1 = webapps.read_json(self.manifest_path_1)
127        manifest_json_2 = webapps.read_json(self.manifest_path_2)
128        self.assertEqual(len(webapps_json), len(manifest_json_1))
129        for app in webapps_json:
130            self.assertTrue(app in manifest_json_1)
131
132        # Remove one of the webapps from WebappCollection after it got installed
133        removed_app = manifest_json_1[2]
134        webapps.remove(removed_app)
135        # Add new webapps to the collection
136        webapps.extend(manifest_json_2)
137
138        # update the webapp manifests a second time
139        webapps.update_manifests()
140        self.assertFalse(os.path.isdir(os.path.join(self.profile, webapps.backup_dir)))
141        self.assertTrue(os.path.isfile(self.webapps_json_path))
142
143        webapps_json = webapps.read_json(self.webapps_json_path, description="a description")
144        self.assertEqual(len(webapps_json), 11)
145
146        # The new apps should be added
147        for app in webapps_json:
148            self.assertIsInstance(app, Webapp)
149            self.assertTrue(os.path.isfile(os.path.join(self.webapps_dir, app['name'],
150                                                        'manifest.webapp')))
151        # The removed app should not exist in the manifest
152        self.assertNotIn(removed_app, webapps_json)
153        self.assertFalse(os.path.exists(os.path.join(self.webapps_dir, removed_app['name'])))
154
155        # Cleaning should delete the webapps directory entirely
156        # since there was nothing there before
157        webapps.clean()
158        self.assertFalse(os.path.isdir(self.webapps_dir))
159
160    def test_install_webapps_preexisting(self):
161        """Tests installing webapps when the webapps directory already exists"""
162        manifest_json_2 = WebappCollection.read_json(self.manifest_path_2)
163
164        # Synthesize a pre-existing webapps directory
165        os.mkdir(self.webapps_dir)
166        shutil.copyfile(self.manifest_path_2, self.webapps_json_path)
167        for app in manifest_json_2:
168            app_path = os.path.join(self.webapps_dir, app['name'])
169            os.mkdir(app_path)
170            f = open(os.path.join(app_path, 'manifest.webapp'), 'w')
171            f.close()
172
173        webapps = WebappCollection(self.profile, apps=self.manifest_path_1)
174        self.assertTrue(os.path.exists(self.webapps_dir))
175
176        # update webapp manifests for the first time
177        webapps.update_manifests()
178        # A backup should be created
179        self.assertTrue(os.path.isdir(os.path.join(self.profile, webapps.backup_dir)))
180
181        # Both manifests should remain installed
182        webapps_json = webapps.read_json(self.webapps_json_path, description='a fake description')
183        self.assertEqual(len(webapps_json), 12)
184        for app in webapps_json:
185            self.assertIsInstance(app, Webapp)
186            self.assertTrue(os.path.isfile(os.path.join(self.webapps_dir, app['name'],
187                                                        'manifest.webapp')))
188
189        # Upon cleaning the backup should be restored
190        webapps.clean()
191        self.assertFalse(os.path.isdir(os.path.join(self.profile, webapps.backup_dir)))
192
193        # The original webapps should still be installed
194        webapps_json = webapps.read_json(self.webapps_json_path)
195        for app in webapps_json:
196            self.assertIsInstance(app, Webapp)
197            self.assertTrue(os.path.isfile(os.path.join(self.webapps_dir, app['name'],
198                                                        'manifest.webapp')))
199        self.assertEqual(webapps_json, manifest_json_2)
200
201if __name__ == '__main__':
202    unittest.main()
203