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