普通文本  |  117行  |  3.64 KB

#!/usr/bin/env python
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""A "smart" test runner for gtest unit tests (that caches successes)."""

import logging
import os
import subprocess
import sys

_logging = logging.getLogger()

_script_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.join(_script_dir, "pylib"))

from transitive_hash import transitive_hash

def main(argv):
  logging.basicConfig()
  # Uncomment to debug:
  # _logging.setLevel(logging.DEBUG)

  if len(argv) < 3 or len(argv) > 4:
    print "Usage: %s gtest_list_file root_dir [successes_cache_file]" % \
        os.path.basename(argv[0])
    return 0 if len(argv) < 2 else 1

  _logging.debug("Test list file: %s", argv[1])
  with open(argv[1], 'rb') as f:
    gtest_list = [y for y in [x.strip() for x in f.readlines()] \
                      if y and y[0] != '#']
  _logging.debug("Test list: %s" % gtest_list)

  print "Running tests in directory: %s" % argv[2]
  os.chdir(argv[2])

  if len(argv) == 4 and argv[3]:
    successes_cache_filename = argv[3]
    print "Successes cache file: %s" % successes_cache_filename
  else:
    successes_cache_filename = None
    print "No successes cache file (will run all tests unconditionally)"

  if successes_cache_filename:
    # This file simply contains a list of transitive hashes of tests that
    # succeeded.
    try:
      _logging.debug("Trying to read successes cache file: %s",
                     successes_cache_filename)
      with open(argv[3], 'rb') as f:
        successes = set([x.strip() for x in f.readlines()])
      _logging.debug("Successes: %s", successes)
    except:
      # Just assume that it didn't exist, or whatever.
      print "Failed to read successes cache file %s (will create)" % argv[3]
      successes = set()

  # Run gtests with color if we're on a TTY (and we're not being told explicitly
  # what to do).
  if sys.stdout.isatty() and 'GTEST_COLOR' not in os.environ:
    _logging.debug("Setting GTEST_COLOR=yes")
    os.environ['GTEST_COLOR'] = 'yes'

  # TODO(vtl): We may not close this file on failure.
  successes_cache_file = open(successes_cache_filename, 'ab') \
      if successes_cache_filename else None
  for gtest in gtest_list:
    if gtest[0] == '*':
      gtest = gtest[1:]
      _logging.debug("%s is marked as non-cacheable" % gtest)
      cacheable = False
    else:
      cacheable = True

    if successes_cache_file and cacheable:
      _logging.debug("Getting transitive hash for %s ... " % gtest)
      try:
        gtest_hash = transitive_hash(gtest)
      except:
        print "Failed to get transitive hash for %s" % gtest
        return 1
      _logging.debug("  Transitive hash: %s" % gtest_hash)

      if gtest_hash in successes:
        print "Skipping %s (previously succeeded)" % gtest
        continue

    print "Running %s...." % gtest,
    sys.stdout.flush()
    try:
      subprocess.check_output(["./" + gtest], stderr=subprocess.STDOUT)
      print "Succeeded"
      # Record success.
      if successes_cache_filename and cacheable:
        successes.add(gtest_hash)
        successes_cache_file.write(gtest_hash + '\n')
        successes_cache_file.flush()
    except subprocess.CalledProcessError as e:
      print "Failed with exit code %d and output:" % e.returncode
      print 72 * '-'
      print e.output
      print 72 * '-'
      return 1
    except OSError as e:
      print "  Failed to start test"
      return 1
  print "All tests succeeded"
  if successes_cache_file:
    successes_cache_file.close()

  return 0

if __name__ == '__main__':
  sys.exit(main(sys.argv))