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__': |