- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / test / functional / extensions.py
1 #!/usr/bin/env python
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """
7 This module is a simple qa tool that installs extensions and tests whether the
8 browser crashes while visiting a list of urls.
9
10 Usage: python extensions.py -v
11
12 Note: This assumes that there is a directory of extensions called
13 'extensions-tool' and that there is a file of newline-separated urls to visit
14 called 'urls.txt' in the data directory.
15 """
16
17 import glob
18 import logging
19 import os
20 import sys
21
22 import pyauto_functional # must be imported before pyauto
23 import pyauto
24
25
26 class ExtensionsPage(object):
27   """Access options in extensions page (chrome://extensions-frame)."""
28
29   _URL = 'chrome://extensions-frame'
30
31   def __init__(self, driver):
32     self._driver = driver
33     self._driver.get(ExtensionsPage._URL)
34
35   def CheckExtensionVisible(self, ext_id):
36     """Returns True if |ext_id| exists on page."""
37     return len(self._driver.find_elements_by_id(ext_id)) == 1
38
39   def SetEnabled(self, ext_id, enabled):
40     """Clicks on 'Enabled' checkbox for specified extension.
41
42     Args:
43       ext_id: Extension ID to be enabled or disabled.
44       enabled: Boolean indicating whether |ext_id| is to be enabled or disabled.
45     """
46     checkbox = self._driver.find_element_by_xpath(
47         '//*[@id="%s"]//*[@class="enable-controls"]//*[@type="checkbox"]' %
48         ext_id)
49     if checkbox != enabled:
50       checkbox.click()
51     # Reload page to ensure that the UI is recreated.
52     self._driver.get(ExtensionsPage._URL)
53
54   def SetAllowInIncognito(self, ext_id, allowed):
55     """Clicks on 'Allow in incognito' checkbox for specified extension.
56
57     Args:
58       ext_id: Extension ID to be enabled or disabled.
59       allowed: Boolean indicating whether |ext_id| is to be allowed or
60           disallowed in incognito.
61     """
62     checkbox = self._driver.find_element_by_xpath(
63         '//*[@id="%s"]//*[@class="incognito-control"]//*[@type="checkbox"]' %
64         ext_id)
65     if checkbox.is_selected() != allowed:
66       checkbox.click()
67     # Reload page to ensure that the UI is recreated.
68     self._driver.get(ExtensionsPage._URL)
69
70   def SetAllowAccessFileURLs(self, ext_id, allowed):
71     """Clicks on 'Allow access to file URLs' checkbox for specified extension.
72
73     Args:
74       ext_id: Extension ID to be enabled or disabled.
75       allowed: Boolean indicating whether |ext_id| is to be allowed access to
76           file URLs.
77     """
78     checkbox = self._driver.find_element_by_xpath(
79         '//*[@id="%s"]//*[@class="file-access-control"]//*[@type="checkbox"]' %
80         ext_id)
81     if checkbox.is_selected() != allowed:
82       checkbox.click()
83
84
85 class ExtensionsTest(pyauto.PyUITest):
86   """Test of extensions."""
87
88   def Debug(self):
89     """Test method for experimentation.
90
91     This method is not run automatically.
92     """
93     while True:
94       raw_input('Interact with the browser and hit <enter> to dump history.')
95       print '*' * 20
96       self.pprint(self.GetExtensionsInfo())
97
98   def _GetInstalledExtensionIds(self):
99     return [extension['id'] for extension in self.GetExtensionsInfo()]
100
101   def _ReturnCrashingExtensions(self, extensions, group_size, top_urls):
102     """Returns the group of extensions that crashes (if any).
103
104     Install the given extensions in groups of group_size and return the
105     group of extensions that crashes (if any).
106
107     Args:
108       extensions: A list of extensions to install.
109       group_size: The number of extensions to install at one time.
110       top_urls: The list of top urls to visit.
111
112     Returns:
113       The extensions in the crashing group or None if there is no crash.
114     """
115     curr_extension = 0
116     num_extensions = len(extensions)
117     self.RestartBrowser()
118     orig_extension_ids = self._GetInstalledExtensionIds()
119
120     while curr_extension < num_extensions:
121       logging.debug('New group of %d extensions.', group_size)
122       group_end = curr_extension + group_size
123       for extension in extensions[curr_extension:group_end]:
124         logging.debug('Installing extension: %s', extension)
125         self.InstallExtension(extension)
126
127       for url in top_urls:
128         self.NavigateToURL(url)
129
130       def _LogAndReturnCrashing():
131         crashing_extensions = extensions[curr_extension:group_end]
132         logging.debug('Crashing extensions: %s', crashing_extensions)
133         return crashing_extensions
134
135       # If the browser has crashed, return the extensions in the failing group.
136       try:
137         num_browser_windows = self.GetBrowserWindowCount()
138       except:
139         return _LogAndReturnCrashing()
140       else:
141         if not num_browser_windows:
142           return _LogAndReturnCrashing()
143         else:
144           # Uninstall all extensions that aren't installed by default.
145           new_extension_ids = [id for id in self._GetInstalledExtensionIds()
146                                if id not in orig_extension_ids]
147           for extension_id in new_extension_ids:
148             self.UninstallExtensionById(extension_id)
149
150       curr_extension = group_end
151
152     # None of the extensions crashed.
153     return None
154
155   def _GetExtensionInfoById(self, extensions, id):
156     for x in extensions:
157       if x['id'] == id:
158         return x
159     return None
160
161   def ExtensionCrashes(self):
162     """Add top extensions; confirm browser stays up when visiting top urls."""
163     # TODO: provide a way in pyauto to pass args to a test - take these as args
164     extensions_dir = os.path.join(self.DataDir(), 'extensions-tool')
165     urls_file = os.path.join(self.DataDir(), 'urls.txt')
166
167     error_msg = 'The dir "%s" must exist' % os.path.abspath(extensions_dir)
168     assert os.path.exists(extensions_dir), error_msg
169     error_msg = 'The file "%s" must exist' % os.path.abspath(urls_file)
170     assert os.path.exists(urls_file), error_msg
171
172     num_urls_to_visit = 100
173     extensions_group_size = 20
174
175     top_urls = [l.rstrip() for l in
176                 open(urls_file).readlines()[:num_urls_to_visit]]
177
178     failed_extensions = glob.glob(os.path.join(extensions_dir, '*.crx'))
179     group_size = extensions_group_size
180
181     while (group_size and failed_extensions):
182       failed_extensions = self._ReturnCrashingExtensions(
183           failed_extensions, group_size, top_urls)
184       group_size = group_size // 2
185
186     self.assertFalse(failed_extensions,
187                      'Extension(s) in failing group: %s' % failed_extensions)
188
189   def _InstallExtensionCheckDefaults(self, crx_file):
190     """Installs extension at extensions/|crx_file| and checks default status.
191
192     Checks that the installed extension is enabled and not allowed in incognito.
193
194     Args:
195       crx_file: Relative path from self.DataDir()/extensions to .crx extension
196                 to be installed.
197
198     Returns:
199       The extension ID.
200     """
201     crx_file_path = os.path.abspath(
202         os.path.join(self.DataDir(), 'extensions', crx_file))
203     ext_id = self.InstallExtension(crx_file_path)
204     extension = self._GetExtensionInfoById(self.GetExtensionsInfo(), ext_id)
205     self.assertTrue(extension['is_enabled'],
206                     msg='Extension was not enabled on installation')
207     self.assertFalse(extension['allowed_in_incognito'],
208                      msg='Extension was allowed in incognito on installation.')
209
210     return ext_id
211
212   def _ExtensionValue(self, ext_id, key):
213     """Returns the value of |key| for |ext_id|.
214
215     Args:
216       ext_id: The extension ID.
217       key: The key for which the extensions info value is required.
218
219     Returns:
220       The value of extensions info |key| for |ext_id|.
221     """
222     return self._GetExtensionInfoById(self.GetExtensionsInfo(), ext_id)[key]
223
224   def _FileAccess(self, ext_id):
225     """Returns the value of newAllowFileAccess for |ext_id|.
226
227     Args:
228       ext_id: The extension ID.
229
230     Returns:
231       The value of extensions settings newAllowFileAccess for |ext_id|.
232     """
233     extension_settings = self.GetPrefsInfo().Prefs()['extensions']['settings']
234     return extension_settings[ext_id]['newAllowFileAccess']
235
236   def testGetExtensionPermissions(self):
237     """Ensures we can retrieve the host/api permissions for an extension.
238
239     This test assumes that the 'Bookmark Manager' extension exists in a fresh
240     profile.
241     """
242     extensions_info = self.GetExtensionsInfo()
243     bm_exts = [x for x in extensions_info if x['name'] == 'Bookmark Manager']
244     self.assertTrue(bm_exts,
245                     msg='Could not find info for the Bookmark Manager '
246                     'extension.')
247     ext = bm_exts[0]
248
249     permissions_host = ext['host_permissions']
250     self.assertTrue(len(permissions_host) == 2 and
251                     'chrome://favicon/*' in permissions_host and
252                     'chrome://resources/*' in permissions_host,
253                     msg='Unexpected host permissions information.')
254
255     permissions_api = ext['api_permissions']
256     print permissions_api
257     self.assertTrue(len(permissions_api) == 5 and
258                     'bookmarks' in permissions_api and
259                     'bookmarkManagerPrivate' in permissions_api and
260                     'metricsPrivate' in permissions_api and
261                     'systemPrivate' in permissions_api and
262                     'tabs' in permissions_api,
263                     msg='Unexpected API permissions information.')
264
265   def testDisableEnableExtension(self):
266     """Tests that an extension can be disabled and enabled with the UI."""
267     ext_id = self._InstallExtensionCheckDefaults('good.crx')
268
269     # Disable extension.
270     driver = self.NewWebDriver()
271     ext_page = ExtensionsPage(driver)
272     self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id])
273     ext_page.SetEnabled(ext_id, False)
274     self.WaitUntil(self._ExtensionValue, args=[ext_id, 'is_enabled'],
275                    expect_retval=False)
276     self.assertFalse(self._ExtensionValue(ext_id, 'is_enabled'),
277                      msg='Extension did not get disabled.')
278
279     # Enable extension.
280     self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id])
281     ext_page.SetEnabled(ext_id, True)
282     self.WaitUntil(self._ExtensionValue, args=[ext_id, 'is_enabled'],
283                    expect_retval=True)
284     self.assertTrue(self._ExtensionValue(ext_id, 'is_enabled'),
285                     msg='Extension did not get enabled.')
286
287   def testAllowIncognitoExtension(self):
288     """Tests allowing and disallowing an extension in incognito mode."""
289     ext_id = self._InstallExtensionCheckDefaults('good.crx')
290
291     # Allow in incognito.
292     driver = self.NewWebDriver()
293     ext_page = ExtensionsPage(driver)
294     self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id])
295     ext_page.SetAllowInIncognito(ext_id, True)
296
297     # Check extension now allowed in incognito.
298     self.WaitUntil(self._ExtensionValue, args=[ext_id, 'allowed_in_incognito'],
299                    expect_retval=True)
300     self.assertTrue(self._ExtensionValue(ext_id, 'allowed_in_incognito'),
301                     msg='Extension did not get allowed in incognito.')
302
303     # Disallow in incognito.
304     self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id])
305     ext_page.SetAllowInIncognito(ext_id, False)
306
307     # Check extension now disallowed in incognito.
308     self.WaitUntil(self._ExtensionValue, args=[ext_id, 'allowed_in_incognito'],
309                    expect_retval=False)
310     self.assertFalse(self._ExtensionValue(ext_id, 'allowed_in_incognito'),
311                      msg='Extension did not get disallowed in incognito.')
312
313   def testAllowAccessFileURLs(self):
314     """Tests disallowing and allowing and extension access to file URLs."""
315     ext_id = self._InstallExtensionCheckDefaults(os.path.join('permissions',
316                                                               'files'))
317
318     # Check extension allowed access to file URLs by default.
319     extension_settings = self.GetPrefsInfo().Prefs()['extensions']['settings']
320     self.assertTrue(extension_settings[ext_id]['newAllowFileAccess'],
321                     msg='Extension was not allowed access to file URLs on '
322                     'installation')
323
324     # Disallow access to file URLs.
325     driver = self.NewWebDriver()
326     ext_page = ExtensionsPage(driver)
327     self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id])
328     ext_page.SetAllowAccessFileURLs(ext_id, False)
329
330     # Check that extension does not have access to file URLs.
331     self.WaitUntil(self._FileAccess, args=[ext_id], expect_retval=False)
332     self.assertFalse(self._FileAccess(ext_id),
333                      msg='Extension did not have access to file URLs denied.')
334
335     # Allow access to file URLs.
336     self.WaitUntil(ext_page.CheckExtensionVisible, args=[ext_id])
337     ext_page.SetAllowAccessFileURLs(ext_id, True)
338
339     # Check that extension now has access to file URLs.
340     self.WaitUntil(self._FileAccess, args=[ext_id], expect_retval=True)
341     self.assertTrue(self._FileAccess(ext_id),
342                     msg='Extension did not have access to file URLs granted.')
343
344
345 if __name__ == '__main__':
346   pyauto_functional.Main()