#!/usr/bin/env python # vim: fileencoding=utf-8 #------------------------------------------------------------------------- # CxxTest: A lightweight C++ unit testing library. # Copyright (c) 2008 Sandia Corporation. # This software is distributed under the LGPL License v3 # For more information, see the COPYING file in the top CxxTest directory. # Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, # the U.S. Government retains certain rights in this software. #------------------------------------------------------------------------- from __future__ import print_function import os, sys from os.path import isdir, isfile, islink, join from optparse import OptionParser from subprocess import check_call, CalledProcessError, PIPE options = None args = [] available_types = set(['scons']) tool_stdout = PIPE def main(): global options global args global tool_stdout """Parse the options and execute the program.""" usage = \ """Usage: %prog [options] [test1 [test2 [...]]] If you provide one or more tests, this will run the provided tests. Otherwise, it will look for tests in the current directory and run them all. """ # option parsing parser = OptionParser(usage) parser.set_defaults( action='run', verbose=True) parser.add_option("-c", "--clean", action='store_const', const='clean', dest='action', help="deletes any generated files in the tests") parser.add_option("--run", action='store_const', const='run', dest='action', help="sets up the environment, compiles and runs the tests") parser.add_option("-v", "--verbose", action='store_true', dest='verbose', help="spew out more details") parser.add_option("-q", "--quiet", action='store_false', dest='verbose', help="spew out only success/failure of tests") parser.add_option("--target-dir", dest='target_dir', action='store', default='./', help='target directory to look for tests in. default: %default') parser.add_option("--debug", dest='debug', action='store_true', default=False, help='turn on debug output.') (options, args) = parser.parse_args() if options.debug or options.verbose: tool_stdout = None # gather the tests tests = [] if len(args) == 0: tests = crawl_tests(options.target_dir) else: tests = args tests = purge_tests(tests) # run the tests if options.action == 'run': for t in tests: run_test(t) elif options.action == 'clean': for t in tests: clean_test(t) def crawl_tests(target): """Gather the directories in the test directory.""" files = os.listdir(target) return [f for f in files if isdir(f) and f[0] != '.'] def purge_tests(dirs): """Look at the test candidates and purge those that aren't from the list""" tests = [] for t in dirs: if isfile(join(t, 'TestDef.py')): tests.append(t) else: warn("{0} is not a test (missing TestDef.py file).".format(t)) return tests def warn(msg): """A general warning function.""" if options.verbose: print('[Warn]: ' + msg, file=sys.stderr) def notice(msg): """A general print function.""" if options.verbose: print(msg) def debug(msg): """A debugging function""" if options.debug: print(msg) def run_test(t): """Runs the test in directory t.""" opts = read_opts(t) notice("-----------------------------------------------------") notice("running test '{0}':\n".format(t)) readme = join(t, 'README') if isfile(readme): notice(open(readme).read()) notice("") if opts['type'] not in available_types: warn('{0} is not a recognised test type in {1}'.format(opts['type'], t)) return if not opts['expect_success']: warn("tests that fail intentionally are not yet supported.") return # set up the environment setup_env(t, opts) # run the test try: if opts['type'] == 'scons': run_scons(t, opts) except RuntimeError as e: print("Test {0} failed.".format(t)) return if not options.verbose: print('.', end='') sys.stdout.flush() else: print("test '{0}' successful.".format(t)) def read_opts(t): """Read the test options and return them.""" opts = { 'expect_success' : True, 'type' : 'scons', 'links' : {} } f = open(join(t, "TestDef.py")) exec(f.read(), opts) return opts def setup_env(t, opts): """Set up the environment for the test.""" # symlinks links = opts['links'] for link in links: frm = links[link] to = join(t, link) debug("Symlinking {0} to {1}".format(frm, to)) if islink(to): os.unlink(to) os.symlink(frm, to) def teardown_env(t, opts): """Remove all files generated for the test.""" links = opts['links'] for link in links: to = join(t, link) debug('removing link {0}'.format(to)) os.unlink(to) def clean_test(t): """Remove all generated files.""" opts = read_opts(t) notice("cleaning test {0}".format(t)) if opts['type'] == 'scons': setup_env(t, opts) # scons needs the environment links to work clean_scons(t, opts) teardown_env(t, opts) def clean_scons(t, opts): """Make scons clean after itself.""" cwd = os.getcwd() os.chdir(t) try: check_call(['scons', '--clean'], stdout=tool_stdout, stderr=None) except CalledProcessError as e: warn("SCons failed with error {0}".format(e.returncode)) os.chdir(cwd) sconsign = join(t, '.sconsign.dblite') if isfile(sconsign): os.unlink(sconsign) def run_scons(t, opts): """Run scons test.""" cwd = os.getcwd() os.chdir(t) try: check_call(['scons', '--clean'], stdout=tool_stdout) check_call(['scons', '.'], stdout=tool_stdout) check_call(['scons', 'check'], stdout=tool_stdout) except CalledProcessError as e: os.chdir(cwd) # clean up raise e os.chdir(cwd) if __name__ == "__main__": main() if not options.verbose: print() # quiet doesn't output newlines.