Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(304)

Side by Side Diff: mojo/tools/run_mojo_python_tests.py

Issue 344233008: Rework run-mojo-python-tests to use layout-test-compatible json output. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: patch for review Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2014 The Chromium Authors. All rights reserved. 2 # Copyright 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 import optparse 6 import json
Paweł Hajdan Jr. 2014/06/25 01:01:03 nit: Sort.
Dirk Pranke 2014/06/25 01:05:11 whoops. will fix, thanks!
7 import argparse
7 import os 8 import os
8 import re 9 import re
9 import sys 10 import sys
11 import time
10 import unittest 12 import unittest
11 13
12 14
13 def main(): 15 def main():
14 parser = optparse.OptionParser() 16 parser = argparse.ArgumentParser()
15 parser.usage = 'run_mojo_python_tests.py [options] [tests...]' 17 parser.usage = 'run_mojo_python_tests.py [options] [tests...]'
16 parser.add_option('-v', '--verbose', action='count', default=0) 18 parser.add_argument('-v', '--verbose', action='count', default=0)
17 parser.add_option('--unexpected-failures', metavar='FILENAME', action='store', 19 parser.add_argument('--metadata', action='append', default=[],
18 help=('path to write a list of any tests that fail ' 20 help=('optional key=value metadata that will be stored '
19 'unexpectedly.')) 21 'in the results files (can be used for revision '
20 parser.epilog = ('If --unexpected-failures is passed, a list of the tests ' 22 'numbers, etc.)'))
21 'that failed (one per line) will be written to the file. ' 23 parser.add_argument('--write-full-results-to', metavar='FILENAME',
22 'If no tests failed, the file will be truncated (empty). ' 24 action='store',
23 'If the test run did not completely properly, or something ' 25 help='path to write the list of full results to.')
24 'else weird happened, any existing file will be left ' 26 parser.add_argument('tests', nargs='*')
25 'unmodified. ' 27
26 'If --unexpected-failures is *not* passed, any existing ' 28 args = parser.parse_args()
27 'file will be ignored and left unmodified.') 29
28 options, args = parser.parse_args() 30 bad_metadata = False
31 for val in args.metadata:
32 if '=' not in val:
33 print >> sys.stderr, ('Error: malformed metadata "%s"' % val)
Paweł Hajdan Jr. 2014/06/25 01:01:03 Why not parser.error(...)?
Dirk Pranke 2014/06/25 01:05:11 parser.error() calls sys.exit() directly, which ma
34 bad_metadata = True
35 if bad_metadata:
36 print >> sys.stderr
37 parser.print_help()
38 return 2
29 39
30 chromium_src_dir = os.path.join(os.path.dirname(__file__), 40 chromium_src_dir = os.path.join(os.path.dirname(__file__),
31 os.pardir, 41 os.pardir,
32 os.pardir) 42 os.pardir)
33 43
34 loader = unittest.loader.TestLoader() 44 loader = unittest.loader.TestLoader()
35 print "Running Python unit tests under mojo/public/tools/bindings/pylib ..." 45 print "Running Python unit tests under mojo/public/tools/bindings/pylib ..."
36 46
37 pylib_dir = os.path.join(chromium_src_dir, 'mojo', 'public', 47 pylib_dir = os.path.join(chromium_src_dir, 'mojo', 'public',
38 'tools', 'bindings', 'pylib') 48 'tools', 'bindings', 'pylib')
39 if args: 49 if args.tests:
40 if not pylib_dir in sys.path: 50 if pylib_dir not in sys.path:
41 sys.path.append(pylib_dir) 51 sys.path.append(pylib_dir)
42 suite = unittest.TestSuite() 52 suite = unittest.TestSuite()
43 for test_name in args: 53 for test_name in args:
44 suite.addTests(loader.loadTestsFromName(test_name)) 54 suite.addTests(loader.loadTestsFromName(test_name))
45 else: 55 else:
46 suite = loader.discover(pylib_dir, pattern='*_unittest.py') 56 suite = loader.discover(pylib_dir, pattern='*_unittest.py')
47 57
48 runner = unittest.runner.TextTestRunner(verbosity=(options.verbose + 1)) 58 runner = unittest.runner.TextTestRunner(verbosity=(args.verbose + 1))
49 result = runner.run(suite) 59 result = runner.run(suite)
50 60
51 if options.unexpected_failures: 61 full_results = _FullResults(suite, result, args.metadata)
52 WriteUnexpectedFailures(result, options.unexpected_failures) 62 if args.write_full_results_to:
63 with open(args.write_full_results_to, 'w') as fp:
64 json.dump(full_results, fp, indent=2)
65 fp.write("\n")
Paweł Hajdan Jr. 2014/06/25 01:01:03 Is this needed?
Dirk Pranke 2014/06/25 01:05:11 No, but it makes the file slightly easier to read
53 66
54 return 0 if result.wasSuccessful() else 1 67 return 0 if result.wasSuccessful() else 1
55 68
56 69
57 def WriteUnexpectedFailures(result, path): 70 TEST_SEPARATOR = '.'
58 71
72
73 def _FullResults(suite, result, metadata):
74 "Convert the unittest results to the Chromium JSON test result format.
viettrungluu 2014/06/25 01:08:23 " -> """?
Dirk Pranke 2014/06/25 18:43:45 Whoops. Done.
75
76 This matches run-webkit-tests (the layout tests) and the flakiness dashboard.
77 """
78
79 full_results = {}
80 full_results['interrupted'] = False
81 full_results['path_delimiter'] = TEST_SEPARATOR
82 full_results['version'] = 3
83 full_results['seconds_since_epoch'] = time.time()
84 for md in metadata:
85 key, val = md.split('=', 1)
86 full_results[key] = val
87
88 all_test_names = _AllTestNames(suite)
89 failed_test_names = _FailedTestNames(result)
90
91 full_results['num_failures_by_type'] = {
92 'Failure': len(failed_test_names),
viettrungluu 2014/06/25 01:08:23 nit: indentation
Dirk Pranke 2014/06/25 18:43:45 python style uses 4-space hanging indents as far a
93 'Pass': len(all_test_names) - len(failed_test_names),
94 }
95
96 full_results['tests'] = {}
97
98 for test_name in all_test_names:
99 value = {
100 'expected': 'PASS',
viettrungluu 2014/06/25 01:08:23 "
101 'actual': 'FAIL' if (test_name in failed_test_names) else 'FAIL',
102 }
103 _AddPathToTrie(full_results['tests'], test_name, value)
104
105 return full_results
106
107
108 def _AllTestNames(suite):
109 test_names = []
110 # _tests is protected pylint: disable=W0212
111 for test in suite._tests:
112 if isinstance(test, unittest.suite.TestSuite):
113 test_names.extend(_AllTestNames(test))
114 else:
115 test_names.append(_UnitTestName(test))
116 return test_names
117
118
119 def _FailedTestNames(result):
120 failed_test_names = set()
121 for (test, _) in result.failures + result.errors:
122 failed_test_names.add(_UnitTestName(test))
123 return failed_test_names
124
125
126 def _AddPathToTrie(trie, path, value):
127 if TEST_SEPARATOR not in path:
128 trie[path] = value
129 return
130 directory, rest = path.split(TEST_SEPARATOR, 1)
131 if directory not in trie:
132 trie[directory] = {}
133 _AddPathToTrie(trie[directory], rest, value)
134
135
136 _UNITTEST_NAME_REGEX = re.compile("(\w+) \(([\w.]+)\)")
137
138
139 def _UnitTestName(test):
59 # This regex and UnitTestName() extracts the test_name in a way 140 # This regex and UnitTestName() extracts the test_name in a way
60 # that can be handed back to the loader successfully. 141 # that can be handed back to the loader successfully.
61 142 m = _UNITTEST_NAME_REGEX.match(str(test))
62 test_description = re.compile("(\w+) \(([\w.]+)\)") 143 assert m, "could not find test name from test description %s" % str(test)
63 144 return "%s.%s" % (m.group(2), m.group(1))
64 def UnitTestName(test):
65 m = test_description.match(str(test))
66 return "%s.%s" % (m.group(2), m.group(1))
67
68 with open(path, 'w') as fp:
69 for (test, _) in result.failures + result.errors:
70 fp.write(UnitTestName(test) + '\n')
71 145
72 146
73 if __name__ == '__main__': 147 if __name__ == '__main__':
74 sys.exit(main()) 148 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698