Home | History | Annotate | Download | only in tools
      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 
      8 import logging
      9 import os
     10 import subprocess
     11 import sys
     12 
     13 _logging = logging.getLogger()
     14 
     15 _script_dir = os.path.dirname(os.path.abspath(__file__))
     16 sys.path.insert(0, os.path.join(_script_dir, "pylib"))
     17 
     18 from transitive_hash import transitive_hash
     19 
     20 def 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 
    115 if __name__ == '__main__':
    116   sys.exit(main(sys.argv))
    117