Index: tests/inbrowser_crash_test/crash_dump_tester.py |
diff --git a/tests/inbrowser_crash_test/crash_dump_tester.py b/tests/inbrowser_crash_test/crash_dump_tester.py |
index 37f47bdfa6b24eb12ab7587e875c69b44c5d17cc..6468c37728296b6f1ad466a2269c608bbd891a63 100644 |
--- a/tests/inbrowser_crash_test/crash_dump_tester.py |
+++ b/tests/inbrowser_crash_test/crash_dump_tester.py |
@@ -4,55 +4,22 @@ |
# found in the LICENSE file. |
import os |
+import shutil |
import subprocess |
import sys |
+import tempfile |
import time |
script_dir = os.path.dirname(__file__) |
sys.path.append(os.path.join(script_dir, '../../tools/browser_tester')) |
import browser_tester |
+import browsertester.browserlauncher |
# This script extends browser_tester to check for the presence of |
# Breakpad crash dumps. |
-# TODO(mseaborn): Change Chromium's crash_service.exe so that we can |
-# tell it to put dumps in a per-test temp directory. |
-def GetDumpDirs(): |
- if sys.platform == 'win32': |
- # We duplicate Chromium's logic for deciding where its per-user |
- # directory should be on Windows. We can remove this if we use a |
- # temp directory instead. |
- import win32com.shell.shell |
- import win32com.shell.shellcon |
- # This typically returns a pathname like |
- # 'C:\Documents and Settings\Username\Local Settings\Application Data'. |
- appdata_dir = win32com.shell.shell.SHGetFolderPath( |
- 0, win32com.shell.shellcon.CSIDL_LOCAL_APPDATA, 0, 0) |
- return [os.path.join(appdata_dir, app_name, 'User Data', 'Crash Reports') |
- for app_name in ('Chromium', os.path.join('Google', 'Chrome'))] |
- else: |
- # TODO(mseaborn): Handle other platforms when NaCl supports |
- # Breakpad crash reporting there. |
- return [] |
- |
- |
-def GetDumpFiles(): |
- file_list = [] |
- for dump_dir in GetDumpDirs(): |
- if os.path.exists(dump_dir): |
- for dump_file in sorted(os.listdir(dump_dir)): |
- file_list.append(os.path.join(dump_dir, dump_file)) |
- return file_list |
- |
- |
-def PrintDumps(desc, dump_files): |
- sys.stdout.write('crash_dump_tester: Found %i %s\n' % (len(dump_files), desc)) |
- for dump_file in dump_files: |
- sys.stdout.write(' %s\n' % dump_file) |
- |
- |
# This reads a file of lines containing 'key:value' pairs. |
# The file contains entries like the following: |
# plat:Win32 |
@@ -70,23 +37,19 @@ def ReadDumpTxtFile(filename): |
return dump_info |
-def StartCrashService(browser_path): |
+def StartCrashService(browser_path, dumps_dir, windows_pipe_name): |
if sys.platform == 'win32': |
# Find crash_service.exe relative to chrome.exe. This is a bit icky. |
browser_dir = os.path.dirname(browser_path) |
- proc = subprocess.Popen([os.path.join(browser_dir, 'crash_service.exe')]) |
+ proc = subprocess.Popen([os.path.join(browser_dir, 'crash_service.exe'), |
+ '--dumps-dir=%s' % dumps_dir, |
+ '--pipe-name=%s' % windows_pipe_name]) |
def Cleanup(): |
- try: |
- proc.terminate() |
- sys.stdout.write('crash_dump_tester: Stopped crash_service.exe\n') |
- except WindowsError: |
- # If the process has already exited, we will get an 'Access is |
- # denied' error. This can happen if another instance of |
- # crash_service.exe was already running, because our instance |
- # will fail to claim the named pipe. |
- # TODO(mseaborn): We could change crash_service.exe to create |
- # unique pipe names for testing purposes. |
- pass |
+ # Note that if the process has already exited, this will raise |
+ # an 'Access is denied' WindowsError exception, but |
+ # crash_service.exe is not supposed to do this and such |
+ # behaviour should make the test fail. |
+ proc.terminate() |
status = proc.wait() |
sys.stdout.write('crash_dump_tester: ' |
'crash_service.exe exited with status %s\n' % status) |
@@ -103,6 +66,16 @@ def StartCrashService(browser_path): |
return Cleanup |
+def GetDumpFiles(dumps_dir): |
+ all_files = [os.path.join(dumps_dir, dump_file) |
+ for dump_file in os.listdir(dumps_dir)] |
+ sys.stdout.write('crash_dump_tester: Found %i files\n' % len(all_files)) |
+ for dump_file in all_files: |
+ sys.stdout.write(' %s\n' % dump_file) |
+ return [dump_file for dump_file in all_files |
+ if dump_file.endswith('.dmp')] |
+ |
+ |
def Main(): |
parser = browser_tester.BuildArgParser() |
parser.add_option('--expected_crash_dumps', dest='expected_crash_dumps', |
@@ -110,36 +83,33 @@ def Main(): |
help='The number of crash dumps that we should expect') |
options, args = parser.parse_args() |
+ dumps_dir = tempfile.mkdtemp(prefix='nacl_crash_dump_tester_') |
+ # To get a guaranteed unique pipe name, use the base name of the |
+ # directory we just created. |
+ windows_pipe_name = r'\\.\pipe\%s_crash_service' % os.path.basename(dumps_dir) |
+ |
# This environment variable enables Breakpad crash dumping in |
# non-official Windows builds of Chromium. |
os.environ['CHROME_HEADLESS'] = '1' |
+ # Override the default (global) Windows pipe name that Chromium will |
+ # use for out-of-process crash reporting. |
+ os.environ['CHROME_BREAKPAD_PIPE_NAME'] = windows_pipe_name |
- dumps_before = GetDumpFiles() |
- PrintDumps('crash dump files before the test', dumps_before) |
- |
- cleanup_func = StartCrashService(options.browser_path) |
+ cleanup_func = StartCrashService(options.browser_path, dumps_dir, |
+ windows_pipe_name) |
try: |
result = browser_tester.Run(options.url, options) |
finally: |
cleanup_func() |
- dumps_after = GetDumpFiles() |
- PrintDumps('crash dump files after the test', dumps_after) |
- # Find the new files. This is only necessary because we are not |
- # using a clean temp directory. This is subject to a race condition |
- # if running crash dump tests concurrently. |
- dumps_diff = sorted(set(dumps_after).difference(dumps_before)) |
- PrintDumps('new crash dump files', dumps_diff) |
- new_dumps = [dump_file for dump_file in dumps_diff |
- if dump_file.endswith('.dmp')] |
- |
+ dmp_files = GetDumpFiles(dumps_dir) |
failed = False |
msg = ('crash_dump_tester: ERROR: Got %i crash dumps but expected %i\n' % |
- (len(new_dumps), options.expected_crash_dumps)) |
- if len(new_dumps) != options.expected_crash_dumps: |
+ (len(dmp_files), options.expected_crash_dumps)) |
+ if len(dmp_files) != options.expected_crash_dumps: |
sys.stdout.write(msg) |
failed = True |
- for dump_file in new_dumps: |
+ for dump_file in dmp_files: |
# The crash dumps should come in pairs of a .dmp and .txt file. |
second_file = dump_file[:-4] + '.txt' |
msg = ('crash_dump_tester: ERROR: File %r is missing a corresponding ' |
@@ -168,15 +138,8 @@ def Main(): |
result = 1 |
else: |
sys.stdout.write('crash_dump_tester: PASSED\n') |
- # Clean up the dump files only if we are sure we produced them. |
- for dump_file in dumps_diff: |
- try: |
- os.unlink(dump_file) |
- except Exception: |
- # Handle exception in case the file is locked. |
- sys.stdout.write('crash_dump_tester: Deleting %r failed, ' |
- 'but continuing anyway\n' % dump_file) |
+ browsertester.browserlauncher.RemoveDirectory(dumps_dir) |
return result |