aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorzhanyong.wan <zhanyong.wan@861a406c-534a-0410-8894-cb66d6ee9925>2009-06-09 05:47:03 +0000
committerzhanyong.wan <zhanyong.wan@861a406c-534a-0410-8894-cb66d6ee9925>2009-06-09 05:47:03 +0000
commit819501581ccab949e7058ece111120e866197540 (patch)
tree42fb087d7db22fc981b2daf122bbc01d82f057c2
parentfd36c200f446aaada1a1f0b026f2d742d4b2fa5a (diff)
downloadgoogletest-819501581ccab949e7058ece111120e866197540.tar.gz
googletest-819501581ccab949e7058ece111120e866197540.tar.bz2
googletest-819501581ccab949e7058ece111120e866197540.zip
Adds run_tests.py for running the tests (by Vlad Losev).
-rwxr-xr-xrun_tests.py433
-rwxr-xr-xtest/run_tests_test.py527
2 files changed, 960 insertions, 0 deletions
diff --git a/run_tests.py b/run_tests.py
new file mode 100755
index 00000000..ab58a825
--- /dev/null
+++ b/run_tests.py
@@ -0,0 +1,433 @@
+#!/usr/bin/env python
+#
+# Copyright 2008, Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Runs specified tests for Google Test.
+
+SYNOPSIS
+ run_tests.py [OPTION]... [BUILD_DIR]... [TEST]...
+
+DESCRIPTION
+ Runs the specified tests (either binary or Python), and prints a
+ summary of the results. BUILD_DIRS will be used to search for the
+ binaries. If no TESTs are specified, all binary tests found in
+ BUILD_DIRs and all Python tests found in the directory test/ (in the
+ gtest root) are run.
+
+ TEST is a name of either a binary or a Python test. A binary test is
+ an executable file named *_test or *_unittest (with the .exe
+ extension on Windows) A Python test is a script named *_test.py or
+ *_unittest.py.
+
+OPTIONS
+ -c CONFIGURATIONS
+ Specify build directories via build configurations.
+ CONFIGURATIONS is either a comma-separated list of build
+ configurations or 'all'. Each configuration is equivalent to
+ adding 'scons/build/<configuration>/scons' to BUILD_DIRs.
+ Specifying -c=all is equivalent to providing all directories
+ listed in KNOWN BUILD DIRECTORIES section below.
+
+ -a
+ Equivalent to -c=all
+
+ -b
+ Equivalent to -c=all with the exception that the script will not
+ fail if some of the KNOWN BUILD DIRECTORIES do not exists; the
+ script will simply not run the tests there. 'b' stands for
+ 'built directories'.
+
+RETURN VALUE
+ Returns 0 if all tests are successful; otherwise returns 1.
+
+EXAMPLES
+ run_tests.py
+ Runs all tests for the default build configuration.
+
+ run_tests.py -a
+ Runs all tests with binaries in KNOWN BUILD DIRECTORIES.
+
+ run_tests.py -b
+ Runs all tests in KNOWN BUILD DIRECTORIES that have been
+ built.
+
+ run_tests.py foo/
+ Runs all tests in the foo/ directory and all Python tests in
+ the directory test. The Python tests are instructed to look
+ for binaries in foo/.
+
+ run_tests.py bar_test.exe test/baz_test.exe foo/ bar/
+ Runs foo/bar_test.exe, bar/bar_test.exe, foo/baz_test.exe, and
+ bar/baz_test.exe.
+
+ run_tests.py foo bar test/foo_test.py
+ Runs test/foo_test.py twice instructing it to look for its
+ test binaries in the directories foo and bar,
+ correspondingly.
+
+KNOWN BUILD DIRECTORIES
+ run_tests.py knows about directories where the SCons build script
+ deposits its products. These are the directories where run_tests.py
+ will be looking for its binaries. Currently, gtest's SConstruct file
+ defines them as follows (the default build directory is the first one
+ listed in each group):
+ On Windows:
+ <gtest root>/scons/build/win-dbg/scons/
+ <gtest root>/scons/build/win-opt/scons/
+ <gtest root>/scons/build/win-dbg8/scons/
+ <gtest root>/scons/build/win-opt8/scons/
+ On Mac:
+ <gtest root>/scons/build/mac-dbg/scons/
+ <gtest root>/scons/build/mac-opt/scons/
+ On other platforms:
+ <gtest root>/scons/build/dbg/scons/
+ <gtest root>/scons/build/opt/scons/
+
+AUTHOR
+ Written by Zhanyong Wan (wan@google.com)
+ and Vlad Losev(vladl@google.com).
+
+REQUIREMENTS
+ This script requires Python 2.3 or higher.
+"""
+
+import optparse
+import os
+import re
+import sets
+import sys
+
+try:
+ # subrocess module is a preferable way to invoke subprocesses but it may
+ # not be available on MacOS X 10.4.
+ import subprocess
+except ImportError:
+ subprocess = None
+
+IS_WINDOWS = os.name == 'nt'
+IS_MAC = os.name == 'posix' and os.uname()[0] == 'Darwin'
+
+# Definition of CONFIGS must match that of the build directory names in the
+# SConstruct script. The first list item is the default build configuration.
+if IS_WINDOWS:
+ CONFIGS = ('win-dbg', 'win-dbg8', 'win-opt', 'win-opt8')
+elif IS_MAC:
+ CONFIGS = ('mac-dbg', 'mac-opt')
+else:
+ CONFIGS = ('dbg', 'opt')
+
+if IS_WINDOWS:
+ PYTHON_TEST_REGEX = re.compile(r'_(unit)?test\.py$', re.IGNORECASE)
+ BINARY_TEST_REGEX = re.compile(r'_(unit)?test(\.exe)?$', re.IGNORECASE)
+else:
+ PYTHON_TEST_REGEX = re.compile(r'_(unit)?test\.py$')
+ BINARY_TEST_REGEX = re.compile(r'_(unit)?test$')
+
+GTEST_BUILD_DIR = 'GTEST_BUILD_DIR'
+
+def ScriptDir():
+ """Returns the directory containing this script file."""
+
+ my_path = sys.argv[0]
+ my_dir = os.path.dirname(my_path)
+ if not my_dir or __name__ != '__main__':
+ my_dir = '.'
+ return my_dir
+
+
+MY_DIR = ScriptDir()
+
+
+def GetBuildDirForConfig(config):
+ """Returns the build directory for a given configuration."""
+
+ return 'scons/build/%s/scons' % config
+
+
+class TestRunner(object):
+ """Provides facilities for running Python and binary tests for Google Test."""
+
+ def __init__(self, injected_os=os, injected_subprocess=subprocess):
+ self.os = injected_os
+ self.subprocess = injected_subprocess
+
+ def Run(self, args):
+ """Runs the executable with given args (args[0] is the executable name).
+
+ Args:
+ args: Command line arguments for the process.
+
+ Returns:
+ Process's exit code if it exits normally, or -signal if the process is
+ killed by a signal.
+ """
+
+ if self.subprocess:
+ return self.subprocess.Popen(args).wait()
+ else:
+ return self.os.spawn(self.os.P_WAIT, args[0], args)
+
+ def RunBinaryTest(self, test):
+ """Runs the binary test script given its path relative to the gtest root.
+
+ Args:
+ test: Path to the test binary relative to the location of this script.
+
+ Returns:
+ Process's exit code if it exits normally, or -signal if the process is
+ killed by a signal.
+ """
+
+ return self.Run([self.os.path.abspath(self.os.path.join(MY_DIR, test))])
+
+ def RunPythonTest(self, test, build_dir):
+ """Runs the Python test script with the specified build directory.
+
+ Args:
+ test: Name of the test's Python script.
+ build_dir: Path to the directory where the test binary is to be found.
+
+ Returns:
+ Process's exit code if it exits normally, or -signal if the process is
+ killed by a signal.
+ """
+
+ old_build_dir = self.os.environ.get(GTEST_BUILD_DIR)
+
+ try:
+ self.os.environ[GTEST_BUILD_DIR] = build_dir
+
+ # If this script is run on a Windows machine that has no association
+ # between the .py extension and a python interpreter, simply passing
+ # the script name into subprocess.Popen/os.spawn will not work.
+ script = self.os.path.join(MY_DIR, test)
+ print 'Running %s . . .' % (script,)
+ return self.Run([sys.executable, script])
+
+ finally:
+ if old_build_dir is None:
+ del self.os.environ[GTEST_BUILD_DIR]
+ else:
+ self.os.environ[GTEST_BUILD_DIR] = old_build_dir
+
+ def FindFilesByRegex(self, directory, regex):
+ """Returns files in a directory whose names match a regular expression.
+
+ Args:
+ directory: Path to the directory to search for files.
+ regex: Regular expression to filter file names.
+
+ Returns:
+ The list of the paths to the files in the directory.
+ """
+
+ return [self.os.path.join(directory, file_name)
+ for file_name in self.os.listdir(directory)
+ if re.search(regex, file_name)]
+
+ # TODO(vladl@google.com): Implement parsing of scons/SConscript to run all
+ # tests defined there when no tests are specified.
+ # TODO(vladl@google.com): Update the docstring after the code is changed to
+ # try to test all builds defined in scons/SConscript.
+ def GetTestsToRun(self,
+ args,
+ named_configurations,
+ built_configurations,
+ available_configurations=CONFIGS):
+ """Determines what tests should be run.
+
+ Args:
+ args: The list of non-option arguments from the command line.
+ named_configurations: The list of configurations specified via -c or -a.
+ built_configurations: True if -b has been specified.
+ available_configurations: a list of configurations available on the
+ current platform, injectable for testing.
+
+ Returns:
+ A tuple with 2 elements: the list of Python tests to run and the list of
+ binary tests to run.
+ """
+
+ if named_configurations == 'all':
+ named_configurations = ','.join(available_configurations)
+
+ # A final list of build directories which will be searched for the test
+ # binaries. First, add directories specified directly on the command
+ # line.
+ build_dirs = [arg for arg in args if self.os.path.isdir(arg)]
+
+ # Adds build directories specified via their build configurations using
+ # the -c or -a options.
+ if named_configurations:
+ build_dirs += [GetBuildDirForConfig(config)
+ for config in named_configurations.split(',')]
+
+ # Adds KNOWN BUILD DIRECTORIES if -b is specified.
+ if built_configurations:
+ build_dirs += [GetBuildDirForConfig(config)
+ for config in available_configurations
+ if self.os.path.isdir(GetBuildDirForConfig(config))]
+
+ # If no directories were specified either via -a, -b, -c, or directly, use
+ # the default configuration.
+ elif not build_dirs:
+ build_dirs = [GetBuildDirForConfig(config)
+ for config in available_configurations[0:1]]
+
+ # Makes sure there are no duplications.
+ build_dirs = sets.Set(build_dirs)
+
+ errors_found = False
+ listed_python_tests = [] # All Python tests listed on the command line.
+ listed_binary_tests = [] # All binary tests listed on the command line.
+
+ # Sifts through non-directory arguments fishing for any Python or binary
+ # tests and detecting errors.
+ for argument in sets.Set(args) - build_dirs:
+ if re.search(PYTHON_TEST_REGEX, argument):
+ python_path = self.os.path.join('test', self.os.path.basename(argument))
+ if self.os.path.isfile(self.os.path.join(MY_DIR, python_path)):
+ listed_python_tests.append(python_path)
+ else:
+ sys.stderr.write('Unable to find Python test %s' % argument)
+ errors_found = True
+ elif re.search(BINARY_TEST_REGEX, argument):
+ # This script also accepts binary test names prefixed with test/ for
+ # the convenience of typing them (can use path completions in the
+ # shell). Strips test/ prefix from the binary test names.
+ listed_binary_tests.append(self.os.path.basename(argument))
+ else:
+ sys.stderr.write('%s is neither test nor build directory' % argument)
+ errors_found = True
+
+ if errors_found:
+ return None
+
+ user_has_listed_tests = listed_python_tests or listed_binary_tests
+
+ if user_has_listed_tests:
+ selected_python_tests = listed_python_tests
+ else:
+ selected_python_tests = self.FindFilesByRegex('test', PYTHON_TEST_REGEX)
+
+ # TODO(vladl@google.com): skip unbuilt Python tests when -b is specified.
+ python_test_pairs = []
+ for directory in build_dirs:
+ for test in selected_python_tests:
+ python_test_pairs.append((directory, test))
+
+ binary_test_pairs = []
+ for directory in build_dirs:
+ if user_has_listed_tests:
+ binary_test_pairs.extend(
+ [(directory, self.os.path.join(directory, test))
+ for test in listed_binary_tests])
+ else:
+ tests = self.FindFilesByRegex(directory, BINARY_TEST_REGEX)
+ binary_test_pairs.extend([(directory, test) for test in tests])
+
+ return (python_test_pairs, binary_test_pairs)
+
+ def RunTests(self, python_tests, binary_tests):
+ """Runs Python and binary tests represented as pairs (work_dir, binary).
+
+ Args:
+ python_tests: List of Python tests to run in the form of tuples
+ (build directory, Python test script).
+ binary_tests: List of binary tests to run in the form of tuples
+ (build directory, binary file).
+
+ Returns:
+ The exit code the program should pass into sys.exit().
+ """
+
+ if python_tests or binary_tests:
+ results = []
+ for directory, test in python_tests:
+ results.append((directory,
+ test,
+ self.RunPythonTest(test, directory) == 0))
+ for directory, test in binary_tests:
+ results.append((directory,
+ self.os.path.basename(test),
+ self.RunBinaryTest(test) == 0))
+
+ failed = [(directory, test)
+ for (directory, test, success) in results
+ if not success]
+ print
+ print '%d tests run.' % len(results)
+ if failed:
+ print 'The following %d tests failed:' % len(failed)
+ for (directory, test) in failed:
+ print '%s in %s' % (test, directory)
+ return 1
+ else:
+ print 'All tests passed!'
+ else: # No tests defined
+ print 'Nothing to test - no tests specified!'
+
+ return 0
+
+
+def _Main():
+ """Runs all tests for Google Test."""
+
+ parser = optparse.OptionParser()
+ parser.add_option('-c',
+ action='store',
+ dest='configurations',
+ default=None,
+ help='Test in the specified build directories')
+ parser.add_option('-a',
+ action='store_const',
+ dest='configurations',
+ default=None,
+ const='all',
+ help='Test in all default build directories')
+ parser.add_option('-b',
+ action='store_const',
+ dest='built_configurations',
+ default=False,
+ const=True,
+ help=('Test in all default build directories, do not fail'
+ 'if some of them do not exist'))
+ (options, args) = parser.parse_args()
+
+ test_runner = TestRunner()
+ tests = test_runner.GetTestsToRun(args,
+ options.configurations,
+ options.built_configurations)
+ if not tests:
+ sys.exit(1) # Incorrect parameters given, abort execution.
+
+ sys.exit(test_runner.RunTests(tests[0], tests[1]))
+
+if __name__ == '__main__':
+ _Main()
diff --git a/test/run_tests_test.py b/test/run_tests_test.py
new file mode 100755
index 00000000..75b7d9d2
--- /dev/null
+++ b/test/run_tests_test.py
@@ -0,0 +1,527 @@
+#!/usr/bin/env python
+#
+# Copyright 2009 Google Inc. All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Tests for run_tests.py test runner script."""
+
+__author__ = 'vladl@google.com (Vlad Losev)'
+
+import os
+import sys
+import unittest
+
+sys.path.append(os.path.join(os.path.dirname(sys.argv[0]), os.pardir))
+import run_tests
+
+
+class FakePath(object):
+ """A fake os.path module for testing."""
+
+ def __init__(self, current_dir=os.getcwd(), known_paths=None):
+ self.current_dir = current_dir
+ self.tree = {}
+ self.path_separator = os.sep
+
+ if known_paths:
+ self._AddPaths(known_paths)
+
+ def _AddPath(self, path):
+ ends_with_slash = path.endswith('/')
+ path = self.abspath(path)
+ if ends_with_slash:
+ path += self.path_separator
+ name_list = path.split(self.path_separator)
+ tree = self.tree
+ for name in name_list[:-1]:
+ if not name:
+ continue
+ if name in tree:
+ tree = tree[name]
+ else:
+ tree[name] = {}
+ tree = tree[name]
+
+ name = name_list[-1]
+ if name:
+ if name in tree:
+ assert tree[name] == 1
+ else:
+ tree[name] = 1
+
+ def _AddPaths(self, paths):
+ for path in paths:
+ self._AddPath(path)
+
+ def PathElement(self, path):
+ """Returns an internal representation of directory tree entry for path."""
+ tree = self.tree
+ name_list = self.abspath(path).split(self.path_separator)
+ for name in name_list:
+ if not name:
+ continue
+ tree = tree.get(name, None)
+ if tree is None:
+ break
+
+ return tree
+
+ def abspath(self, path):
+ return os.path.normpath(os.path.join(self.current_dir, path))
+
+ def isfile(self, path):
+ return self.PathElement(self.abspath(path)) == 1
+
+ def isdir(self, path):
+ return type(self.PathElement(self.abspath(path))) == type(dict())
+
+ def basename(self, path):
+ return os.path.basename(path)
+
+ def dirname(self, path):
+ return os.path.dirname(path)
+
+ def join(self, *kargs):
+ return os.path.join(*kargs)
+
+
+class FakeOs(object):
+ """A fake os module for testing."""
+ P_WAIT = os.P_WAIT
+
+ def __init__(self, fake_path_module):
+ self.path = fake_path_module
+
+ # Some methods/attributes are delegated to the real os module.
+ self.environ = os.environ
+
+ def listdir(self, path):
+ assert self.path.isdir(path)
+ return self.path.PathElement(path).iterkeys()
+
+ def spawn(self, wait, executable, *kargs):
+ assert wait == FakeOs.P_WAIT
+ return self.spawn_impl(executable, kargs)
+
+
+class GetTestsToRunTest(unittest.TestCase):
+ """Exercises TestRunner.GetTestsToRun."""
+
+ def AssertResultsEqual(self, results, expected):
+ """Asserts results returned by GetTestsToRun equal to expected results."""
+
+ def NormalizeResultPaths(paths):
+ """Normalizes values returned by GetTestsToRun for comparison."""
+
+ def NormalizeResultPair(pair):
+ return (os.path.normpath(pair[0]), os.path.normpath(pair[1]))
+
+ return (sorted(map(NormalizeResultPair, paths[0])),
+ sorted(map(NormalizeResultPair, paths[1])))
+
+ self.assertEqual(NormalizeResultPaths(results),
+ NormalizeResultPaths(expected),
+ 'Incorrect set of tests %s returned vs %s expected' %
+ (results, expected))
+
+ def setUp(self):
+ self.fake_os = FakeOs(FakePath(
+ current_dir=os.path.abspath(os.path.dirname(run_tests.__file__)),
+ known_paths=['scons/build/dbg/scons/gtest_unittest',
+ 'scons/build/opt/scons/gtest_unittest',
+ 'test/gtest_color_test.py']))
+ self.fake_configurations = ['dbg', 'opt']
+ self.test_runner = run_tests.TestRunner(injected_os=self.fake_os,
+ injected_subprocess=None)
+
+ def testBinaryTestsOnly(self):
+ """Exercises GetTestsToRun with parameters designating binary tests only."""
+
+ # A default build.
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['gtest_unittest'],
+ '',
+ False,
+ available_configurations=self.fake_configurations),
+ ([],
+ [('scons/build/dbg/scons', 'scons/build/dbg/scons/gtest_unittest')]))
+
+ # An explicitly specified directory.
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['scons/build/dbg/scons', 'gtest_unittest'],
+ '',
+ False,
+ available_configurations=self.fake_configurations),
+ ([],
+ [('scons/build/dbg/scons', 'scons/build/dbg/scons/gtest_unittest')]))
+
+ # A particular configuration.
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['gtest_unittest'],
+ 'other',
+ False,
+ available_configurations=self.fake_configurations),
+ ([],
+ [('scons/build/other/scons',
+ 'scons/build/other/scons/gtest_unittest')]))
+
+ # All available configurations
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['gtest_unittest'],
+ 'all',
+ False,
+ available_configurations=self.fake_configurations),
+ ([],
+ [('scons/build/dbg/scons', 'scons/build/dbg/scons/gtest_unittest'),
+ ('scons/build/opt/scons', 'scons/build/opt/scons/gtest_unittest')]))
+
+ # All built configurations (unbuilt don't cause failure).
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['gtest_unittest'],
+ '',
+ True,
+ available_configurations=self.fake_configurations + ['unbuilt']),
+ ([],
+ [('scons/build/dbg/scons', 'scons/build/dbg/scons/gtest_unittest'),
+ ('scons/build/opt/scons', 'scons/build/opt/scons/gtest_unittest')]))
+
+ # A combination of an explicit directory and a configuration.
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['scons/build/dbg/scons', 'gtest_unittest'],
+ 'opt',
+ False,
+ available_configurations=self.fake_configurations),
+ ([],
+ [('scons/build/dbg/scons', 'scons/build/dbg/scons/gtest_unittest'),
+ ('scons/build/opt/scons', 'scons/build/opt/scons/gtest_unittest')]))
+
+ # Same test specified in an explicit directory and via a configuration.
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['scons/build/dbg/scons', 'gtest_unittest'],
+ 'dbg',
+ False,
+ available_configurations=self.fake_configurations),
+ ([],
+ [('scons/build/dbg/scons', 'scons/build/dbg/scons/gtest_unittest')]))
+
+ # All built configurations + explicit directory + explicit configuration.
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['scons/build/dbg/scons', 'gtest_unittest'],
+ 'opt',
+ True,
+ available_configurations=self.fake_configurations),
+ ([],
+ [('scons/build/dbg/scons', 'scons/build/dbg/scons/gtest_unittest'),
+ ('scons/build/opt/scons', 'scons/build/opt/scons/gtest_unittest')]))
+
+ def testPythonTestsOnly(self):
+ """Exercises GetTestsToRun with parameters designating Python tests only."""
+
+ # A default build.
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['gtest_color_test.py'],
+ '',
+ False,
+ available_configurations=self.fake_configurations),
+ ([('scons/build/dbg/scons', 'test/gtest_color_test.py')],
+ []))
+
+ # An explicitly specified directory.
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['scons/build/dbg/scons', 'test/gtest_color_test.py'],
+ '',
+ False,
+ available_configurations=self.fake_configurations),
+ ([('scons/build/dbg/scons', 'test/gtest_color_test.py')],
+ []))
+
+ # A particular configuration.
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['gtest_color_test.py'],
+ 'other',
+ False,
+ available_configurations=self.fake_configurations),
+ ([('scons/build/other/scons', 'test/gtest_color_test.py')],
+ []))
+
+ # All available configurations
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['test/gtest_color_test.py'],
+ 'all',
+ False,
+ available_configurations=self.fake_configurations),
+ ([('scons/build/dbg/scons', 'test/gtest_color_test.py'),
+ ('scons/build/opt/scons', 'test/gtest_color_test.py')],
+ []))
+
+ # All built configurations (unbuilt don't cause failure).
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['gtest_color_test.py'],
+ '',
+ True,
+ available_configurations=self.fake_configurations + ['unbuilt']),
+ ([('scons/build/dbg/scons', 'test/gtest_color_test.py'),
+ ('scons/build/opt/scons', 'test/gtest_color_test.py')],
+ []))
+
+ # A combination of an explicit directory and a configuration.
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['scons/build/dbg/scons', 'gtest_color_test.py'],
+ 'opt',
+ False,
+ available_configurations=self.fake_configurations),
+ ([('scons/build/dbg/scons', 'test/gtest_color_test.py'),
+ ('scons/build/opt/scons', 'test/gtest_color_test.py')],
+ []))
+
+ # Same test specified in an explicit directory and via a configuration.
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['scons/build/dbg/scons', 'gtest_color_test.py'],
+ 'dbg',
+ False,
+ available_configurations=self.fake_configurations),
+ ([('scons/build/dbg/scons', 'test/gtest_color_test.py')],
+ []))
+
+ # All built configurations + explicit directory + explicit configuration.
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['scons/build/dbg/scons', 'gtest_color_test.py'],
+ 'opt',
+ True,
+ available_configurations=self.fake_configurations),
+ ([('scons/build/dbg/scons', 'test/gtest_color_test.py'),
+ ('scons/build/opt/scons', 'test/gtest_color_test.py')],
+ []))
+
+ def testCombinationOfBinaryAndPythonTests(self):
+ """Exercises GetTestsToRun with mixed binary/Python tests."""
+
+ # Use only default configuration for this test.
+
+ # Neither binary nor Python tests are specified so find all.
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ [],
+ '',
+ False,
+ available_configurations=self.fake_configurations),
+ ([('scons/build/dbg/scons', 'test/gtest_color_test.py')],
+ [('scons/build/dbg/scons', 'scons/build/dbg/scons/gtest_unittest')]))
+
+ # Specifying both binary and Python tests.
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['gtest_unittest', 'gtest_color_test.py'],
+ '',
+ False,
+ available_configurations=self.fake_configurations),
+ ([('scons/build/dbg/scons', 'test/gtest_color_test.py')],
+ [('scons/build/dbg/scons', 'scons/build/dbg/scons/gtest_unittest')]))
+
+ # Specifying binary tests suppresses Python tests.
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['gtest_unittest'],
+ '',
+ False,
+ available_configurations=self.fake_configurations),
+ ([],
+ [('scons/build/dbg/scons', 'scons/build/dbg/scons/gtest_unittest')]))
+
+ # Specifying Python tests suppresses binary tests.
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ ['gtest_color_test.py'],
+ '',
+ False,
+ available_configurations=self.fake_configurations),
+ ([('scons/build/dbg/scons', 'test/gtest_color_test.py')],
+ []))
+
+ def testIgnoresNonTestFiles(self):
+ """Verifies that GetTestsToRun ignores non-test files in the filesystem."""
+
+ self.fake_os = FakeOs(FakePath(
+ current_dir=os.path.abspath(os.path.dirname(run_tests.__file__)),
+ known_paths=['scons/build/dbg/scons/gtest_nontest',
+ 'scons/build/opt/scons/gtest_nontest.exe',
+ 'test/']))
+ self.test_runner = run_tests.TestRunner(injected_os=self.fake_os,
+ injected_subprocess=None)
+ self.AssertResultsEqual(
+ self.test_runner.GetTestsToRun(
+ [],
+ '',
+ True,
+ available_configurations=self.fake_configurations),
+ ([], []))
+
+ def testNonTestBinary(self):
+ """Exercises GetTestsToRun with a non-test parameter."""
+
+ self.assert_(
+ not self.test_runner.GetTestsToRun(
+ ['gtest_unittest_not_really'],
+ '',
+ False,
+ available_configurations=self.fake_configurations))
+
+ def testNonExistingPythonTest(self):
+ """Exercises GetTestsToRun with a non-existent Python test parameter."""
+
+ self.assert_(
+ not self.test_runner.GetTestsToRun(
+ ['nonexistent_test.py'],
+ '',
+ False,
+ available_configurations=self.fake_configurations))
+
+
+class RunTestsTest(unittest.TestCase):
+ """Exercises TestRunner.RunTests."""
+
+ def SpawnSuccess(self, unused_executable, unused_argv):
+ """Fakes test success by returning 0 as an exit code."""
+
+ self.num_spawn_calls += 1
+ return 0
+
+ def SpawnFailure(self, unused_executable, unused_argv):
+ """Fakes test success by returning 1 as an exit code."""
+
+ self.num_spawn_calls += 1
+ return 1
+
+ def setUp(self):
+ self.fake_os = FakeOs(FakePath(
+ current_dir=os.path.abspath(os.path.dirname(run_tests.__file__)),
+ known_paths=['scons/build/dbg/scons/gtest_unittest',
+ 'scons/build/opt/scons/gtest_unittest',
+ 'test/gtest_color_test.py']))
+ self.fake_configurations = ['dbg', 'opt']
+ self.test_runner = run_tests.TestRunner(injected_os=self.fake_os,
+ injected_subprocess=None)
+ self.num_spawn_calls = 0 # A number of calls to spawn.
+
+ def testRunPythonTestSuccess(self):
+ """Exercises RunTests to handle a Python test success."""
+
+ self.fake_os.spawn_impl = self.SpawnSuccess
+ self.assertEqual(
+ self.test_runner.RunTests(
+ [('scons/build/dbg/scons', 'test/gtest_color_test.py')],
+ []),
+ 0)
+ self.assertEqual(self.num_spawn_calls, 1)
+
+ def testRunBinaryTestSuccess(self):
+ """Exercises RunTests to handle a binary test success."""
+
+ self.fake_os.spawn_impl = self.SpawnSuccess
+ self.assertEqual(
+ self.test_runner.RunTests(
+ [],
+ [('scons/build/dbg/scons',
+ 'scons/build/dbg/scons/gtest_unittest')]),
+ 0)
+ self.assertEqual(self.num_spawn_calls, 1)
+
+ def testRunPythonTestFauilure(self):
+ """Exercises RunTests to handle a Python test failure."""
+
+ self.fake_os.spawn_impl = self.SpawnFailure
+ self.assertEqual(
+ self.test_runner.RunTests(
+ [('scons/build/dbg/scons', 'test/gtest_color_test.py')],
+ []),
+ 1)
+ self.assertEqual(self.num_spawn_calls, 1)
+
+ def testRunBinaryTestFailure(self):
+ """Exercises RunTests to handle a binary test failure."""
+
+ self.fake_os.spawn_impl = self.SpawnFailure
+ self.assertEqual(
+ self.test_runner.RunTests(
+ [],
+ [('scons/build/dbg/scons',
+ 'scons/build/dbg/scons/gtest_unittest')]),
+ 1)
+ self.assertEqual(self.num_spawn_calls, 1)
+
+ def testCombinedTestSuccess(self):
+ """Exercises RunTests to handle a success of both Python and binary test."""
+
+ self.fake_os.spawn_impl = self.SpawnSuccess
+ self.assertEqual(
+ self.test_runner.RunTests(
+ [('scons/build/dbg/scons', 'scons/build/dbg/scons/gtest_unittest')],
+ [('scons/build/dbg/scons',
+ 'scons/build/dbg/scons/gtest_unittest')]),
+ 0)
+ self.assertEqual(self.num_spawn_calls, 2)
+
+ def testCombinedTestSuccessAndFailure(self):
+ """Exercises RunTests to handle a success of both Python and binary test."""
+
+ def SpawnImpl(executable, argv):
+ self.num_spawn_calls += 1
+ # Simulates failure of a Python test and success of a binary test.
+ if '.py' in executable or '.py' in argv[0]:
+ return 1
+ else:
+ return 0
+
+ self.fake_os.spawn_impl = SpawnImpl
+ self.assertEqual(
+ self.test_runner.RunTests(
+ [('scons/build/dbg/scons', 'scons/build/dbg/scons/gtest_unittest')],
+ [('scons/build/dbg/scons',
+ 'scons/build/dbg/scons/gtest_unittest')]),
+ 0)
+ self.assertEqual(self.num_spawn_calls, 2)
+
+
+if __name__ == '__main__':
+ unittest.main()