OLD | NEW |
| (Empty) |
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() | |
OLD | NEW |