Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 """A "smart" test runner for gtest unit tests (that caches successes).""" | 6 """A "smart" test runner for gtest unit tests (that caches successes).""" |
| 7 | 7 |
| 8 import argparse | |
| 8 import logging | 9 import logging |
| 9 import os | 10 import os |
| 10 import platform | 11 import platform |
| 11 import subprocess | 12 import subprocess |
| 12 import sys | 13 import sys |
| 13 | 14 |
| 14 _logging = logging.getLogger() | 15 _logging = logging.getLogger() |
| 15 | 16 |
| 17 from mopy.paths import Paths | |
| 16 from mopy.transitive_hash import transitive_hash | 18 from mopy.transitive_hash import transitive_hash |
| 17 | 19 |
| 18 def main(argv): | 20 paths = Paths() |
| 21 | |
| 22 def main(): | |
| 19 logging.basicConfig() | 23 logging.basicConfig() |
| 20 # Uncomment to debug: | 24 # Uncomment to debug: |
| 21 # _logging.setLevel(logging.DEBUG) | 25 # _logging.setLevel(logging.DEBUG) |
| 22 | 26 |
| 23 if len(argv) < 3 or len(argv) > 4: | 27 parser = argparse.ArgumentParser( |
| 24 print "Usage: %s gtest_list_file root_dir [successes_cache_file]" % \ | 28 description="A 'smart' test runner for gtest unit tests (that caches " |
| 25 os.path.basename(argv[0]) | 29 "successes).") |
| 26 return 0 if len(argv) < 2 else 1 | |
| 27 | 30 |
| 28 _logging.debug("Test list file: %s", argv[1]) | 31 debug_group = parser.add_mutually_exclusive_group() |
| 29 with open(argv[1], 'rb') as f: | 32 debug_group.add_argument('--debug', help='Debug build (default)', |
|
viettrungluu
2014/11/14 16:36:32
By design, this should not know about debug/releas
| |
| 33 default=True, action='store_true') | |
| 34 debug_group.add_argument('--release', help='Release build', default=False, | |
| 35 dest='debug', action='store_false') | |
| 36 | |
| 37 os_group = parser.add_mutually_exclusive_group() | |
| 38 os_group.add_argument("--android", help="Run tests for android", | |
|
viettrungluu
2014/11/14 16:36:32
It probably shouldn't have knowledge of android ei
qsr
2014/11/14 17:02:33
Hum. Running unit test on android is not the same
| |
| 39 action='store_true') | |
| 40 | |
| 41 parser.add_argument("gtest_list_file", | |
| 42 help="The file containing the tests to run.") | |
| 43 parser.add_argument("root_dir", help="The build directory.") | |
| 44 parser.add_argument("successes_cache_filename", | |
| 45 help="The file caching test results.", default=None, | |
| 46 nargs='?') | |
| 47 args = parser.parse_args() | |
| 48 | |
| 49 _logging.debug("Test list file: %s", args.gtest_list_file) | |
| 50 with open(args.gtest_list_file, 'rb') as f: | |
| 30 gtest_list = [y for y in [x.strip() for x in f.readlines()] \ | 51 gtest_list = [y for y in [x.strip() for x in f.readlines()] \ |
| 31 if y and y[0] != '#'] | 52 if y and y[0] != '#'] |
| 32 _logging.debug("Test list: %s" % gtest_list) | 53 _logging.debug("Test list: %s" % gtest_list) |
| 33 | 54 |
| 34 print "Running tests in directory: %s" % argv[2] | 55 print "Running tests in directory: %s" % args.root_dir |
| 35 os.chdir(argv[2]) | 56 os.chdir(args.root_dir) |
| 36 | 57 |
| 37 if len(argv) == 4 and argv[3]: | 58 if args.successes_cache_filename: |
| 38 successes_cache_filename = argv[3] | 59 print "Successes cache file: %s" % args.successes_cache_filename |
| 39 print "Successes cache file: %s" % successes_cache_filename | |
| 40 else: | 60 else: |
| 41 successes_cache_filename = None | |
| 42 print "No successes cache file (will run all tests unconditionally)" | 61 print "No successes cache file (will run all tests unconditionally)" |
| 43 | 62 |
| 44 if successes_cache_filename: | 63 if args.successes_cache_filename: |
| 45 # This file simply contains a list of transitive hashes of tests that | 64 # This file simply contains a list of transitive hashes of tests that |
| 46 # succeeded. | 65 # succeeded. |
| 47 try: | 66 try: |
| 48 _logging.debug("Trying to read successes cache file: %s", | 67 _logging.debug("Trying to read successes cache file: %s", |
| 49 successes_cache_filename) | 68 args.successes_cache_filename) |
| 50 with open(argv[3], 'rb') as f: | 69 with open(args.successes_cache_filename, 'rb') as f: |
| 51 successes = set([x.strip() for x in f.readlines()]) | 70 successes = set([x.strip() for x in f.readlines()]) |
| 52 _logging.debug("Successes: %s", successes) | 71 _logging.debug("Successes: %s", successes) |
| 53 except IOError: | 72 except IOError: |
| 54 # Just assume that it didn't exist, or whatever. | 73 # Just assume that it didn't exist, or whatever. |
| 55 print "Failed to read successes cache file %s (will create)" % argv[3] | 74 print ("Failed to read successes cache file %s (will create)" % |
| 75 args.successes_cache_filename) | |
| 56 successes = set() | 76 successes = set() |
| 57 | 77 |
| 58 # Run gtests with color if we're on a TTY (and we're not being told explicitly | 78 # Run gtests with color if we're on a TTY (and we're not being told explicitly |
| 59 # what to do). | 79 # what to do). |
| 60 if sys.stdout.isatty() and 'GTEST_COLOR' not in os.environ: | 80 if sys.stdout.isatty() and 'GTEST_COLOR' not in os.environ: |
| 61 _logging.debug("Setting GTEST_COLOR=yes") | 81 _logging.debug("Setting GTEST_COLOR=yes") |
| 62 os.environ['GTEST_COLOR'] = 'yes' | 82 os.environ['GTEST_COLOR'] = 'yes' |
| 63 | 83 |
| 64 # TODO(vtl): We may not close this file on failure. | 84 # TODO(vtl): We may not close this file on failure. |
| 65 successes_cache_file = open(successes_cache_filename, 'ab') \ | 85 successes_cache_file = open(args.successes_cache_filename, 'ab') \ |
| 66 if successes_cache_filename else None | 86 if args.successes_cache_filename else None |
| 67 for gtest in gtest_list: | 87 for gtest in gtest_list: |
| 68 if gtest[0] == '*': | 88 if gtest[0] == '*': |
| 69 gtest = gtest[1:] | 89 gtest = gtest[1:] |
| 70 _logging.debug("%s is marked as non-cacheable" % gtest) | 90 _logging.debug("%s is marked as non-cacheable" % gtest) |
| 71 cacheable = False | 91 cacheable = False |
| 72 else: | 92 else: |
| 73 cacheable = True | 93 cacheable = True |
| 74 | 94 |
| 95 gtest_file = gtest | |
| 75 if platform.system() == 'Windows': | 96 if platform.system() == 'Windows': |
| 76 gtest += ".exe" | 97 gtest_file += ".exe" |
| 98 if args.android: | |
| 99 gtest_file = gtest + "_apk/" + gtest + "-debug.apk" | |
| 77 | 100 |
| 78 if successes_cache_file and cacheable: | 101 if successes_cache_file and cacheable: |
| 79 _logging.debug("Getting transitive hash for %s ... " % gtest) | 102 _logging.debug("Getting transitive hash for %s ... " % gtest) |
| 80 try: | 103 try: |
| 81 gtest_hash = transitive_hash(gtest) | 104 gtest_hash = transitive_hash(gtest_file, not args.android) |
| 82 except subprocess.CalledProcessError: | 105 except subprocess.CalledProcessError: |
| 83 print "Failed to get transitive hash for %s" % gtest | 106 print "Failed to get transitive hash for %s" % gtest |
| 84 return 1 | 107 return 1 |
| 85 _logging.debug(" Transitive hash: %s" % gtest_hash) | 108 _logging.debug(" Transitive hash: %s" % gtest_hash) |
| 86 | 109 |
| 87 if gtest_hash in successes: | 110 if gtest_hash in successes: |
| 88 print "Skipping %s (previously succeeded)" % gtest | 111 print "Skipping %s (previously succeeded)" % gtest |
| 89 continue | 112 continue |
| 90 | 113 |
| 91 print "Running %s...." % gtest, | 114 print "Running %s...." % gtest, |
| 92 sys.stdout.flush() | 115 sys.stdout.flush() |
| 93 try: | 116 try: |
| 94 subprocess.check_output(["./" + gtest], stderr=subprocess.STDOUT) | 117 if args.android: |
| 118 command = [ "python" ] | |
| 119 command.append(os.path.join(paths.src_root, | |
| 120 "build", "android", "test_runner.py")) | |
| 121 command.append("gtest") | |
| 122 command.append("--output-directory") | |
| 123 command.append(args.root_dir) | |
| 124 command.append("-s") | |
| 125 command.append(gtest) | |
| 126 command.append('--debug' if args.debug else '--release') | |
| 127 else: | |
| 128 command = [ "./" + gtest ] | |
| 129 subprocess.check_output(command, stderr=subprocess.STDOUT) | |
| 95 print "Succeeded" | 130 print "Succeeded" |
| 96 # Record success. | 131 # Record success. |
| 97 if successes_cache_filename and cacheable: | 132 if args.successes_cache_filename and cacheable: |
| 98 successes.add(gtest_hash) | 133 successes.add(gtest_hash) |
| 99 successes_cache_file.write(gtest_hash + '\n') | 134 successes_cache_file.write(gtest_hash + '\n') |
| 100 successes_cache_file.flush() | 135 successes_cache_file.flush() |
| 101 except subprocess.CalledProcessError as e: | 136 except subprocess.CalledProcessError as e: |
| 102 print "Failed with exit code %d and output:" % e.returncode | 137 print "Failed with exit code %d and output:" % e.returncode |
| 103 print 72 * '-' | 138 print 72 * '-' |
| 104 print e.output | 139 print e.output |
| 105 print 72 * '-' | 140 print 72 * '-' |
| 106 return 1 | 141 return 1 |
| 107 except OSError as e: | 142 except OSError as e: |
| 108 print " Failed to start test" | 143 print " Failed to start test" |
| 109 return 1 | 144 return 1 |
| 110 print "All tests succeeded" | 145 print "All tests succeeded" |
| 111 if successes_cache_file: | 146 if successes_cache_file: |
| 112 successes_cache_file.close() | 147 successes_cache_file.close() |
| 113 | 148 |
| 114 return 0 | 149 return 0 |
| 115 | 150 |
| 116 if __name__ == '__main__': | 151 if __name__ == '__main__': |
| 117 sys.exit(main(sys.argv)) | 152 sys.exit(main()) |
| OLD | NEW |