#!/usr/bin/python
# Copyright (c) 2010 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""
This module is a simple qa tool that installs extensions and tests whether the
browser crashes while visiting a list of urls.

Usage: python extensions.py -v

Note: This assumes that there is a directory of extensions called 'extensions'
and that there is a file of newline-separated urls to visit called 'urls.txt'
in the same directory as the script.
"""

import glob
import logging
import os
import sys

import pyauto_functional # must be imported before pyauto
import pyauto


class ExtensionsTest(pyauto.PyUITest):
  """Test of extensions."""
  # TODO: provide a way in pyauto to pass args to a test and take these as args
  extensions_dir_ = 'extensions'  # The directory of extensions
  urls_file_ = 'urls.txt'         # The file which holds a list of urls to visit

  def Debug(self):
    """Test method for experimentation.

    This method is not run automatically.
    """
    while True:
      raw_input('Interact with the browser and hit <enter> to dump history.. ')
      print '*' * 20
      extensions = self.GetExtensionsInfo()
      import pprint
      pp = pprint.PrettyPrinter(indent=2)
      pp.pprint(extensions)

  def _GetInstalledExtensionIds(self):
    return [extension['id'] for extension in self.GetExtensionsInfo()]

  def _ReturnCrashingExtensions(self, extensions, group_size, top_urls):
    """Install the given extensions in groups of group_size and return the
       group of extensions that crashes (if any).

    Args:
      extensions: A list of extensions to install.
      group_size: The number of extensions to install at one time.
      top_urls: The list of top urls to visit.

    Returns:
      The extensions in the crashing group or None if there is no crash.
    """
    curr_extension = 0
    num_extensions = len(extensions)
    self.RestartBrowser()
    orig_extension_ids = self._GetInstalledExtensionIds()

    while curr_extension < num_extensions:
      logging.debug('New group of %d extensions.' % group_size)
      group_end = curr_extension + group_size
      for extension in extensions[curr_extension:group_end]:
        logging.debug('Installing extension: %s' % extension)
        self.InstallExtension(pyauto.FilePath(extension), False)

      for url in top_urls:
        self.NavigateToURL(url)

      def _LogAndReturnCrashing():
        crashing_extensions = extensions[curr_extension:group_end]
        logging.debug('Crashing extensions: %s' % crashing_extensions)
        return crashing_extensions

      # If the browser has crashed, return the extensions in the failing group.
      try:
        num_browser_windows = self.GetBrowserWindowCount()
      except:
        return _LogAndReturnCrashing()
      else:
        if not num_browser_windows:
          return _LogAndReturnCrashing()
        else:
          # Uninstall all extensions that aren't installed by default.
          new_extension_ids = [id for id in self._GetInstalledExtensionIds()
                               if id not in orig_extension_ids]
          for extension_id in new_extension_ids:
            self.UninstallExtensionById(extension_id)

      curr_extension = group_end

    # None of the extensions crashed.
    return None

  def testExtensionCrashes(self):
    """Add top extensions; confirm browser stays up when visiting top urls"""
    self.assertTrue(os.path.exists(self.extensions_dir_),
             'The dir "%s" must exist' % os.path.abspath(self.extensions_dir_))
    self.assertTrue(os.path.exists(self.urls_file_),
             'The file "%s" must exist' % os.path.abspath(self.urls_file_))

    num_urls_to_visit = 100
    extensions_group_size = 20

    top_urls = [l.rstrip() for l in
                open(self.urls_file_).readlines()[:num_urls_to_visit]]

    failed_extensions = glob.glob(os.path.join(self.extensions_dir_, '*.crx'))
    group_size = extensions_group_size

    while(group_size and failed_extensions):
      failed_extensions = self._ReturnCrashingExtensions(
          failed_extensions, group_size, top_urls)
      group_size = group_size // 2

    self.assertFalse(failed_extensions,
                     'Extension(s) in failing group: %s' % failed_extensions)


if __name__ == '__main__':
  pyauto_functional.Main()

@@ -406,17 +406,17 @@
 s called
- 
+%0A
 'extensi
@@ -418,18 +418,23 @@
 tensions
-'%0A
+-tool' 
 and that
@@ -484,17 +484,17 @@
 to visit
- 
+%0A
 called '
@@ -506,20 +506,20 @@
 txt'
-%0A
+ 
 in the 
-same
+data
  dir
@@ -528,22 +528,8 @@
 tory
- as the script
 .%0A%22%22
@@ -693,16 +693,16 @@
 ITest):%0A
+
   %22%22%22Tes
@@ -724,233 +724,8 @@
 .%22%22%22
-%0A  # TODO: provide a way in pyauto to pass args to a test and take these as args%0A  extensions_dir_ = 'extensions'  # The directory of extensions%0A  urls_file_ = 'urls.txt'         # The file which holds a list of urls to visit
 %0A%0A  
@@ -3243,39 +3243,237 @@
 urls%22%22%22%0A    
-self.assertTrue
+# TODO: provide a way in pyauto to pass args to a test - take these as args%0A    extensions_dir = os.path.join(self.DataDir(), 'extensions-tool')%0A    urls_file = os.path.join(self.DataDir(), 'urls.txt')%0A%0A    assert
 (os.path.exi
@@ -3476,21 +3476,16 @@
 .exists(
-self.
 extensio
@@ -3490,22 +3490,19 @@
 ions_dir
-_
 ),%0A
-  
         
@@ -3548,21 +3548,16 @@
 abspath(
-self.
 extensio
@@ -3566,31 +3566,21 @@
 _dir
-_
 ))%0A    
-self.
 assert
-True
 (os.
@@ -3591,21 +3591,16 @@
 .exists(
-self.
 urls_fil
@@ -3604,14 +3604,11 @@
 file
-_
 ),%0A
-  
     
@@ -3659,21 +3659,16 @@
 abspath(
-self.
 urls_fil
@@ -3668,17 +3668,16 @@
 rls_file
-_
 ))%0A%0A    
@@ -3789,21 +3789,16 @@
    open(
-self.
 urls_fil
@@ -3798,17 +3798,16 @@
 rls_file
-_
 ).readli
@@ -3881,21 +3881,16 @@
 th.join(
-self.
 extensio
@@ -3895,17 +3895,16 @@
 ions_dir
-_
 , '*.crx

Move pyauto extensions.py expected files to data dir.