From 532dc2de35f2cef191bc91c3587a9f8f4974756f Mon Sep 17 00:00:00 2001 From: "zhanyong.wan" Date: Wed, 17 Jun 2009 21:06:27 +0000 Subject: Implements a subset of TR1 tuple needed by gtest and gmock (by Zhanyong Wan); cleaned up the Python tests (by Vlad Losev); made run_tests.py invokable from any directory (by Vlad Losev). --- test/gtest-tuple_test.cc | 288 ++++++++++++++++++++++++++++++++ test/gtest_break_on_failure_unittest.py | 3 +- test/gtest_color_test.py | 7 +- test/gtest_env_var_test.py | 12 +- test/gtest_filter_unittest.py | 72 ++++---- test/gtest_help_test.py | 5 +- test/gtest_list_tests_unittest.py | 7 +- test/gtest_nc_test.py | 5 + test/gtest_output_test.py | 87 +++++++--- test/gtest_test_utils.py | 37 +++- test/gtest_throw_on_failure_test.py | 5 +- test/gtest_uninitialized_test.py | 4 +- test/gtest_xml_outfiles_test.py | 15 +- test/gtest_xml_output_unittest.py | 31 ++-- test/gtest_xml_test_utils.py | 7 +- test/run_tests_test.py | 50 +++++- 16 files changed, 524 insertions(+), 111 deletions(-) create mode 100644 test/gtest-tuple_test.cc (limited to 'test') diff --git a/test/gtest-tuple_test.cc b/test/gtest-tuple_test.cc new file mode 100644 index 00000000..3829118e --- /dev/null +++ b/test/gtest-tuple_test.cc @@ -0,0 +1,288 @@ +// Copyright 2007, 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. +// +// Author: wan@google.com (Zhanyong Wan) + +#include +#include +#include + +namespace { + +using ::std::tr1::get; +using ::std::tr1::make_tuple; +using ::std::tr1::tuple; +using ::std::tr1::tuple_element; +using ::std::tr1::tuple_size; +using ::testing::StaticAssertTypeEq; + +// Tests that tuple_element >::type returns TK. +TEST(tuple_element_Test, ReturnsElementType) { + StaticAssertTypeEq >::type>(); + StaticAssertTypeEq >::type>(); + StaticAssertTypeEq >::type>(); +} + +// Tests that tuple_size::value gives the number of fields in tuple +// type T. +TEST(tuple_size_Test, ReturnsNumberOfFields) { + EXPECT_EQ(0, +tuple_size >::value); + EXPECT_EQ(1, +tuple_size >::value); + EXPECT_EQ(1, +tuple_size >::value); + EXPECT_EQ(1, +(tuple_size > >::value)); + EXPECT_EQ(2, +(tuple_size >::value)); + EXPECT_EQ(3, +(tuple_size >::value)); +} + +// Tests comparing a tuple with itself. +TEST(ComparisonTest, ComparesWithSelf) { + const tuple a(5, 'a', false); + + EXPECT_TRUE(a == a); + EXPECT_FALSE(a != a); +} + +// Tests comparing two tuples with the same value. +TEST(ComparisonTest, ComparesEqualTuples) { + const tuple a(5, true), b(5, true); + + EXPECT_TRUE(a == b); + EXPECT_FALSE(a != b); +} + +// Tests comparing two different tuples that have no reference fields. +TEST(ComparisonTest, ComparesUnequalTuplesWithoutReferenceFields) { + typedef tuple FooTuple; + + const FooTuple a(0, 'x'); + const FooTuple b(1, 'a'); + + EXPECT_TRUE(a != b); + EXPECT_FALSE(a == b); + + const FooTuple c(1, 'b'); + + EXPECT_TRUE(b != c); + EXPECT_FALSE(b == c); +} + +// Tests comparing two different tuples that have reference fields. +TEST(ComparisonTest, ComparesUnequalTuplesWithReferenceFields) { + typedef tuple FooTuple; + + int i = 5; + const char ch = 'a'; + const FooTuple a(i, ch); + + int j = 6; + const FooTuple b(j, ch); + + EXPECT_TRUE(a != b); + EXPECT_FALSE(a == b); + + j = 5; + const char ch2 = 'b'; + const FooTuple c(j, ch2); + + EXPECT_TRUE(b != c); + EXPECT_FALSE(b == c); +} + +// Tests that a tuple field with a reference type is an alias of the +// variable it's supposed to reference. +TEST(ReferenceFieldTest, IsAliasOfReferencedVariable) { + int n = 0; + tuple t(true, n); + + n = 1; + EXPECT_EQ(n, get<1>(t)) + << "Changing a underlying variable should update the reference field."; + + // Makes sure that the implementation doesn't do anything funny with + // the & operator for the return type of get<>(). + EXPECT_EQ(&n, &(get<1>(t))) + << "The address of a reference field should equal the address of " + << "the underlying variable."; + + get<1>(t) = 2; + EXPECT_EQ(2, n) + << "Changing a reference field should update the underlying variable."; +} + +// Tests tuple's default constructor. +TEST(TupleConstructorTest, DefaultConstructor) { + // We are just testing that the following compiles. + tuple<> empty; + tuple one_field; + tuple three_fields; +} + +// Tests constructing a tuple from its fields. +TEST(TupleConstructorTest, ConstructsFromFields) { + int n = 1; + // Reference field. + tuple a(n); + EXPECT_EQ(&n, &(get<0>(a))); + + // Non-reference fields. + tuple b(5, 'a'); + EXPECT_EQ(5, get<0>(b)); + EXPECT_EQ('a', get<1>(b)); + + // Const reference field. + const int m = 2; + tuple c(true, m); + EXPECT_TRUE(get<0>(c)); + EXPECT_EQ(&m, &(get<1>(c))); +} + +// Tests tuple's copy constructor. +TEST(TupleConstructorTest, CopyConstructor) { + tuple a(0.0, true); + tuple b(a); + + EXPECT_DOUBLE_EQ(0.0, get<0>(b)); + EXPECT_TRUE(get<1>(b)); +} + +// Tests constructing a tuple from another tuple that has a compatible +// but different type. +TEST(TupleConstructorTest, ConstructsFromDifferentTupleType) { + tuple a(0, 1, 'a'); + tuple b(a); + + EXPECT_DOUBLE_EQ(0.0, get<0>(b)); + EXPECT_EQ(1, get<1>(b)); + EXPECT_EQ('a', get<2>(b)); +} + +// Tests constructing a 2-tuple from an std::pair. +TEST(TupleConstructorTest, ConstructsFromPair) { + ::std::pair a(1, 'a'); + tuple b(a); + tuple c(a); +} + +// Tests assigning a tuple to another tuple with the same type. +TEST(TupleAssignmentTest, AssignsToSameTupleType) { + const tuple a(5, 7L); + tuple b; + b = a; + EXPECT_EQ(5, get<0>(b)); + EXPECT_EQ(7L, get<1>(b)); +} + +// Tests assigning a tuple to another tuple with a different but +// compatible type. +TEST(TupleAssignmentTest, AssignsToDifferentTupleType) { + const tuple a(1, 7L, true); + tuple b; + b = a; + EXPECT_EQ(1L, get<0>(b)); + EXPECT_EQ(7, get<1>(b)); + EXPECT_TRUE(get<2>(b)); +} + +// Tests assigning an std::pair to a 2-tuple. +TEST(TupleAssignmentTest, AssignsFromPair) { + const ::std::pair a(5, true); + tuple b; + b = a; + EXPECT_EQ(5, get<0>(b)); + EXPECT_TRUE(get<1>(b)); + + tuple c; + c = a; + EXPECT_EQ(5L, get<0>(c)); + EXPECT_TRUE(get<1>(c)); +} + +// A fixture for testing big tuples. +class BigTupleTest : public testing::Test { + protected: + typedef tuple BigTuple; + + BigTupleTest() : + a_(1, 0, 0, 0, 0, 0, 0, 0, 0, 2), + b_(1, 0, 0, 0, 0, 0, 0, 0, 0, 3) {} + + BigTuple a_, b_; +}; + +// Tests constructing big tuples. +TEST_F(BigTupleTest, Construction) { + BigTuple a; + BigTuple b(b_); +} + +// Tests that get(t) returns the N-th (0-based) field of tuple t. +TEST_F(BigTupleTest, get) { + EXPECT_EQ(1, get<0>(a_)); + EXPECT_EQ(2, get<9>(a_)); + + // Tests that get() works on a const tuple too. + const BigTuple a(a_); + EXPECT_EQ(1, get<0>(a)); + EXPECT_EQ(2, get<9>(a)); +} + +// Tests comparing big tuples. +TEST_F(BigTupleTest, Comparisons) { + EXPECT_TRUE(a_ == a_); + EXPECT_FALSE(a_ != a_); + + EXPECT_TRUE(a_ != b_); + EXPECT_FALSE(a_ == b_); +} + +TEST(MakeTupleTest, WorksForScalarTypes) { + tuple a; + a = make_tuple(true, 5); + EXPECT_TRUE(get<0>(a)); + EXPECT_EQ(5, get<1>(a)); + + tuple b; + b = make_tuple('a', 'b', 5); + EXPECT_EQ('a', get<0>(b)); + EXPECT_EQ('b', get<1>(b)); + EXPECT_EQ(5, get<2>(b)); +} + +TEST(MakeTupleTest, WorksForPointers) { + int a[] = { 1, 2, 3, 4 }; + const char* const str = "hi"; + int* const p = a; + + tuple t; + t = make_tuple(str, p); + EXPECT_EQ(str, get<0>(t)); + EXPECT_EQ(p, get<1>(t)); +} + +} // namespace diff --git a/test/gtest_break_on_failure_unittest.py b/test/gtest_break_on_failure_unittest.py index cae288ad..218d3713 100755 --- a/test/gtest_break_on_failure_unittest.py +++ b/test/gtest_break_on_failure_unittest.py @@ -43,7 +43,6 @@ __author__ = 'wan@google.com (Zhanyong Wan)' import gtest_test_utils import os import sys -import unittest # Constants. @@ -94,7 +93,7 @@ def Run(command): # The tests. -class GTestBreakOnFailureUnitTest(unittest.TestCase): +class GTestBreakOnFailureUnitTest(gtest_test_utils.TestCase): """Tests using the GTEST_BREAK_ON_FAILURE environment variable or the --gtest_break_on_failure flag to turn assertion failures into segmentation faults. diff --git a/test/gtest_color_test.py b/test/gtest_color_test.py index 1b686304..f617dc5c 100755 --- a/test/gtest_color_test.py +++ b/test/gtest_color_test.py @@ -33,10 +33,9 @@ __author__ = 'wan@google.com (Zhanyong Wan)' -import gtest_test_utils import os -import sys -import unittest +import gtest_test_utils + IS_WINDOWS = os.name = 'nt' @@ -65,7 +64,7 @@ def UsesColor(term, color_env_var, color_flag): return gtest_test_utils.GetExitStatus(os.system(cmd)) -class GTestColorTest(unittest.TestCase): +class GTestColorTest(gtest_test_utils.TestCase): def testNoEnvVarNoFlag(self): """Tests the case when there's neither GTEST_COLOR nor --gtest_color.""" diff --git a/test/gtest_env_var_test.py b/test/gtest_env_var_test.py index 35e8041f..54719fac 100755 --- a/test/gtest_env_var_test.py +++ b/test/gtest_env_var_test.py @@ -33,13 +33,12 @@ __author__ = 'wan@google.com (Zhanyong Wan)' -import gtest_test_utils import os -import sys -import unittest +import gtest_test_utils + IS_WINDOWS = os.name == 'nt' -IS_LINUX = os.name == 'posix' +IS_LINUX = os.name == 'posix' and os.uname()[0] == 'Linux' COMMAND = gtest_test_utils.GetTestExecutablePath('gtest_env_var_test_') @@ -97,12 +96,13 @@ def TestEnvVarAffectsFlag(command): if IS_WINDOWS: TestFlag(command, 'catch_exceptions', '1', '0') + if IS_LINUX: - TestFlag(command, 'stack_trace_depth', '0', '100') TestFlag(command, 'death_test_use_fork', '1', '0') + TestFlag(command, 'stack_trace_depth', '0', '100') -class GTestEnvVarTest(unittest.TestCase): +class GTestEnvVarTest(gtest_test_utils.TestCase): def testEnvVarAffectsFlag(self): TestEnvVarAffectsFlag(COMMAND) diff --git a/test/gtest_filter_unittest.py b/test/gtest_filter_unittest.py index 6002fac9..4e9556b7 100755 --- a/test/gtest_filter_unittest.py +++ b/test/gtest_filter_unittest.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # -# Copyright 2005, Google Inc. -# All rights reserved. +# Copyright 2005 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 @@ -46,8 +45,6 @@ __author__ = 'wan@google.com (Zhanyong Wan)' import os import re import sets -import tempfile -import unittest import gtest_test_utils # Constants. @@ -133,9 +130,7 @@ def SetEnvVar(env_var, value): def Run(command): - """Runs a Google Test program and returns a list of full names of the - tests that were run along with the test exit code. - """ + """Runs a test program and returns its exit code and a list of tests run.""" stdout_file = os.popen(command, 'r') tests_run = [] @@ -169,9 +164,7 @@ def InvokeWithModifiedEnv(extra_env, function, *args, **kwargs): def RunWithSharding(total_shards, shard_index, command): - """Runs the Google Test program shard and returns a list of full names of the - tests that were run along with the exit code. - """ + """Runs a test program shard and returns exit code and a list of tests run.""" extra_env = {SHARD_INDEX_ENV_VAR: str(shard_index), TOTAL_SHARDS_ENV_VAR: str(total_shards)} @@ -180,10 +173,8 @@ def RunWithSharding(total_shards, shard_index, command): # The unit test. -class GTestFilterUnitTest(unittest.TestCase): - """Tests using the GTEST_FILTER environment variable or the - --gtest_filter flag to filter tests. - """ +class GTestFilterUnitTest(gtest_test_utils.TestCase): + """Tests GTEST_FILTER env variable or --gtest_filter flag to filter tests.""" # Utilities. @@ -206,9 +197,8 @@ class GTestFilterUnitTest(unittest.TestCase): self.assertEqual(sets.Set(set_var), sets.Set(full_partition)) def AdjustForParameterizedTests(self, tests_to_run): - """Adjust tests_to_run in case value parameterized tests are disabled - in the binary. - """ + """Adjust tests_to_run in case value parameterized tests are disabled.""" + global param_tests_present if not param_tests_present: return list(sets.Set(tests_to_run) - sets.Set(PARAM_TESTS)) @@ -216,9 +206,8 @@ class GTestFilterUnitTest(unittest.TestCase): return tests_to_run def RunAndVerify(self, gtest_filter, tests_to_run): - """Runs gtest_flag_unittest_ with the given filter, and verifies - that the right set of tests were run. - """ + """Checks that the binary runs correct set of tests for the given filter.""" + tests_to_run = self.AdjustForParameterizedTests(tests_to_run) # First, tests using GTEST_FILTER. @@ -248,11 +237,21 @@ class GTestFilterUnitTest(unittest.TestCase): def RunAndVerifyWithSharding(self, gtest_filter, total_shards, tests_to_run, command=COMMAND, check_exit_0=False): - """Runs all shards of gtest_flag_unittest_ with the given filter, and + """Checks that binary runs correct tests for the given filter and shard. + + Runs all shards of gtest_filter_unittest_ with the given filter, and verifies that the right set of tests were run. The union of tests run on each shard should be identical to tests_to_run, without duplicates. - If check_exit_0, make sure that all shards returned 0. + + Args: + gtest_filter: A filter to apply to the tests. + total_shards: A total number of shards to split test run into. + tests_to_run: A set of tests expected to run. + command: A command to invoke the test binary. + check_exit_0: When set to a true value, make sure that all shards + return 0. """ + tests_to_run = self.AdjustForParameterizedTests(tests_to_run) # Windows removes empty variables from the environment when passing it @@ -275,9 +274,16 @@ class GTestFilterUnitTest(unittest.TestCase): # pylint: enable-msg=C6403 def RunAndVerifyAllowingDisabled(self, gtest_filter, tests_to_run): - """Runs gtest_flag_unittest_ with the given filter, and enables + """Checks that the binary runs correct set of tests for the given filter. + + Runs gtest_filter_unittest_ with the given filter, and enables disabled tests. Verifies that the right set of tests were run. + + Args: + gtest_filter: A filter to apply to the tests. + tests_to_run: A set of tests expected to run. """ + tests_to_run = self.AdjustForParameterizedTests(tests_to_run) # Construct the command line. @@ -294,6 +300,7 @@ class GTestFilterUnitTest(unittest.TestCase): Determines whether value-parameterized tests are enabled in the binary and sets the flags accordingly. """ + global param_tests_present if param_tests_present is None: param_tests_present = PARAM_TEST_REGEX.search( @@ -305,9 +312,8 @@ class GTestFilterUnitTest(unittest.TestCase): self.RunAndVerify(None, ACTIVE_TESTS) def testDefaultBehaviorWithShards(self): - """Tests the behavior of not specifying the filter, with sharding - enabled. - """ + """Tests the behavior without the filter, with sharding enabled.""" + self.RunAndVerifyWithSharding(None, 1, ACTIVE_TESTS) self.RunAndVerifyWithSharding(None, 2, ACTIVE_TESTS) self.RunAndVerifyWithSharding(None, len(ACTIVE_TESTS) - 1, ACTIVE_TESTS) @@ -520,9 +526,7 @@ class GTestFilterUnitTest(unittest.TestCase): ]) def testFlagOverridesEnvVar(self): - """Tests that the --gtest_filter flag overrides the GTEST_FILTER - environment variable. - """ + """Tests that the filter flag overrides the filtering env. variable.""" SetEnvVar(FILTER_ENV_VAR, 'Foo*') command = '%s --%s=%s' % (COMMAND, FILTER_FLAG, '*One') @@ -534,8 +538,8 @@ class GTestFilterUnitTest(unittest.TestCase): def testShardStatusFileIsCreated(self): """Tests that the shard file is created if specified in the environment.""" - test_tmpdir = tempfile.mkdtemp() - shard_status_file = os.path.join(test_tmpdir, 'shard_status_file') + shard_status_file = os.path.join(gtest_test_utils.GetTempDir(), + 'shard_status_file') self.assert_(not os.path.exists(shard_status_file)) extra_env = {SHARD_STATUS_FILE_ENV_VAR: shard_status_file} @@ -546,13 +550,12 @@ class GTestFilterUnitTest(unittest.TestCase): stdout_file.close() self.assert_(os.path.exists(shard_status_file)) os.remove(shard_status_file) - os.removedirs(test_tmpdir) def testShardStatusFileIsCreatedWithListTests(self): """Tests that the shard file is created with --gtest_list_tests.""" - test_tmpdir = tempfile.mkdtemp() - shard_status_file = os.path.join(test_tmpdir, 'shard_status_file2') + shard_status_file = os.path.join(gtest_test_utils.GetTempDir(), + 'shard_status_file2') self.assert_(not os.path.exists(shard_status_file)) extra_env = {SHARD_STATUS_FILE_ENV_VAR: shard_status_file} @@ -564,7 +567,6 @@ class GTestFilterUnitTest(unittest.TestCase): stdout_file.close() self.assert_(os.path.exists(shard_status_file)) os.remove(shard_status_file) - os.removedirs(test_tmpdir) def testShardingWorksWithDeathTests(self): """Tests integration with death tests and sharding.""" diff --git a/test/gtest_help_test.py b/test/gtest_help_test.py index d81f149f..0a2a07b6 100755 --- a/test/gtest_help_test.py +++ b/test/gtest_help_test.py @@ -39,10 +39,9 @@ SYNOPSIS __author__ = 'wan@google.com (Zhanyong Wan)' -import gtest_test_utils import os import re -import unittest +import gtest_test_utils IS_WINDOWS = os.name == 'nt' @@ -83,7 +82,7 @@ def RunWithFlag(flag): return child.exit_code, child.output -class GTestHelpTest(unittest.TestCase): +class GTestHelpTest(gtest_test_utils.TestCase): """Tests the --help flag and its equivalent forms.""" def TestHelpFlag(self, flag): diff --git a/test/gtest_list_tests_unittest.py b/test/gtest_list_tests_unittest.py index 3d006dfe..7dca0b87 100755 --- a/test/gtest_list_tests_unittest.py +++ b/test/gtest_list_tests_unittest.py @@ -39,11 +39,8 @@ Google Test) the command line flags. __author__ = 'phanna@google.com (Patrick Hanna)' -import gtest_test_utils import os -import re -import sys -import unittest +import gtest_test_utils # Constants. @@ -105,7 +102,7 @@ def Run(command): # The unit test. -class GTestListTestsUnitTest(unittest.TestCase): +class GTestListTestsUnitTest(gtest_test_utils.TestCase): """Tests using the --gtest_list_tests flag to list all tests.""" def RunAndVerify(self, flag_value, expected_output, other_flag): diff --git a/test/gtest_nc_test.py b/test/gtest_nc_test.py index 6e77d708..06ffb3f8 100755 --- a/test/gtest_nc_test.py +++ b/test/gtest_nc_test.py @@ -38,6 +38,11 @@ import sys import unittest +IS_LINUX = os.name == 'posix' and os.uname()[0] == 'Linux' +if not IS_LINUX: + sys.exit(0) # Negative compilation tests are not supported on Windows & Mac. + + class GTestNCTest(unittest.TestCase): """Negative compilation test for Google Test.""" diff --git a/test/gtest_output_test.py b/test/gtest_output_test.py index 2751fa66..91cf9153 100755 --- a/test/gtest_output_test.py +++ b/test/gtest_output_test.py @@ -44,7 +44,6 @@ import os import re import string import sys -import unittest import gtest_test_utils @@ -84,11 +83,11 @@ def ToUnixLineEnding(s): return s.replace('\r\n', '\n').replace('\r', '\n') -def RemoveLocations(output): +def RemoveLocations(test_output): """Removes all file location info from a Google Test program's output. Args: - output: the output of a Google Test program. + test_output: the output of a Google Test program. Returns: output with all file location info (in the form of @@ -97,10 +96,10 @@ def RemoveLocations(output): 'FILE_NAME:#: '. """ - return re.sub(r'.*[/\\](.+)(\:\d+|\(\d+\))\: ', r'\1:#: ', output) + return re.sub(r'.*[/\\](.+)(\:\d+|\(\d+\))\: ', r'\1:#: ', test_output) -def RemoveStackTraces(output): +def RemoveStackTraceDetails(output): """Removes all stack traces from a Google Test program's output.""" # *? means "find the shortest string that matches". @@ -108,6 +107,13 @@ def RemoveStackTraces(output): 'Stack trace: (omitted)\n\n', output) +def RemoveStackTraces(output): + """Removes all traces of stack traces from a Google Test program's output.""" + + # *? means "find the shortest string that matches". + return re.sub(r'Stack trace:(.|\n)*?\n\n', '', output) + + def RemoveTime(output): """Removes all time information from a Google Test program's output.""" @@ -123,25 +129,28 @@ def RemoveTestCounts(output): '? FAILED TESTS', output) output = re.sub(r'\d+ tests from \d+ test cases', '? tests from ? test cases', output) + output = re.sub(r'\d+ tests from ([a-zA-Z_])', + r'? tests from \1', output) return re.sub(r'\d+ tests\.', '? tests.', output) def RemoveMatchingTests(test_output, pattern): - """Removes typed test information from a Google Test program's output. + """Removes output of specified tests from a Google Test program's output. - This function strips not only the beginning and the end of a test but also all - output in between. + This function strips not only the beginning and the end of a test but also + all output in between. Args: test_output: A string containing the test output. - pattern: A string that matches names of test cases to remove. + pattern: A regex string that matches names of test cases or + tests to remove. Returns: - Contents of test_output with removed test case whose names match pattern. + Contents of test_output with tests whose names match pattern removed. """ test_output = re.sub( - r'\[ RUN \] .*%s(.|\n)*?\[( FAILED | OK )\] .*%s.*\n' % ( + r'.*\[ RUN \] .*%s(.|\n)*?\[( FAILED | OK )\] .*%s.*\n' % ( pattern, pattern), '', test_output) @@ -153,7 +162,7 @@ def NormalizeOutput(output): output = ToUnixLineEnding(output) output = RemoveLocations(output) - output = RemoveStackTraces(output) + output = RemoveStackTraceDetails(output) output = RemoveTime(output) return output @@ -241,14 +250,28 @@ def GetOutputOfAllCommands(): test_list = GetShellCommandOutput(COMMAND_LIST_TESTS, '') SUPPORTS_DEATH_TESTS = 'DeathTest' in test_list SUPPORTS_TYPED_TESTS = 'TypedTest' in test_list +SUPPORTS_THREADS = 'ExpectFailureWithThreadsTest' in test_list +SUPPORTS_STACK_TRACES = False + +CAN_GENERATE_GOLDEN_FILE = SUPPORTS_DEATH_TESTS and SUPPORTS_TYPED_TESTS -class GTestOutputTest(unittest.TestCase): +class GTestOutputTest(gtest_test_utils.TestCase): def RemoveUnsupportedTests(self, test_output): if not SUPPORTS_DEATH_TESTS: test_output = RemoveMatchingTests(test_output, 'DeathTest') if not SUPPORTS_TYPED_TESTS: test_output = RemoveMatchingTests(test_output, 'TypedTest') + if not SUPPORTS_THREADS: + test_output = RemoveMatchingTests(test_output, + 'ExpectFailureWithThreadsTest') + test_output = RemoveMatchingTests(test_output, + 'ScopedFakeTestPartResultReporterTest') + test_output = RemoveMatchingTests(test_output, + 'WorksConcurrently') + if not SUPPORTS_STACK_TRACES: + test_output = RemoveStackTraces(test_output) + return test_output def testOutput(self): @@ -262,26 +285,48 @@ class GTestOutputTest(unittest.TestCase): golden = ToUnixLineEnding(golden_file.read()) golden_file.close() - # We want the test to pass regardless of death tests being + # We want the test to pass regardless of certain features being # supported or not. - if SUPPORTS_DEATH_TESTS and SUPPORTS_TYPED_TESTS: + if CAN_GENERATE_GOLDEN_FILE: self.assert_(golden == output) else: - self.assert_(RemoveTestCounts(self.RemoveUnsupportedTests(golden)) == - RemoveTestCounts(output)) + normalized_actual = RemoveTestCounts(output) + normalized_golden = RemoveTestCounts(self.RemoveUnsupportedTests(golden)) + + # This code is very handy when debugging test differences so I left it + # here, commented. + # open(os.path.join( + # gtest_test_utils.GetSourceDir(), + # '_gtest_output_test_normalized_actual.txt'), 'wb').write( + # normalized_actual) + # open(os.path.join( + # gtest_test_utils.GetSourceDir(), + # '_gtest_output_test_normalized_golden.txt'), 'wb').write( + # normalized_golden) + + self.assert_(normalized_golden == normalized_actual) if __name__ == '__main__': if sys.argv[1:] == [GENGOLDEN_FLAG]: - if SUPPORTS_DEATH_TESTS and SUPPORTS_TYPED_TESTS: + if CAN_GENERATE_GOLDEN_FILE: output = GetOutputOfAllCommands() golden_file = open(GOLDEN_PATH, 'wb') golden_file.write(output) golden_file.close() else: - print >> sys.stderr, ('Unable to write a golden file when compiled in an ' - 'environment that does not support death tests and ' - 'typed tests. Are you using VC 7.1?') + message = ( + """Unable to write a golden file when compiled in an environment +that does not support all the required features (death tests""") + if IS_WINDOWS: + message += ( + """\nand typed tests). Please check that you are using VC++ 8.0 SP1 +or higher as your compiler.""") + else: + message += """\nand typed tests). Please generate the golden file +using a binary built with those features enabled.""" + + sys.stderr.write(message) sys.exit(1) else: gtest_test_utils.Main() diff --git a/test/gtest_test_utils.py b/test/gtest_test_utils.py index 7dc8c421..45b25cd6 100755 --- a/test/gtest_test_utils.py +++ b/test/gtest_test_utils.py @@ -33,19 +33,32 @@ __author__ = 'wan@google.com (Zhanyong Wan)' +import atexit import os +import shutil import sys +import tempfile import unittest +_test_module = unittest +# Suppresses the 'Import not at the top of the file' lint complaint. +# pylint: disable-msg=C6204 try: import subprocess _SUBPROCESS_MODULE_AVAILABLE = True except: import popen2 _SUBPROCESS_MODULE_AVAILABLE = False +# pylint: enable-msg=C6204 + IS_WINDOWS = os.name == 'nt' +# Here we expose a class from a particular module, depending on the +# environment. The comment suppresses the 'Invalid variable name' lint +# complaint. +TestCase = _test_module.TestCase # pylint: disable-msg=C6409 + # Initially maps a flag to its default value. After # _ParseAndStripGTestFlags() is called, maps a flag to its actual value. _flag_map = {'gtest_source_dir': os.path.dirname(sys.argv[0]), @@ -56,7 +69,9 @@ _gtest_flags_are_parsed = False def _ParseAndStripGTestFlags(argv): """Parses and strips Google Test flags from argv. This is idempotent.""" - global _gtest_flags_are_parsed + # Suppresses the lint complaint about a global variable since we need it + # here to maintain module-wide state. + global _gtest_flags_are_parsed # pylint: disable-msg=W0603 if _gtest_flags_are_parsed: return @@ -103,6 +118,24 @@ def GetBuildDir(): return os.path.abspath(GetFlag('gtest_build_dir')) +_temp_dir = None + +def _RemoveTempDir(): + if _temp_dir: + shutil.rmtree(_temp_dir, ignore_errors=True) + +atexit.register(_RemoveTempDir) + + +def GetTempDir(): + """Returns a directory for temporary files.""" + + global _temp_dir + if not _temp_dir: + _temp_dir = tempfile.mkdtemp() + return _temp_dir + + def GetTestExecutablePath(executable_name): """Returns the absolute path of the test binary given its name. @@ -223,4 +256,4 @@ def Main(): # unittest.main(). Otherwise the latter will be confused by the # --gtest_* flags. _ParseAndStripGTestFlags(sys.argv) - unittest.main() + _test_module.main() diff --git a/test/gtest_throw_on_failure_test.py b/test/gtest_throw_on_failure_test.py index e952da5a..5678ffea 100755 --- a/test/gtest_throw_on_failure_test.py +++ b/test/gtest_throw_on_failure_test.py @@ -37,9 +37,8 @@ Google Test) with different environments and command line flags. __author__ = 'wan@google.com (Zhanyong Wan)' -import gtest_test_utils import os -import unittest +import gtest_test_utils # Constants. @@ -78,7 +77,7 @@ def Run(command): # The tests. TODO(wan@google.com): refactor the class to share common # logic with code in gtest_break_on_failure_unittest.py. -class ThrowOnFailureTest(unittest.TestCase): +class ThrowOnFailureTest(gtest_test_utils.TestCase): """Tests the throw-on-failure mode.""" def RunAndVerify(self, env_var_value, flag_value, should_fail): diff --git a/test/gtest_uninitialized_test.py b/test/gtest_uninitialized_test.py index 19b92e90..6ae57eee 100755 --- a/test/gtest_uninitialized_test.py +++ b/test/gtest_uninitialized_test.py @@ -34,8 +34,6 @@ __author__ = 'wan@google.com (Zhanyong Wan)' import gtest_test_utils -import sys -import unittest COMMAND = gtest_test_utils.GetTestExecutablePath('gtest_uninitialized_test_') @@ -63,7 +61,7 @@ def TestExitCodeAndOutput(command): Assert('InitGoogleTest' in p.output) -class GTestUninitializedTest(unittest.TestCase): +class GTestUninitializedTest(gtest_test_utils.TestCase): def testExitCodeAndOutput(self): TestExitCodeAndOutput(COMMAND) diff --git a/test/gtest_xml_outfiles_test.py b/test/gtest_xml_outfiles_test.py index 9d627932..0fe947f0 100755 --- a/test/gtest_xml_outfiles_test.py +++ b/test/gtest_xml_outfiles_test.py @@ -33,17 +33,14 @@ __author__ = "keith.ray@gmail.com (Keith Ray)" -import gtest_test_utils import os -import sys -import tempfile -import unittest - from xml.dom import minidom, Node +import gtest_test_utils import gtest_xml_test_utils +GTEST_OUTPUT_SUBDIR = "xml_outfiles" GTEST_OUTPUT_1_TEST = "gtest_xml_outfile1_test_" GTEST_OUTPUT_2_TEST = "gtest_xml_outfile2_test_" @@ -71,7 +68,8 @@ class GTestXMLOutFilesTest(gtest_xml_test_utils.GTestXMLTestCase): # We want the trailing '/' that the last "" provides in os.path.join, for # telling Google Test to create an output directory instead of a single file # for xml output. - self.output_dir_ = os.path.join(tempfile.mkdtemp(), "") + self.output_dir_ = os.path.join(gtest_test_utils.GetTempDir(), + GTEST_OUTPUT_SUBDIR, "") self.DeleteFilesAndDir() def tearDown(self): @@ -87,7 +85,7 @@ class GTestXMLOutFilesTest(gtest_xml_test_utils.GTestXMLTestCase): except os.error: pass try: - os.removedirs(self.output_dir_) + os.rmdir(self.output_dir_) except os.error: pass @@ -100,7 +98,8 @@ class GTestXMLOutFilesTest(gtest_xml_test_utils.GTestXMLTestCase): def _TestOutFile(self, test_name, expected_xml): gtest_prog_path = gtest_test_utils.GetTestExecutablePath(test_name) command = [gtest_prog_path, "--gtest_output=xml:%s" % self.output_dir_] - p = gtest_test_utils.Subprocess(command, working_dir=tempfile.mkdtemp()) + p = gtest_test_utils.Subprocess(command, + working_dir=gtest_test_utils.GetTempDir()) self.assert_(p.exited) self.assertEquals(0, p.exit_code) diff --git a/test/gtest_xml_output_unittest.py b/test/gtest_xml_output_unittest.py index 622251ea..a0cd4d09 100755 --- a/test/gtest_xml_output_unittest.py +++ b/test/gtest_xml_output_unittest.py @@ -34,19 +34,24 @@ __author__ = 'eefacm@gmail.com (Sean Mcafee)' import errno -import gtest_test_utils import os import sys -import tempfile -import unittest - from xml.dom import minidom, Node +import gtest_test_utils import gtest_xml_test_utils + GTEST_OUTPUT_FLAG = "--gtest_output" GTEST_DEFAULT_OUTPUT_FILE = "test_detail.xml" +SUPPORTS_STACK_TRACES = False + +if SUPPORTS_STACK_TRACES: + STACK_TRACE_TEMPLATE = "\nStack trace:\n*" +else: + STACK_TRACE_TEMPLATE = "" + EXPECTED_NON_EMPTY_XML = """ @@ -56,7 +61,7 @@ EXPECTED_NON_EMPTY_XML = """ +Expected: 1%(stack)s]]> @@ -64,10 +69,10 @@ Expected: 1]]> +Expected: 1%(stack)s]]> +Expected: 2%(stack)s]]> @@ -85,7 +90,7 @@ Expected: 2]]> -""" +""" % {'stack': STACK_TRACE_TEMPLATE} EXPECTED_EMPTY_XML = """ @@ -120,8 +125,8 @@ class GTestXMLOutputUnitTest(gtest_xml_test_utils.GTestXMLTestCase): Confirms that Google Test produces an XML output file with the expected default name if no name is explicitly specified. """ - temp_dir = tempfile.mkdtemp() - output_file = os.path.join(temp_dir, GTEST_DEFAULT_OUTPUT_FILE) + output_file = os.path.join(gtest_test_utils.GetTempDir(), + GTEST_DEFAULT_OUTPUT_FILE) gtest_prog_path = gtest_test_utils.GetTestExecutablePath( "gtest_no_test_unittest") try: @@ -132,7 +137,7 @@ class GTestXMLOutputUnitTest(gtest_xml_test_utils.GTestXMLTestCase): p = gtest_test_utils.Subprocess( [gtest_prog_path, "%s=xml" % GTEST_OUTPUT_FLAG], - working_dir=temp_dir) + working_dir=gtest_test_utils.GetTempDir()) self.assert_(p.exited) self.assertEquals(0, p.exit_code) self.assert_(os.path.isfile(output_file)) @@ -145,8 +150,8 @@ class GTestXMLOutputUnitTest(gtest_xml_test_utils.GTestXMLTestCase): XML document. Furthermore, the program's exit code must be expected_exit_code. """ - - xml_path = os.path.join(tempfile.mkdtemp(), gtest_prog_name + "out.xml") + xml_path = os.path.join(gtest_test_utils.GetTempDir(), + gtest_prog_name + "out.xml") gtest_prog_path = gtest_test_utils.GetTestExecutablePath(gtest_prog_name) command = [gtest_prog_path, "%s=xml:%s" % (GTEST_OUTPUT_FLAG, xml_path)] diff --git a/test/gtest_xml_test_utils.py b/test/gtest_xml_test_utils.py index 64eebb10..1811c408 100755 --- a/test/gtest_xml_test_utils.py +++ b/test/gtest_xml_test_utils.py @@ -34,14 +34,15 @@ __author__ = 'eefacm@gmail.com (Sean Mcafee)' import re -import unittest - from xml.dom import minidom, Node +import gtest_test_utils + + GTEST_OUTPUT_FLAG = "--gtest_output" GTEST_DEFAULT_OUTPUT_FILE = "test_detail.xml" -class GTestXMLTestCase(unittest.TestCase): +class GTestXMLTestCase(gtest_test_utils.TestCase): """ Base class for tests of Google Test's XML output functionality. """ diff --git a/test/run_tests_test.py b/test/run_tests_test.py index 1d9f3b77..b55739ea 100755 --- a/test/run_tests_test.py +++ b/test/run_tests_test.py @@ -48,6 +48,8 @@ class FakePath(object): self.tree = {} self.path_separator = os.sep + # known_paths contains either absolute or relative paths. Relative paths + # are absolutized with self.current_dir. if known_paths: self._AddPaths(known_paths) @@ -91,8 +93,11 @@ class FakePath(object): return tree + def normpath(self, path): + return os.path.normpath(path) + def abspath(self, path): - return os.path.normpath(os.path.join(self.current_dir, path)) + return self.normpath(os.path.join(self.current_dir, path)) def isfile(self, path): return self.PathElement(self.abspath(path)) == 1 @@ -157,7 +162,8 @@ class GetTestsToRunTest(unittest.TestCase): 'test/gtest_color_test.py'])) self.fake_configurations = ['dbg', 'opt'] self.test_runner = run_tests.TestRunner(injected_os=self.fake_os, - injected_subprocess=None) + injected_subprocess=None, + injected_script_dir='.') def testBinaryTestsOnly(self): """Exercises GetTestsToRun with parameters designating binary tests only.""" @@ -388,7 +394,8 @@ class GetTestsToRunTest(unittest.TestCase): 'scons/build/opt/scons/gtest_nontest.exe', 'test/'])) self.test_runner = run_tests.TestRunner(injected_os=self.fake_os, - injected_subprocess=None) + injected_subprocess=None, + injected_script_dir='.') self.AssertResultsEqual( self.test_runner.GetTestsToRun( [], @@ -397,6 +404,43 @@ class GetTestsToRunTest(unittest.TestCase): available_configurations=self.fake_configurations), ([], [])) + def testWorksFromDifferentDir(self): + """Exercises GetTestsToRun from a directory different from run_test.py's.""" + + # Here we simulate an test script in directory /d/ called from the + # directory /a/b/c/. + self.fake_os = FakeOs(FakePath( + current_dir=os.path.abspath('/a/b/c'), + known_paths=['/a/b/c/', + '/d/scons/build/dbg/scons/gtest_unittest', + '/d/scons/build/opt/scons/gtest_unittest', + '/d/test/gtest_color_test.py'])) + self.fake_configurations = ['dbg', 'opt'] + self.test_runner = run_tests.TestRunner(injected_os=self.fake_os, + injected_subprocess=None, + injected_script_dir='/d/') + # A binary test. + self.AssertResultsEqual( + self.test_runner.GetTestsToRun( + ['gtest_unittest'], + '', + False, + available_configurations=self.fake_configurations), + ([], + [('/d/scons/build/dbg/scons', + '/d/scons/build/dbg/scons/gtest_unittest')])) + + # A Python test. + self.AssertResultsEqual( + self.test_runner.GetTestsToRun( + ['gtest_color_test.py'], + '', + False, + available_configurations=self.fake_configurations), + ([('/d/scons/build/dbg/scons', '/d/test/gtest_color_test.py')], + [])) + + def testNonTestBinary(self): """Exercises GetTestsToRun with a non-test parameter.""" -- cgit v1.2.3