• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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