Home | History | Annotate | Download | only in functional
      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 """
      8 Stess Tests for Google Chrome.
      9 
     10 This script runs 4 different stress tests:
     11 1. Plugin stress.
     12 2. Back and forward stress.
     13 3. Download stress.
     14 4. Preference stress.
     15 
     16 After every cycle (running all 4 stress tests) it checks for crashes.
     17 If there are any crashes, the script generates a report, uploads it to
     18 a server and mails about the crash and the link to the report on the server.
     19 Apart from this whenever the test stops on mac it looks for and reports
     20 zombies.
     21 
     22 Prerequisites:
     23 Test needs the following files/folders in the Data dir.
     24 1. A crash_report tool in "pyauto_private/stress/mac" folder for use on Mac.
     25 2. A "downloads" folder containing stress_downloads and all the files
     26    referenced in it.
     27 3. A pref_dict file in "pyauto_private/stress/mac" folder.
     28 4. A "plugin" folder containing doubleAnimation.xaml, flash.swf, FlashSpin.swf,
     29    generic.html, get_flash_player.gif, js-invoker.swf, mediaplayer.wmv,
     30    NavigatorTicker11.class, Plugins_page.html, sample5.mov, silverlight.xaml,
     31    silverlight.js, embed.pdf, plugins_page.html and test6.swf.
     32 5. A stress_pref file in "pyauto_private/stress".
     33 """
     34 
     35 
     36 import commands
     37 import glob
     38 import logging
     39 import os
     40 import random
     41 import re
     42 import shutil
     43 import sys
     44 import time
     45 import urllib
     46 import test_utils
     47 import subprocess
     48 
     49 import pyauto_functional
     50 import pyauto
     51 import pyauto_utils
     52 
     53 
     54 CRASHES = 'crashes'  # Name of the folder to store crashes
     55 
     56 
     57 class StressTest(pyauto.PyUITest):
     58   """Run all the stress tests."""
     59 
     60   flash_url1 = pyauto.PyUITest.GetFileURLForPath(
     61       os.path.join(pyauto.PyUITest.DataDir(), 'plugin', 'flash.swf'))
     62   flash_url2 = pyauto.PyUITest.GetFileURLForPath(
     63       os.path.join(pyauto.PyUITest.DataDir(),'plugin', 'js-invoker.swf'))
     64   flash_url3 = pyauto.PyUITest.GetFileURLForPath(
     65       os.path.join(pyauto.PyUITest.DataDir(), 'plugin', 'generic.html'))
     66   plugin_url = pyauto.PyUITest.GetFileURLForPath(
     67       os.path.join(pyauto.PyUITest.DataDir(), 'plugin', 'plugins_page.html'))
     68   empty_url = pyauto.PyUITest.GetFileURLForPath(
     69       os.path.join(pyauto.PyUITest.DataDir(), 'empty.html'))
     70   download_url1 = pyauto.PyUITest.GetFileURLForPath(
     71       os.path.join(pyauto.PyUITest.DataDir(), 'downloads', 'a_zip_file.zip'))
     72   download_url2 = pyauto.PyUITest.GetFileURLForPath(
     73       os.path.join(pyauto.PyUITest.DataDir(),'zip', 'test.zip'))
     74   file_list = pyauto.PyUITest.EvalDataFrom(
     75       os.path.join(pyauto.PyUITest.DataDir(), 'downloads', 'stress_downloads'))
     76   symbols_dir = os.path.join(os.getcwd(), 'Build_Symbols')
     77   stress_pref = pyauto.PyUITest.EvalDataFrom(
     78       os.path.join(pyauto.PyUITest.DataDir(), 'pyauto_private', 'stress',
     79                    'stress_pref'))
     80   breakpad_dir = None
     81   chrome_version = None
     82   bookmarks_list = []
     83 
     84 
     85   def _FuncDir(self):
     86     """Returns the path to the functional dir chrome/test/functional."""
     87     return os.path.dirname(__file__)
     88 
     89   def _DownloadSymbols(self):
     90     """Downloads the symbols for the build being tested."""
     91     download_location = os.path.join(os.getcwd(), 'Build_Symbols')
     92     if os.path.exists(download_location):
     93       shutil.rmtree(download_location)
     94     os.makedirs(download_location)
     95 
     96     url = self.stress_pref['symbols_dir'] + self.chrome_version
     97     # TODO: Add linux symbol_files
     98     if self.IsWin():
     99       url = url + '/win/'
    100       symbol_files = ['chrome.dll.pdb', 'chrome.exe.pdb']
    101     elif self.IsMac():
    102       url = url + '/mac/'
    103       symbol_files = map(urllib.quote,
    104                          ['Google Chrome Framework.framework',
    105                           'Google Chrome Helper.app',
    106                           'Google Chrome.app',
    107                           'crash_inspector',
    108                           'crash_report_sender',
    109                           'ffmpegsumo.so',
    110                           'libplugin_carbon_interpose.dylib'])
    111       index = 0
    112       symbol_files = ['%s-%s-i386.breakpad' % (sym_file, self.chrome_version) \
    113                       for sym_file in symbol_files]
    114       logging.info(symbol_files)
    115 
    116     for sym_file in symbol_files:
    117       sym_url = url + sym_file
    118       logging.info(sym_url)
    119       download_sym_file = os.path.join(download_location, sym_file)
    120       logging.info(download_sym_file)
    121       urllib.urlretrieve(sym_url, download_sym_file)
    122 
    123   def setUp(self):
    124     pyauto.PyUITest.setUp(self)
    125     self.breakpad_dir = self._CrashDumpFolder()
    126     self.chrome_version = self.GetBrowserInfo()['properties']['ChromeVersion']
    127 
    128   # Plugin stress functions
    129 
    130   def _CheckForPluginProcess(self, plugin_name):
    131     """Checks if a particular plugin process exists.
    132 
    133     Args:
    134       plugin_name : plugin process which should be running.
    135     """
    136     process = self.GetBrowserInfo()['child_processes']
    137     self.assertTrue([x for x in process
    138                      if x['type'] == 'Plug-in' and
    139                      x['name'] == plugin_name])
    140 
    141   def _GetPluginProcessId(self, plugin_name):
    142     """Get Plugin process id.
    143 
    144     Args:
    145       plugin_name: Plugin whose pid is expected.
    146                     Eg: "Shockwave Flash"
    147 
    148     Returns:
    149       Process id if the plugin process is running.
    150       None otherwise.
    151     """
    152     for process in self.GetBrowserInfo()['child_processes']:
    153       if process['type'] == 'Plug-in' and \
    154          re.search(plugin_name, process['name']):
    155         return process['pid']
    156     return None
    157 
    158   def _CloseAllTabs(self):
    159     """Close all but one tab in first window."""
    160     tab_count = self.GetTabCount(0)
    161     for tab_index in xrange(tab_count - 1, 0, -1):
    162       self.CloseTab(tab_index)
    163 
    164   def _CloseAllWindows(self):
    165     """Close all windows except one."""
    166     win_count = self.GetBrowserWindowCount()
    167     for windex in xrange(win_count - 1, 0, -1):
    168       self.RunCommand(pyauto.IDC_CLOSE_WINDOW, windex)
    169 
    170   def _ReloadAllTabs(self):
    171     """Reload all the tabs in first window."""
    172     for tab_index in range(self.GetTabCount()):
    173       self.ReloadTab(tab_index)
    174 
    175   def _LoadFlashInMultipleTabs(self):
    176     """Load Flash in multiple tabs in first window."""
    177     self.NavigateToURL(self.empty_url)
    178     # Open 18 tabs with flash
    179     for _ in range(9):
    180       self.AppendTab(pyauto.GURL(self.flash_url1))
    181       self.AppendTab(pyauto.GURL(self.flash_url2))
    182 
    183   def _OpenAndCloseMultipleTabsWithFlash(self):
    184     """Stress test for flash in multiple tabs."""
    185     logging.info("In _OpenAndCloseMultipleWindowsWithFlash.")
    186     self._LoadFlashInMultipleTabs()
    187     self._CheckForPluginProcess('Shockwave Flash')
    188     self._CloseAllTabs()
    189 
    190   def _OpenAndCloseMultipleWindowsWithFlash(self):
    191     """Stress test for flash in multiple windows."""
    192     logging.info('In _OpenAndCloseMultipleWindowsWithFlash.')
    193     # Open 5 Normal and 4 Incognito windows
    194     for tab_index in range(1, 10):
    195       if tab_index < 6:
    196         self.OpenNewBrowserWindow(True)
    197       else:
    198         self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW)
    199       self.NavigateToURL(self.flash_url2, tab_index, 0)
    200       self.AppendTab(pyauto.GURL(self.flash_url2), tab_index)
    201     self._CloseAllWindows()
    202 
    203   def _OpenAndCloseMultipleTabsWithMultiplePlugins(self):
    204     """Stress test using multiple plugins in multiple tabs."""
    205     logging.info('In _OpenAndCloseMultipleTabsWithMultiplePlugins.')
    206     # Append 4 tabs with URL
    207     for _ in range(5):
    208       self.AppendTab(pyauto.GURL(self.plugin_url))
    209     self._CloseAllTabs()
    210 
    211   def _OpenAndCloseMultipleWindowsWithMultiplePlugins(self):
    212     """Stress test using multiple plugins in multiple windows."""
    213     logging.info('In _OpenAndCloseMultipleWindowsWithMultiplePlugins.')
    214     # Open 4 windows with URL
    215     for tab_index in range(1, 5):
    216       if tab_index < 6:
    217         self.OpenNewBrowserWindow(True)
    218       else:
    219         self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW)
    220       self.NavigateToURL(self.plugin_url, tab_index, 0)
    221     self._CloseAllWindows()
    222 
    223   def _KillAndReloadFlash(self):
    224     """Stress test by killing flash process and reloading tabs."""
    225     self._LoadFlashInMultipleTabs()
    226     flash_process_id1 = self._GetPluginProcessId('Shockwave Flash')
    227     self.Kill(flash_process_id1)
    228     self._ReloadAllTabs()
    229     self._CloseAllTabs()
    230 
    231   def _KillAndReloadRenderersWithFlash(self):
    232     """Stress test by killing renderer processes and reloading tabs."""
    233     logging.info('In _KillAndReloadRenderersWithFlash')
    234     self._LoadFlashInMultipleTabs()
    235     info = self.GetBrowserInfo()
    236     # Kill all renderer processes
    237     for tab_index in range(self.GetTabCount(0)):
    238       self.KillRendererProcess(
    239           info['windows'][0]['tabs'][tab_index]['renderer_pid'])
    240     self._ReloadAllTabs()
    241     self._CloseAllTabs()
    242 
    243   def _TogglePlugin(self, plugin_name):
    244     """Toggle plugin status.
    245 
    246     Args:
    247       plugin_name: Name of the plugin to toggle.
    248     """
    249     plugins = self.GetPluginsInfo().Plugins()
    250     for item in range(len(plugins)):
    251       if re.search(plugin_name, plugins[item]['name']):
    252         if plugins[item]['enabled']:
    253           self.DisablePlugin(plugins[item]['path'])
    254         else:
    255           self.EnablePlugin(plugins[item]['path'])
    256 
    257   def _ToggleAndReloadFlashPlugin(self):
    258     """Toggle flash and reload all tabs."""
    259     logging.info('In _ToggleAndReloadFlashPlugin')
    260     for _ in range(10):
    261       self.AppendTab(pyauto.GURL(self.flash_url3))
    262     # Disable Flash Plugin
    263     self._TogglePlugin('Shockwave Flash')
    264     self._ReloadAllTabs()
    265     # Enable Flash Plugin
    266     self._TogglePlugin('Shockwave Flash')
    267     self._ReloadAllTabs()
    268     self._CloseAllTabs()
    269 
    270   # Downloads stress functions
    271 
    272   def _LoadDownloadsInMultipleTabs(self):
    273     """Load Downloads in multiple tabs in the same window."""
    274     # Open 15 tabs with downloads
    275     logging.info('In _LoadDownloadsInMultipleTabs')
    276     for tab_index in range(15):
    277       # We open an empty tab and then downlad a file from it.
    278       self.AppendTab(pyauto.GURL(self.empty_url))
    279       self.NavigateToURL(self.download_url1, 0, tab_index + 1)
    280       self.AppendTab(pyauto.GURL(self.empty_url))
    281       self.NavigateToURL(self.download_url2, 0, tab_index + 2)
    282 
    283   def _OpenAndCloseMultipleTabsWithDownloads(self):
    284     """Download items in multiple tabs."""
    285     logging.info('In _OpenAndCloseMultipleTabsWithDownloads')
    286     self._LoadDownloadsInMultipleTabs()
    287     self._CloseAllTabs()
    288 
    289   def _OpenAndCloseMultipleWindowsWithDownloads(self):
    290     """Randomly have downloads in multiple windows."""
    291     logging.info('In _OpenAndCloseMultipleWindowsWithDownloads')
    292     # Open 15 Windows randomly on both regular and incognito with downloads
    293     for window_index in range(15):
    294       tick = round(random.random() * 100)
    295       if tick % 2 != 0:
    296         self.NavigateToURL(self.download_url2, 0, 0)
    297       else:
    298         self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW)
    299         self.AppendTab(pyauto.GURL(self.empty_url), 1)
    300         self.NavigateToURL(self.download_url2, 1, 1)
    301     self._CloseAllWindows()
    302 
    303   def _OpenAndCloseMultipleTabsWithMultipleDownloads(self):
    304     """Download multiple items in multiple tabs."""
    305     logging.info('In _OpenAndCloseMultipleTabsWithMultipleDownloads')
    306     self.NavigateToURL(self.empty_url)
    307     for _ in range(15):
    308       for file in self.file_list:
    309         count = 1
    310         url = self.GetFileURLForPath(
    311             os.path.join(self.DataDir(), 'downloads', file))
    312         self.AppendTab(pyauto.GURL(self.empty_url))
    313         self.NavigateToURL(url, 0, count)
    314         count = count + 1
    315       self._CloseAllTabs()
    316 
    317   def _OpenAndCloseMultipleWindowsWithMultipleDownloads(self):
    318     """Randomly multiple downloads in multiple windows."""
    319     logging.info('In _OpenAndCloseMultipleWindowsWithMultipleDownloads')
    320     for _ in range(15):
    321       for file in self.file_list:
    322         tick = round(random.random() * 100)
    323         url = self.GetFileURLForPath(
    324             os.path.join(self.DataDir(), 'downloads', file))
    325         if tick % 2!= 0:
    326           self.NavigateToURL(url, 0, 0)
    327         else:
    328           self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW)
    329           self.AppendTab(pyauto.GURL(self.empty_url), 1)
    330           self.NavigateToURL(url, 1, 1)
    331     self._CloseAllWindows()
    332 
    333   # Back and Forward stress functions
    334 
    335   def _BrowserGoBack(self, window_index):
    336     """Go back in the browser history.
    337 
    338     Chrome has limitation on going back and can only go back 49 pages.
    339 
    340     Args:
    341       window_index: the index of the browser window to work on.
    342     """
    343     for nback in range(48):  # Go back 48 times.
    344       if nback % 4 == 0:   # Bookmark every 5th url when going back.
    345         self._BookMarkEvery5thURL(window_index)
    346       self.TabGoBack(tab_index=0, windex=window_index)
    347 
    348   def _BrowserGoForward(self, window_index):
    349     """Go Forward in the browser history.
    350 
    351     Chrome has limitation on going back and can only go back 49 pages.
    352 
    353     Args:
    354       window_index: the index of the browser window to work on.
    355     """
    356     for nforward in range(48):  # Go back 48 times.
    357       if nforward % 4 == 0:  # Bookmark every 5th url when going Forward
    358            self._BookMarkEvery5thURL(window_index)
    359       self.TabGoForward(tab_index=0, windex=window_index)
    360 
    361   def _AddToListAndBookmark(self, newname, url):
    362     """Bookmark the url to bookmarkbar and to he list of bookmarks.
    363 
    364     Args:
    365       newname: the name of the bookmark.
    366       url: the url to bookmark.
    367     """
    368     bookmarks = self.GetBookmarkModel()
    369     bar_id = bookmarks.BookmarkBar()['id']
    370     self.AddBookmarkURL(bar_id, 0, newname, url)
    371     self.bookmarks_list.append(newname)
    372 
    373   def _RemoveFromListAndBookmarkBar(self, name):
    374     """Remove the bookmark bor and bookmarks list.
    375 
    376     Args:
    377       name: the name of bookmark to remove.
    378     """
    379     bookmarks = self.GetBookmarkModel()
    380     node = bookmarks.FindByTitle(name)
    381     self.RemoveBookmark(node[0]['id'])
    382     self.bookmarks_list.remove(name)
    383 
    384   def _DuplicateBookmarks(self, name):
    385     """Find duplicate bookmark in the bookmarks list.
    386 
    387     Args:
    388       name: name of the bookmark.
    389 
    390     Returns:
    391       True if it's a duplicate.
    392     """
    393     for index in (self.bookmarks_list):
    394       if index ==  name:
    395         return True
    396     return False
    397 
    398   def _BookMarkEvery5thURL(self, window_index):
    399     """Check for duplicate in list and bookmark current url.
    400     If its the first time and list is empty add the bookmark.
    401     If its a duplicate remove the bookmark.
    402     If its new tab page move over.
    403 
    404     Args:
    405       window_index: the index of the browser window to work on.
    406     """
    407     tab_title = self.GetActiveTabTitle(window_index)  # get the page title
    408     url = self.GetActiveTabURL(window_index).spec()  # get the page url
    409     if not self.bookmarks_list:
    410       self._AddToListAndBookmark(tab_title, url)  # first run bookmark the url
    411       return
    412     elif self._DuplicateBookmarks(tab_title):
    413       self._RemoveFromListAndBookmarkBar(tab_title)
    414       return
    415     elif tab_title == 'New Tab':  # new tab page pass over
    416       return
    417     else:
    418       # new bookmark add it to bookmarkbar
    419       self._AddToListAndBookmark(tab_title, url)
    420       return
    421 
    422   def _ReadFileAndLoadInNormalAndIncognito(self):
    423     """Read urls and load them in normal and incognito window.
    424     We load 96 urls only as we can go back and forth 48 times.
    425     Uses time to get different urls in normal and incognito window
    426     The source file is taken from stress folder in /data folder.
    427     """
    428     # URL source from stress folder in data folder
    429     data_file = os.path.join(self.DataDir(), 'pyauto_private', 'stress',
    430                              'urls_and_titles')
    431     url_data = self.EvalDataFrom(data_file)
    432     urls = url_data.keys()
    433     i = 0
    434     ticks = int(time.time())  # get the latest time.
    435     for url in urls:
    436       if i <= 96 :  # load only 96 urls.
    437         if ticks % 2 == 0:  # loading in Incognito and Normal window.
    438           self.NavigateToURL(url)
    439         else:
    440           self.NavigateToURL(url, 1, 0)
    441       else:
    442         break
    443       ticks = ticks - 1
    444       i += 1
    445     return
    446 
    447   def _StressTestNavigation(self):
    448     """ This is the method from where various navigations are called.
    449     First we load the urls then call navigete back and forth in
    450     incognito window then in normal window.
    451     """
    452     self._ReadFileAndLoadInNormalAndIncognito()  # Load the urls.
    453     self._BrowserGoBack(1)  # Navigate back in incognito window.
    454     self._BrowserGoForward(1)  # Navigate forward in incognito window
    455     self._BrowserGoBack(0)  # Navigate back in normal window
    456     self._BrowserGoForward(0)  # Navigate forward in normal window
    457 
    458   # Preference stress functions
    459 
    460   def _RandomBool(self):
    461     """For any preferences bool value, it takes True or False value.
    462     We are generating random True or False value.
    463     """
    464     return random.randint(0, 1) == 1
    465 
    466   def _RandomURL(self):
    467     """Some of preferences take string url, so generating random url here."""
    468     # Site list
    469     site_list = ['test1.html', 'test2.html','test3.html','test4.html',
    470                  'test5.html', 'test7.html', 'test6.html']
    471     random_site = random.choice(site_list)
    472     # Returning a url of random site
    473     return self.GetFileURLForPath(os.path.join(self.DataDir(), random_site))
    474 
    475   def _RandomURLArray(self):
    476     """Returns a list of 10 random URLs."""
    477     return [self._RandomURL() for _ in range(10)]
    478 
    479   def _RandomInt(self, max_number):
    480     """Some of the preferences takes integer value.
    481     Eg: If there are three options, we generate random
    482     value for any option.
    483 
    484     Arg:
    485       max_number: The number of options that a preference has.
    486     """
    487     return random.randrange(1, max_number)
    488 
    489   def _RandomDownloadDir(self):
    490     """Returns a random download directory."""
    491     return random.choice(['dl_dir1', 'dl_dir2', 'dl_dir3',
    492                           'dl_dir4', 'dl_dir5'])
    493 
    494   def _SetPref(self):
    495     """Reads the preferences from file and
    496        sets the preferences to Chrome.
    497     """
    498     raw_dictionary = self.EvalDataFrom(os.path.join(self.DataDir(),
    499         'pyauto_private', 'stress', 'pref_dict'))
    500     value_dictionary = {}
    501 
    502     for key, value in raw_dictionary.iteritems():
    503       if value == 'BOOL':
    504          value_dictionary[key] = self._RandomBool()
    505       elif value == 'STRING_URL':
    506          value_dictionary[key] = self._RandomURL()
    507       elif value == 'ARRAY_URL':
    508          value_dictionary[key] = self._RandomURLArray()
    509       elif value == 'STRING_PATH':
    510          value_dictionary[key] = self._RandomDownloadDir()
    511       elif value[0:3] == 'INT':
    512          # Normally we difine INT datatype with number of options,
    513          # so parsing number of options and selecting any of them
    514          # randomly.
    515          value_dictionary[key] = 1
    516          max_number = raw_dictionary[key][3:4]
    517          if not max_number == 1:
    518            value_dictionary[key]= self._RandomInt(int(max_number))
    519       self.SetPrefs(getattr(pyauto,key), value_dictionary[key])
    520 
    521     return value_dictionary
    522 
    523   # Crash reporting functions
    524 
    525   def _CrashDumpFolder(self):
    526     """Get the breakpad folder.
    527 
    528     Returns:
    529       The full path of the Crash Reports folder.
    530     """
    531     breakpad_folder = self.GetBrowserInfo()['properties']['DIR_CRASH_DUMPS']
    532     self.assertTrue(breakpad_folder, 'Cannot figure crash dir')
    533     return breakpad_folder
    534 
    535   def _DeleteDumps(self):
    536     """Delete all the dump files in teh Crash Reports folder."""
    537     # should be called at the start of stress run
    538     if os.path.exists(self.breakpad_dir):
    539       logging.info('xxxxxxxxxxxxxxxINSIDE DELETE DUMPSxxxxxxxxxxxxxxxxx')
    540       if self.IsMac():
    541         shutil.rmtree(self.breakpad_dir)
    542       elif self.IsWin():
    543         files = os.listdir(self.breakpad_dir)
    544         for file in files:
    545           os.remove(file)
    546 
    547     first_crash = os.path.join(os.getcwd(), '1stcrash')
    548     crashes_dir = os.path.join(os.getcwd(), 'crashes')
    549     if (os.path.exists(crashes_dir)):
    550       shutil.rmtree(crashes_dir)
    551       shutil.rmtree(first_crash)
    552 
    553   def _SymbolicateCrashDmp(self, dmp_file, symbols_dir, output_file):
    554     """Generate symbolicated crash report.
    555 
    556     Args:
    557       dmp_file: the dmp file to symbolicate.
    558       symbols_dir: the directory containing the symbols.
    559       output_file: the output file.
    560 
    561     Returns:
    562       Crash report text.
    563     """
    564     report = ''
    565     if self.IsWin():
    566       windbg_cmd = [
    567           os.path.join('C:', 'Program Files', 'Debugging Tools for Windows',
    568                        'windbg.exe'),
    569           '-Q',
    570           '-y',
    571           '\"',
    572           symbols_dir,
    573           '\"',
    574           '-c',
    575           '\".ecxr;k50;.logclose;q\"',
    576           '-logo',
    577           output_file,
    578           '-z',
    579           '\"',
    580           dmp_file,
    581           '\"']
    582       subprocess.call(windbg_cmd)
    583       # Since we are directly writing the info into output_file,
    584       # we just need to copy that in to report
    585       report = open(output_file, 'r').read()
    586 
    587     elif self.IsMac():
    588       crash_report = os.path.join(self.DataDir(), 'pyauto_private', 'stress',
    589                                   'mac', 'crash_report')
    590       for i in range(5):  # crash_report doesn't work sometimes. So we retry
    591         report = test_utils.Shell2(
    592             '%s -S "%s" "%s"' % (crash_report, symbols_dir, dmp_file))[0]
    593         if len(report) < 200:
    594           try_again = 'Try %d. crash_report didn\'t work out. Trying again', i
    595           logging.info(try_again)
    596         else:
    597           break
    598       open(output_file, 'w').write(report)
    599     return report
    600 
    601   def _SaveSymbols(self, symbols_dir, dump_dir=' ', multiple_dumps=True):
    602     """Save the symbolicated files for all crash dumps.
    603 
    604     Args:
    605       symbols_dir: the directory containing the symbols.
    606       dump_dir: Path to the directory holding the crash dump files.
    607       multiple_dumps: True if we are processing multiple dump files,
    608                       False if we are processing only the first crash.
    609     """
    610     if multiple_dumps:
    611       dump_dir = self.breakpad_dir
    612 
    613     if not os.path.isdir(CRASHES):
    614       os.makedirs(CRASHES)
    615 
    616     # This will be sent to the method by the caller.
    617     dmp_files = glob.glob(os.path.join(dump_dir, '*.dmp'))
    618     for dmp_file in dmp_files:
    619       dmp_id = os.path.splitext(os.path.basename(dmp_file))[0]
    620       if multiple_dumps:
    621         report_folder = CRASHES
    622       else:
    623         report_folder = dump_dir
    624       report_fname = os.path.join(report_folder,
    625                                   '%s.txt' % (dmp_id))
    626       report = self._SymbolicateCrashDmp(dmp_file, symbols_dir,
    627                                          report_fname)
    628       if report == '':
    629         logging.info('Crash report is empty.')
    630       # This is for copying the original dumps.
    631       if multiple_dumps:
    632         shutil.copy2(dmp_file, CRASHES)
    633 
    634   def _GetFirstCrashDir(self):
    635     """Get first crash file in the crash folder.
    636     Here we create the 1stcrash directory which holds the
    637     first crash report, which will be attached to the mail.
    638     """
    639     breakpad_folder = self.breakpad_dir
    640     dump_list = glob.glob1(breakpad_folder,'*.dmp')
    641     dump_list.sort(key=lambda s: os.path.getmtime(os.path.join(
    642                                                   breakpad_folder, s)))
    643     first_crash_file = os.path.join(breakpad_folder, dump_list[0])
    644 
    645     if not os.path.isdir('1stcrash'):
    646       os.makedirs('1stcrash')
    647     shutil.copy2(first_crash_file, '1stcrash')
    648     first_crash_dir = os.path.join(os.getcwd(), '1stcrash')
    649     return first_crash_dir
    650 
    651   def _GetFirstCrashFile(self):
    652     """Get first crash file in the crash folder."""
    653     first_crash_dir = os.path.join(os.getcwd(), '1stcrash')
    654     for each in os.listdir(first_crash_dir):
    655       if each.endswith('.txt'):
    656         first_crash_file = each
    657         return os.path.join(first_crash_dir, first_crash_file)
    658 
    659   def _ProcessOnlyFirstCrash(self):
    660     """ Process only the first crash report for email."""
    661     first_dir = self._GetFirstCrashDir()
    662     self._SaveSymbols(self.symbols_dir, first_dir, False)
    663 
    664   def _GetOSName(self):
    665     """Returns the OS type we are running this script on."""
    666     os_name = ''
    667     if self.IsMac():
    668       os_number = commands.getoutput('sw_vers -productVersion | cut -c 1-4')
    669       if os_number == '10.6':
    670         os_name = 'Snow_Leopard'
    671       elif os_number == '10.5':
    672         os_name = 'Leopard'
    673     elif self.IsWin():
    674       # TODO: Windows team need to find the way to get OS name
    675       os_name = 'Windows'
    676       if platform.version()[0] == '5':
    677         os_name = os_name + '_XP'
    678       else:
    679         os_name = os_name + '_Vista/Win7'
    680     return os_name
    681 
    682   def _ProcessUploadAndEmailCrashes(self):
    683     """Upload the crashes found and email the team about this."""
    684     logging.info('#########INSIDE _ProcessUploadAndEmailCrashes#########')
    685     try:
    686       build_version = self.chrome_version
    687       self._SaveSymbols(self.symbols_dir)
    688       self._ProcessOnlyFirstCrash()
    689       file_to_attach =  self._GetFirstCrashFile()
    690       # removing the crash_txt for now,
    691       # since we are getting UnicodeDecodeError
    692       # crash_txt = open(file_to_attach).read()
    693     except ValueError:
    694       test_utils.SendMail(self.stress_pref['mailing_address'],
    695                           self.stress_pref['mailing_address'],
    696                           "We don't have build version",
    697                           "BROWSER CRASHED, PLEASE CHECK",
    698                           self.stress_pref['smtp'])
    699     # Move crash reports and dumps to server
    700     os_name = self._GetOSName()
    701     dest_dir = build_version + '_' + os_name
    702     if (test_utils.Shell2(self.stress_pref['script'] % (CRASHES, dest_dir))):
    703       logging.info('Copy Complete')
    704     upload_dir= self.stress_pref['upload_dir'] + dest_dir
    705     num_crashes =  '\n \n Number of Crashes :' + \
    706                    str(len(glob.glob1(self.breakpad_dir, '*.dmp')))
    707     mail_content =  '\n\n Crash Report URL :' + upload_dir + '\n' + \
    708                     num_crashes + '\n\n' # + crash_txt
    709     mail_subject = 'Stress Results :' + os_name + '_' + build_version
    710     # Sending mail with first crash report, # of crashes, location of upload
    711     test_utils.SendMail(self.stress_pref['mailing_address'],
    712                         self.stress_pref['mailing_address'],
    713                         mail_subject, mail_content,
    714                         self.stress_pref['smtp'], file_to_attach)
    715 
    716   def _ReportCrashIfAny(self):
    717     """Check for browser crashes and report."""
    718     if os.path.isdir(self.breakpad_dir):
    719       listOfDumps = glob.glob(os.path.join(self.breakpad_dir, '*.dmp'))
    720       if len(listOfDumps) > 0:
    721         logging.info('========== INSIDE REPORT CRASH++++++++++++++')
    722         # inform a method to process the dumps
    723         self._ProcessUploadAndEmailCrashes()
    724 
    725   # Test functions
    726 
    727   def _PrefStress(self):
    728     """Stress preferences."""
    729     default_prefs = self.GetPrefsInfo()
    730     pref_dictionary = self._SetPref()
    731     for key, value in pref_dictionary.iteritems():
    732       self.assertEqual(value, self.GetPrefsInfo().Prefs(
    733                        getattr(pyauto, key)))
    734 
    735     for key, value in pref_dictionary.iteritems():
    736       self.SetPrefs(getattr(pyauto, key),
    737                     default_prefs.Prefs(getattr(pyauto, key)))
    738 
    739   def _NavigationStress(self):
    740     """Run back and forward stress in normal and incognito window."""
    741     self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW)
    742     self._StressTestNavigation()
    743 
    744   def _DownloadStress(self):
    745     """Run all the Download stress test."""
    746     org_download_dir = self.GetDownloadDirectory().value()
    747     new_dl_dir = os.path.join(org_download_dir, 'My+Downloads Folder')
    748     os.path.exists(new_dl_dir) and shutil.rmtree(new_dl_dir)
    749     os.makedirs(new_dl_dir)
    750     self.SetPrefs(pyauto.kDownloadDefaultDirectory, new_dl_dir)
    751     self._OpenAndCloseMultipleTabsWithDownloads()
    752     self._OpenAndCloseMultipleWindowsWithDownloads()
    753     self._OpenAndCloseMultipleTabsWithMultipleDownloads()
    754     self._OpenAndCloseMultipleWindowsWithMultipleDownloads()
    755     pyauto_utils.RemovePath(new_dl_dir)  # cleanup
    756     self.SetPrefs(pyauto.kDownloadDefaultDirectory, org_download_dir)
    757 
    758   def _PluginStress(self):
    759     """Run all the plugin stress tests."""
    760     self._OpenAndCloseMultipleTabsWithFlash()
    761     self._OpenAndCloseMultipleWindowsWithFlash()
    762     self._OpenAndCloseMultipleTabsWithMultiplePlugins()
    763     self._OpenAndCloseMultipleWindowsWithMultiplePlugins()
    764     self._KillAndReloadRenderersWithFlash()
    765     self._ToggleAndReloadFlashPlugin()
    766 
    767   def testStress(self):
    768     """Run all the stress tests for 24 hrs."""
    769     if self.GetBrowserInfo()['properties']['branding'] != 'Google Chrome':
    770       logging.info('This is not a branded build, so stopping the stress')
    771       return 1
    772     self._DownloadSymbols()
    773     run_number = 1
    774     start_time = time.time()
    775     while True:
    776       logging.info('run %d...' % run_number)
    777       run_number = run_number + 1
    778       if (time.time() - start_time) >= 24*60*60:
    779         logging.info('Its been 24hrs, so we break now.')
    780         break
    781       try:
    782         methods = [self._NavigationStress, self._DownloadStress,
    783                    self._PluginStress, self._PrefStress]
    784         random.shuffle(methods)
    785         for method in methods:
    786           method()
    787           logging.info('Method %s done' % method)
    788       except KeyboardInterrupt:
    789         logging.info('----------We got a KeyboardInterrupt-----------')
    790       except Exception, error:
    791         logging.info('-------------There was an ERROR---------------')
    792         logging.info(error)
    793 
    794       # Crash Reporting
    795       self._ReportCrashIfAny()
    796       self._DeleteDumps()
    797 
    798     if self.IsMac():
    799       zombie = 'ps -el | grep Chrom | grep -v grep | grep Z | wc -l'
    800       zombie_count = int(commands.getoutput(zombie))
    801       if zombie_count > 0:
    802         logging.info('WE HAVE ZOMBIES = %d' % zombie_count)
    803 
    804 
    805 if __name__ == '__main__':
    806   pyauto_functional.Main()
    807