1#!/usr/bin/env python 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 4# found in the LICENSE file. 5 6"""A "smart" test runner for gtest unit tests (that caches successes).""" 7 8import logging 9import os 10import subprocess 11import sys 12 13_logging = logging.getLogger() 14 15_script_dir = os.path.dirname(os.path.abspath(__file__)) 16sys.path.insert(0, os.path.join(_script_dir, "pylib")) 17 18from transitive_hash import transitive_hash 19 20def main(argv): 21 logging.basicConfig() 22 # Uncomment to debug: 23 # _logging.setLevel(logging.DEBUG) 24 25 if len(argv) < 3 or len(argv) > 4: 26 print "Usage: %s gtest_list_file root_dir [successes_cache_file]" % \ 27 os.path.basename(argv[0]) 28 return 0 if len(argv) < 2 else 1 29 30 _logging.debug("Test list file: %s", argv[1]) 31 with open(argv[1], 'rb') as f: 32 gtest_list = [y for y in [x.strip() for x in f.readlines()] \ 33 if y and y[0] != '#'] 34 _logging.debug("Test list: %s" % gtest_list) 35 36 print "Running tests in directory: %s" % argv[2] 37 os.chdir(argv[2]) 38 39 if len(argv) == 4 and argv[3]: 40 successes_cache_filename = argv[3] 41 print "Successes cache file: %s" % successes_cache_filename 42 else: 43 successes_cache_filename = None 44 print "No successes cache file (will run all tests unconditionally)" 45 46 if successes_cache_filename: 47 # This file simply contains a list of transitive hashes of tests that 48 # succeeded. 49 try: 50 _logging.debug("Trying to read successes cache file: %s", 51 successes_cache_filename) 52 with open(argv[3], 'rb') as f: 53 successes = set([x.strip() for x in f.readlines()]) 54 _logging.debug("Successes: %s", successes) 55 except: 56 # Just assume that it didn't exist, or whatever. 57 print "Failed to read successes cache file %s (will create)" % argv[3] 58 successes = set() 59 60 # Run gtests with color if we're on a TTY (and we're not being told explicitly 61 # what to do). 62 if sys.stdout.isatty() and 'GTEST_COLOR' not in os.environ: 63 _logging.debug("Setting GTEST_COLOR=yes") 64 os.environ['GTEST_COLOR'] = 'yes' 65 66 # TODO(vtl): We may not close this file on failure. 67 successes_cache_file = open(successes_cache_filename, 'ab') \ 68 if successes_cache_filename else None 69 for gtest in gtest_list: 70 if gtest[0] == '*': 71 gtest = gtest[1:] 72 _logging.debug("%s is marked as non-cacheable" % gtest) 73 cacheable = False 74 else: 75 cacheable = True 76 77 if successes_cache_file and cacheable: 78 _logging.debug("Getting transitive hash for %s ... " % gtest) 79 try: 80 gtest_hash = transitive_hash(gtest) 81 except: 82 print "Failed to get transitive hash for %s" % gtest 83 return 1 84 _logging.debug(" Transitive hash: %s" % gtest_hash) 85 86 if gtest_hash in successes: 87 print "Skipping %s (previously succeeded)" % gtest 88 continue 89 90 print "Running %s...." % gtest, 91 sys.stdout.flush() 92 try: 93 subprocess.check_output(["./" + gtest], stderr=subprocess.STDOUT) 94 print "Succeeded" 95 # Record success. 96 if successes_cache_filename and cacheable: 97 successes.add(gtest_hash) 98 successes_cache_file.write(gtest_hash + '\n') 99 successes_cache_file.flush() 100 except subprocess.CalledProcessError as e: 101 print "Failed with exit code %d and output:" % e.returncode 102 print 72 * '-' 103 print e.output 104 print 72 * '-' 105 return 1 106 except OSError as e: 107 print " Failed to start test" 108 return 1 109 print "All tests succeeded" 110 if successes_cache_file: 111 successes_cache_file.close() 112 113 return 0 114 115if __name__ == '__main__': 116 sys.exit(main(sys.argv)) 117