Chromium Code Reviews| Index: snapshot/win/end_to_end_test.py |
| diff --git a/snapshot/win/end_to_end_test.py b/snapshot/win/end_to_end_test.py |
| index 0a13befd1b76fddc42bfe96c1dfc1a7512490138..07ff557feb1496f56be687299fc450313891b8ab 100644 |
| --- a/snapshot/win/end_to_end_test.py |
| +++ b/snapshot/win/end_to_end_test.py |
| @@ -15,12 +15,29 @@ |
| # limitations under the License. |
| import os |
| +import re |
| +import subprocess |
| import sys |
| +import tempfile |
| + |
| +g_temp_dirs = [] |
| + |
| + |
| +def MakeTempDir(): |
| + global g_temp_dirs |
| + new_dir = tempfile.mkdtemp() |
| + g_temp_dirs.append(new_dir) |
| + return new_dir |
| + |
| + |
| +def CleanUpTempDirs(): |
| + global g_temp_dirs |
| + for d in g_temp_dirs: |
| + subprocess.call(['rmdir', '/s', '/q', d], shell=True) |
| def FindInstalledWindowsApplication(app_path): |
| - search_paths = [os.getenv('PROGRAMFILES(X86)'), |
| - os.getenv('PROGRAMFILES'), |
| + search_paths = [os.getenv('PROGRAMFILES(X86)'), os.getenv('PROGRAMFILES'), |
|
Mark Mentovai
2015/10/09 18:34:04
I liked this better before, one element per line.
scottmg
2015/10/09 18:49:28
Yeah, I started using https://github.com/google/ya
|
| os.getenv('LOCALAPPDATA')] |
| search_paths += os.getenv('PATH', '').split(os.pathsep) |
| @@ -35,6 +52,9 @@ def FindInstalledWindowsApplication(app_path): |
| def GetCdbPath(): |
| + """Search in some reasonable places to find cdb.exe. Searches x64 before x86 |
| + and newer versions before older versions. |
| + """ |
| possible_paths = ( |
| os.path.join('Windows Kits', '10', 'Debuggers', 'x64'), |
| os.path.join('Windows Kits', '10', 'Debuggers', 'x86'), |
| @@ -42,14 +62,8 @@ def GetCdbPath(): |
| os.path.join('Windows Kits', '8.1', 'Debuggers', 'x86'), |
| os.path.join('Windows Kits', '8.0', 'Debuggers', 'x64'), |
| os.path.join('Windows Kits', '8.0', 'Debuggers', 'x86'), |
| - 'Debugging Tools For Windows (x64)', |
| - 'Debugging Tools For Windows (x86)', |
| - 'Debugging Tools For Windows', |
| - os.path.join('win_toolchain', 'vs2013_files', 'win8sdk', 'Debuggers', |
| - 'x64'), |
| - os.path.join('win_toolchain', 'vs2013_files', 'win8sdk', 'Debuggers', |
| - 'x86'), |
| - ) |
| + 'Debugging Tools For Windows (x64)', 'Debugging Tools For Windows (x86)', |
| + 'Debugging Tools For Windows',) |
| for possible_path in possible_paths: |
| app_path = os.path.join(possible_path, 'cdb.exe') |
| app_path = FindInstalledWindowsApplication(app_path) |
| @@ -58,10 +72,132 @@ def GetCdbPath(): |
| return None |
| +def GetDumpFromCrashyProgram(out_dir): |
| + """Initialize a crash database, run crashpad_handler, run crashy_program |
| + connecting to the crash_handler. Returns the minidump generated by |
| + crash_handler for further testing. |
| + """ |
| + pipe_name = r'\\.\pipe\end-to-end' |
|
Mark Mentovai
2015/10/09 18:34:05
Should this be a little more random? pid + 64 rand
scottmg
2015/10/09 18:49:28
Done.
|
| + |
| + test_database = MakeTempDir() |
| + |
| + try: |
| + if subprocess.call( |
| + [os.path.join(out_dir, 'crashpad_database_util.exe'), '--create', |
| + '--database=' + test_database]) != 0: |
| + print 'could not initialize report database' |
| + return None |
| + |
| + handler = subprocess.Popen([ |
| + os.path.join(out_dir, 'crashpad_handler.exe'), '--pipe-name=' + |
| + pipe_name, '--database=' + test_database |
|
Mark Mentovai
2015/10/09 18:34:05
'pipe_name=' and pipe_name should at least be on t
scottmg
2015/10/09 18:49:28
Done.
|
| + ]) |
| + |
| + subprocess.call([os.path.join(out_dir, 'crashy_program.exe'), pipe_name]) |
| + |
| + out = subprocess.check_output([ |
| + os.path.join(out_dir, 'crashpad_database_util.exe'), |
| + '--database=' + test_database, |
| + '--show-completed-reports', |
| + '--show-all-report-info', |
| + ]) |
| + for line in out.splitlines(): |
| + if line.strip().startswith('Path:'): |
| + return line.partition(':')[2].strip() |
| + |
| + finally: |
| + if handler: |
| + handler.kill() |
| + |
| + |
| +class CdbRun(object): |
| + """Run cdb.exe passing it the given commands and capturing the output. |
| + `Check()` searchs for regex patterns in sequence allowing verification of |
|
Mark Mentovai
2015/10/09 18:34:05
searches
scottmg
2015/10/09 18:49:28
Done.
|
| + expected output. |
| + """ |
| + def __init__(self, cdb_path, dump_path, *commands): |
| + # Run a command line that loads the dump, runs the specified commands, and |
| + # then quits, and capture the stdout. |
| + self.out = subprocess.check_output([cdb_path, '-z', dump_path, '-c', |
| + ';'.join(commands) + ';q']) |
|
Mark Mentovai
2015/10/09 18:34:05
Dunno about how cdb command lines should be escape
Mark Mentovai
2015/10/09 18:34:05
'-c' next to this, since this whole thing applies
scottmg
2015/10/09 18:49:28
Switched to just 'command'.
scottmg
2015/10/09 18:49:28
Done.
|
| + |
| + def Check(self, pattern, message): |
| + print ' ... ' + message, |
| + sys.stdout.flush() |
| + match_obj = re.search(pattern, self.out) |
| + if match_obj: |
| + # Matched. Consume up to end of match. |
| + self.out = self.out[match_obj.end(0):] |
| + print '\rok - ' |
|
Mark Mentovai
2015/10/09 18:34:04
'\rok - '
Not really sure what you’re going for h
scottmg
2015/10/09 18:49:28
I wanted the "ok"s before the names so they're eas
|
| + sys.stdout.flush() |
| + else: |
| + print '-'*80 |
|
Mark Mentovai
2015/10/09 18:34:05
Errors to stderr?
scottmg
2015/10/09 18:49:28
Done.
|
| + print 'did not match:\n %s' % pattern |
| + print '-'*80 |
| + print 'remaining output was:\n %s' % self.out |
| + print '-'*80 |
| + sys.exit(1) |
| + |
| + |
| +def RunTests(cdb_path, dump_path): |
| + """Runs various tests in sequence. Runs a new cdb instance on the dump for |
| + each block of tests to reduce the chances that output from one command is |
| + confused for output from another. |
| + """ |
| + out = CdbRun(cdb_path, dump_path, '.ecxr') |
| + out.Check('This dump file has an exception of interest stored in it', |
| + 'captured exception') |
| + out.Check( |
| + 'crashy_program!crashpad::`anonymous namespace\'::SomeCrashyFunction', |
| + 'exception at correct location') |
| + |
| + out = CdbRun(cdb_path, dump_path, '!peb') |
| + out.Check(r'PEB at', 'found the PEB') |
| + out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+', 'PEB_LDR_DATA saved') |
| + out.Check(r'Base TimeStamp Module', 'module list present') |
| + out.Check(r'CommandLine: *\'.*crashy_program.exe *\\\\.\\pipe\\end-to-end', |
| + 'some PEB data is correct') |
| + out.Check(r'SystemRoot=C:\\Windows', 'some of environment captured') |
| + |
| + out = CdbRun(cdb_path, dump_path, '!teb') |
| + out.Check(r'TEB at', 'found the TEB') |
| + out.Check(r'ExceptionList:\s+[0-9a-fA-F]+', 'some valid teb data') |
| + out.Check(r'LastErrorValue:\s+2', 'correct LastErrorValue') |
| + |
| + out = CdbRun(cdb_path, dump_path, '!gle') |
| + out.Check('LastErrorValue: \(Win32\) 0x2 \(2\) - The system cannot find the ' |
| + 'file specified.', '!gle gets last error') |
| + out.Check('LastStatusValue: \(NTSTATUS\) 0xc000000f - {File Not Found} The ' |
| + 'file %hs does not exist.', '!gle gets last ntstatus') |
| + |
| + # Locks. |
| + out = CdbRun(cdb_path, dump_path, '!locks') |
| + out.Check(r'CritSec crashy_program!crashpad::`anonymous namespace\'::' |
| + r'g_test_critical_section', 'lock was captured') |
| + out.Check(r'\*\*\* Locked', 'lock debug info was captured, and is locked') |
| + |
| + |
| def main(args): |
| - cdb_path = GetCdbPath() |
| - print 'cdb_path:', cdb_path |
| - return 0 |
| + try: |
| + if len(args) != 1: |
| + print 'must supply out dir' |
|
Mark Mentovai
2015/10/09 18:34:05
stderr for these
scottmg
2015/10/09 18:49:28
Done.
|
| + return 1 |
| + |
| + cdb_path = GetCdbPath() |
| + if not cdb_path: |
| + print 'could not find cdb' |
| + return 1 |
| + |
| + dump_path = GetDumpFromCrashyProgram(args[0]) |
| + if not dump_path: |
| + return 1 |
| + |
| + RunTests(cdb_path, dump_path) |
| + |
| + return 0 |
| + finally: |
| + CleanUpTempDirs() |
| if __name__ == '__main__': |