1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
13 class MojoPythonTestRunner(object):
14 """Helper class to run python tests on the bots."""
16 def __init__(self
, test_dir
):
17 self
._test
_dir
= test_dir
20 parser
= argparse
.ArgumentParser()
21 parser
.usage
= 'run_mojo_python_tests.py [options] [tests...]'
22 parser
.add_argument('-v', '--verbose', action
='count', default
=0)
23 parser
.add_argument('--metadata', action
='append', default
=[],
24 help=('optional key=value metadata that will be stored '
25 'in the results files (can be used for revision '
27 parser
.add_argument('--write-full-results-to', metavar
='FILENAME',
29 help='path to write the list of full results to.')
30 parser
.add_argument('tests', nargs
='*')
32 self
.add_custom_commandline_options(parser
)
33 args
= parser
.parse_args()
34 self
.apply_customization(args
)
37 for val
in args
.metadata
:
39 print >> sys
.stderr
, ('Error: malformed metadata "%s"' % val
)
46 chromium_src_dir
= os
.path
.join(os
.path
.dirname(__file__
),
51 loader
= unittest
.loader
.TestLoader()
52 print "Running Python unit tests under %s..." % self
._test
_dir
54 pylib_dir
= os
.path
.join(chromium_src_dir
, self
._test
_dir
)
56 if pylib_dir
not in sys
.path
:
57 sys
.path
.append(pylib_dir
)
58 suite
= unittest
.TestSuite()
59 for test_name
in args
.tests
:
60 suite
.addTests(loader
.loadTestsFromName(test_name
))
62 suite
= loader
.discover(pylib_dir
, pattern
='*_unittest.py')
64 runner
= unittest
.runner
.TextTestRunner(verbosity
=(args
.verbose
+ 1))
65 result
= runner
.run(suite
)
67 full_results
= _FullResults(suite
, result
, args
.metadata
)
68 if args
.write_full_results_to
:
69 with
open(args
.write_full_results_to
, 'w') as fp
:
70 json
.dump(full_results
, fp
, indent
=2)
73 return 0 if result
.wasSuccessful() else 1
75 def add_custom_commandline_options(self
, parser
):
76 """Allow to add custom option to the runner script."""
79 def apply_customization(self
, args
):
80 """Allow to apply any customization to the runner."""
87 def _FullResults(suite
, result
, metadata
):
88 """Convert the unittest results to the Chromium JSON test result format.
90 This matches run-webkit-tests (the layout tests) and the flakiness dashboard.
94 full_results
['interrupted'] = False
95 full_results
['path_delimiter'] = TEST_SEPARATOR
96 full_results
['version'] = 3
97 full_results
['seconds_since_epoch'] = time
.time()
99 key
, val
= md
.split('=', 1)
100 full_results
[key
] = val
102 all_test_names
= _AllTestNames(suite
)
103 failed_test_names
= _FailedTestNames(result
)
105 full_results
['num_failures_by_type'] = {
106 'FAIL': len(failed_test_names
),
107 'PASS': len(all_test_names
) - len(failed_test_names
),
110 full_results
['tests'] = {}
112 for test_name
in all_test_names
:
114 value
['expected'] = 'PASS'
115 if test_name
in failed_test_names
:
116 value
['actual'] = 'FAIL'
117 value
['is_unexpected'] = True
119 value
['actual'] = 'PASS'
120 _AddPathToTrie(full_results
['tests'], test_name
, value
)
125 def _AllTestNames(suite
):
127 # _tests is protected pylint: disable=W0212
128 for test
in suite
._tests
:
129 if isinstance(test
, unittest
.suite
.TestSuite
):
130 test_names
.extend(_AllTestNames(test
))
132 test_names
.append(test
.id())
136 def _FailedTestNames(result
):
137 return set(test
.id() for test
, _
in result
.failures
+ result
.errors
)
140 def _AddPathToTrie(trie
, path
, value
):
141 if TEST_SEPARATOR
not in path
:
144 directory
, rest
= path
.split(TEST_SEPARATOR
, 1)
145 if directory
not in trie
:
147 _AddPathToTrie(trie
[directory
], rest
, value
)