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"""
8Stess Tests for Google Chrome.
9
10This script runs 4 different stress tests:
111. Plugin stress.
122. Back and forward stress.
133. Download stress.
144. Preference stress.
15
16After every cycle (running all 4 stress tests) it checks for crashes.
17If there are any crashes, the script generates a report, uploads it to
18a server and mails about the crash and the link to the report on the server.
19Apart from this whenever the test stops on mac it looks for and reports
20zombies.
21
22Prerequisites:
23Test needs the following files/folders in the Data dir.
241. A crash_report tool in "pyauto_private/stress/mac" folder for use on Mac.
252. A "downloads" folder containing stress_downloads and all the files
26   referenced in it.
273. A pref_dict file in "pyauto_private/stress/mac" folder.
284. 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.
325. A stress_pref file in "pyauto_private/stress".
33"""
34
35
36import commands
37import glob
38import logging
39import os
40import random
41import re
42import shutil
43import sys
44import time
45import urllib
46import test_utils
47import subprocess
48
49import pyauto_functional
50import pyauto
51import pyauto_utils
52
53
54CRASHES = 'crashes'  # Name of the folder to store crashes
55
56
57class 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
805if __name__ == '__main__':
806  pyauto_functional.Main()
807