summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorzhanyong.wan <zhanyong.wan@861a406c-534a-0410-8894-cb66d6ee9925>2009-09-30 23:46:28 (GMT)
committerzhanyong.wan <zhanyong.wan@861a406c-534a-0410-8894-cb66d6ee9925>2009-09-30 23:46:28 (GMT)
commitbd851333e89517762c91a3fef67cf25a6f1bd37a (patch)
tree9b23d718046f2e83236287dca1ad8a7c863a47e5 /test
parentf8b268ee86ca74bba3276352f1e7de53d1336c3e (diff)
downloadgoogletest-bd851333e89517762c91a3fef67cf25a6f1bd37a.zip
googletest-bd851333e89517762c91a3fef67cf25a6f1bd37a.tar.gz
googletest-bd851333e89517762c91a3fef67cf25a6f1bd37a.tar.bz2
Implements test shuffling (by Zhanyong Wan, based on Josh Kelley's original patch).
Enables death tests on minGW (by Vlad Losev).
Diffstat (limited to 'test')
-rw-r--r--test/gtest-death-test_test.cc8
-rwxr-xr-xtest/gtest_shuffle_test.py331
-rw-r--r--test/gtest_shuffle_test_.cc104
-rw-r--r--test/gtest_unittest.cc301
4 files changed, 737 insertions, 7 deletions
diff --git a/test/gtest-death-test_test.cc b/test/gtest-death-test_test.cc
index 7bf6a71..288c70a 100644
--- a/test/gtest-death-test_test.cc
+++ b/test/gtest-death-test_test.cc
@@ -659,7 +659,11 @@ static void TestExitMacros() {
EXPECT_EXIT(_exit(1), testing::ExitedWithCode(1), "");
ASSERT_EXIT(_exit(42), testing::ExitedWithCode(42), "");
-#if GTEST_OS_WINDOWS
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
+ // MinGW (as of MinGW 5.1.6 and MSYS 1.0.11) does not tag crashed
+ // processes with non-zero exit code and does not honor calls to
+ // SetErrorMode(SEM_NOGPFAULTERRORBOX) that are supposed to suppress
+ // error pop-ups.
EXPECT_EXIT({
testing::GTEST_FLAG(catch_exceptions) = false;
*static_cast<int*>(NULL) = 1;
@@ -671,7 +675,9 @@ static void TestExitMacros() {
*static_cast<int*>(NULL) = 1;
}, testing::ExitedWithCode(0), "") << "This failure is expected.";
}, "This failure is expected.");
+#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
+#if GTEST_OS_WINDOWS
// Of all signals effects on the process exit code, only those of SIGABRT
// are documented on Windows.
// See http://msdn.microsoft.com/en-us/library/dwwzkt4c(VS.71).aspx.
diff --git a/test/gtest_shuffle_test.py b/test/gtest_shuffle_test.py
new file mode 100755
index 0000000..a870a01
--- /dev/null
+++ b/test/gtest_shuffle_test.py
@@ -0,0 +1,331 @@
+#!/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.
+
+"""Verifies that test shuffling works."""
+
+__author__ = 'wan@google.com (Zhanyong Wan)'
+
+import os
+import gtest_test_utils
+
+# Command to run the gtest_shuffle_test_ program.
+COMMAND = gtest_test_utils.GetTestExecutablePath('gtest_shuffle_test_')
+
+# The environment variables for test sharding.
+TOTAL_SHARDS_ENV_VAR = 'GTEST_TOTAL_SHARDS'
+SHARD_INDEX_ENV_VAR = 'GTEST_SHARD_INDEX'
+
+TEST_FILTER = 'A*.A:A*.B:C*'
+
+ALL_TESTS = []
+ACTIVE_TESTS = []
+FILTERED_TESTS = []
+SHARDED_TESTS = []
+
+SHUFFLED_ALL_TESTS = []
+SHUFFLED_ACTIVE_TESTS = []
+SHUFFLED_FILTERED_TESTS = []
+SHUFFLED_SHARDED_TESTS = []
+
+
+def AlsoRunDisabledTestsFlag():
+ return '--gtest_also_run_disabled_tests'
+
+
+def FilterFlag(test_filter):
+ return '--gtest_filter=%s' % (test_filter,)
+
+
+def RepeatFlag(n):
+ return '--gtest_repeat=%s' % (n,)
+
+
+def ShuffleFlag():
+ return '--gtest_shuffle'
+
+
+def RandomSeedFlag(n):
+ return '--gtest_random_seed=%s' % (n,)
+
+
+def RunAndReturnOutput(extra_env, args):
+ """Runs the test program and returns its output."""
+
+ try:
+ original_env = os.environ.copy()
+ os.environ.update(extra_env)
+ return gtest_test_utils.Subprocess([COMMAND] + args).output
+ finally:
+ for key in extra_env.iterkeys():
+ if key in original_env:
+ os.environ[key] = original_env[key]
+ else:
+ del os.environ[key]
+
+
+def GetTestsForAllIterations(extra_env, args):
+ """Runs the test program and returns a list of test lists.
+
+ Args:
+ extra_env: a map from environment variables to their values
+ args: command line flags to pass to gtest_shuffle_test_
+
+ Returns:
+ A list where the i-th element is the list of tests run in the i-th
+ test iteration.
+ """
+
+ test_iterations = []
+ for line in RunAndReturnOutput(extra_env, args).split('\n'):
+ if line.startswith('----'):
+ tests = []
+ test_iterations.append(tests)
+ elif line.strip():
+ tests.append(line.strip()) # 'TestCaseName.TestName'
+
+ return test_iterations
+
+
+def GetTestCases(tests):
+ """Returns a list of test cases in the given full test names.
+
+ Args:
+ tests: a list of full test names
+
+ Returns:
+ A list of test cases from 'tests', in their original order.
+ Consecutive duplicates are removed.
+ """
+
+ test_cases = []
+ for test in tests:
+ test_case = test.split('.')[0]
+ if not test_case in test_cases:
+ test_cases.append(test_case)
+
+ return test_cases
+
+
+def CalculateTestLists():
+ """Calculates the list of tests run under different flags."""
+
+ if not ALL_TESTS:
+ ALL_TESTS.extend(
+ GetTestsForAllIterations({}, [AlsoRunDisabledTestsFlag()])[0])
+
+ if not ACTIVE_TESTS:
+ ACTIVE_TESTS.extend(GetTestsForAllIterations({}, [])[0])
+
+ if not FILTERED_TESTS:
+ FILTERED_TESTS.extend(
+ GetTestsForAllIterations({}, [FilterFlag(TEST_FILTER)])[0])
+
+ if not SHARDED_TESTS:
+ SHARDED_TESTS.extend(
+ GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3',
+ SHARD_INDEX_ENV_VAR: '1'},
+ [])[0])
+
+ if not SHUFFLED_ALL_TESTS:
+ SHUFFLED_ALL_TESTS.extend(GetTestsForAllIterations(
+ {}, [AlsoRunDisabledTestsFlag(), ShuffleFlag(), RandomSeedFlag(1)])[0])
+
+ if not SHUFFLED_ACTIVE_TESTS:
+ SHUFFLED_ACTIVE_TESTS.extend(GetTestsForAllIterations(
+ {}, [ShuffleFlag(), RandomSeedFlag(1)])[0])
+
+ if not SHUFFLED_FILTERED_TESTS:
+ SHUFFLED_FILTERED_TESTS.extend(GetTestsForAllIterations(
+ {}, [ShuffleFlag(), RandomSeedFlag(1), FilterFlag(TEST_FILTER)])[0])
+
+ if not SHUFFLED_SHARDED_TESTS:
+ SHUFFLED_SHARDED_TESTS.extend(
+ GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3',
+ SHARD_INDEX_ENV_VAR: '1'},
+ [ShuffleFlag(), RandomSeedFlag(1)])[0])
+
+
+class GTestShuffleUnitTest(gtest_test_utils.TestCase):
+ """Tests test shuffling."""
+
+ def setUp(self):
+ CalculateTestLists()
+
+ def testShufflePreservesNumberOfTests(self):
+ self.assertEqual(len(ALL_TESTS), len(SHUFFLED_ALL_TESTS))
+ self.assertEqual(len(ACTIVE_TESTS), len(SHUFFLED_ACTIVE_TESTS))
+ self.assertEqual(len(FILTERED_TESTS), len(SHUFFLED_FILTERED_TESTS))
+ self.assertEqual(len(SHARDED_TESTS), len(SHUFFLED_SHARDED_TESTS))
+
+ def testShuffleChangesTestOrder(self):
+ self.assert_(SHUFFLED_ALL_TESTS != ALL_TESTS, SHUFFLED_ALL_TESTS)
+ self.assert_(SHUFFLED_ACTIVE_TESTS != ACTIVE_TESTS, SHUFFLED_ACTIVE_TESTS)
+ self.assert_(SHUFFLED_FILTERED_TESTS != FILTERED_TESTS,
+ SHUFFLED_FILTERED_TESTS)
+ self.assert_(SHUFFLED_SHARDED_TESTS != SHARDED_TESTS,
+ SHUFFLED_SHARDED_TESTS)
+
+ def testShuffleChangesTestCaseOrder(self):
+ self.assert_(GetTestCases(SHUFFLED_ALL_TESTS) != GetTestCases(ALL_TESTS),
+ GetTestCases(SHUFFLED_ALL_TESTS))
+ self.assert_(
+ GetTestCases(SHUFFLED_ACTIVE_TESTS) != GetTestCases(ACTIVE_TESTS),
+ GetTestCases(SHUFFLED_ACTIVE_TESTS))
+ self.assert_(
+ GetTestCases(SHUFFLED_FILTERED_TESTS) != GetTestCases(FILTERED_TESTS),
+ GetTestCases(SHUFFLED_FILTERED_TESTS))
+ self.assert_(
+ GetTestCases(SHUFFLED_SHARDED_TESTS) != GetTestCases(SHARDED_TESTS),
+ GetTestCases(SHUFFLED_SHARDED_TESTS))
+
+ def testShuffleDoesNotRepeatTest(self):
+ for test in SHUFFLED_ALL_TESTS:
+ self.assertEqual(1, SHUFFLED_ALL_TESTS.count(test),
+ '%s appears more than once' % (test,))
+ for test in SHUFFLED_ACTIVE_TESTS:
+ self.assertEqual(1, SHUFFLED_ACTIVE_TESTS.count(test),
+ '%s appears more than once' % (test,))
+ for test in SHUFFLED_FILTERED_TESTS:
+ self.assertEqual(1, SHUFFLED_FILTERED_TESTS.count(test),
+ '%s appears more than once' % (test,))
+ for test in SHUFFLED_SHARDED_TESTS:
+ self.assertEqual(1, SHUFFLED_SHARDED_TESTS.count(test),
+ '%s appears more than once' % (test,))
+
+ def testShuffleDoesNotCreateNewTest(self):
+ for test in SHUFFLED_ALL_TESTS:
+ self.assert_(test in ALL_TESTS, '%s is an invalid test' % (test,))
+ for test in SHUFFLED_ACTIVE_TESTS:
+ self.assert_(test in ACTIVE_TESTS, '%s is an invalid test' % (test,))
+ for test in SHUFFLED_FILTERED_TESTS:
+ self.assert_(test in FILTERED_TESTS, '%s is an invalid test' % (test,))
+ for test in SHUFFLED_SHARDED_TESTS:
+ self.assert_(test in SHARDED_TESTS, '%s is an invalid test' % (test,))
+
+ def testShuffleIncludesAllTests(self):
+ for test in ALL_TESTS:
+ self.assert_(test in SHUFFLED_ALL_TESTS, '%s is missing' % (test,))
+ for test in ACTIVE_TESTS:
+ self.assert_(test in SHUFFLED_ACTIVE_TESTS, '%s is missing' % (test,))
+ for test in FILTERED_TESTS:
+ self.assert_(test in SHUFFLED_FILTERED_TESTS, '%s is missing' % (test,))
+ for test in SHARDED_TESTS:
+ self.assert_(test in SHUFFLED_SHARDED_TESTS, '%s is missing' % (test,))
+
+ def testShuffleLeavesDeathTestsAtFront(self):
+ non_death_test_found = False
+ for test in SHUFFLED_ACTIVE_TESTS:
+ if 'DeathTest.' in test:
+ self.assert_(not non_death_test_found,
+ '%s appears after a non-death test' % (test,))
+ else:
+ non_death_test_found = True
+
+ def _VerifyTestCasesDoNotInterleave(self, tests):
+ test_cases = []
+ for test in tests:
+ [test_case, _] = test.split('.')
+ if test_cases and test_cases[-1] != test_case:
+ test_cases.append(test_case)
+ self.assertEqual(1, test_cases.count(test_case),
+ 'Test case %s is not grouped together in %s' %
+ (test_case, tests))
+
+ def testShuffleDoesNotInterleaveTestCases(self):
+ self._VerifyTestCasesDoNotInterleave(SHUFFLED_ALL_TESTS)
+ self._VerifyTestCasesDoNotInterleave(SHUFFLED_ACTIVE_TESTS)
+ self._VerifyTestCasesDoNotInterleave(SHUFFLED_FILTERED_TESTS)
+ self._VerifyTestCasesDoNotInterleave(SHUFFLED_SHARDED_TESTS)
+
+ def testShuffleRestoresOrderAfterEachIteration(self):
+ # Get the test lists in all 3 iterations, using random seed 1, 2,
+ # and 3 respectively. Google Test picks a different seed in each
+ # iteration, and this test depends on the current implementation
+ # picking successive numbers. This dependency is not ideal, but
+ # makes the test much easier to write.
+ [tests_in_iteration1, tests_in_iteration2, tests_in_iteration3] = (
+ GetTestsForAllIterations(
+ {}, [ShuffleFlag(), RandomSeedFlag(1), RepeatFlag(3)]))
+
+ # Make sure running the tests with random seed 1 gets the same
+ # order as in iteration 1 above.
+ [tests_with_seed1] = GetTestsForAllIterations(
+ {}, [ShuffleFlag(), RandomSeedFlag(1)])
+ self.assertEqual(tests_in_iteration1, tests_with_seed1)
+
+ # Make sure running the tests with random seed 2 gets the same
+ # order as in iteration 2 above. Success means that Google Test
+ # correctly restores the test order before re-shuffling at the
+ # beginning of iteration 2.
+ [tests_with_seed2] = GetTestsForAllIterations(
+ {}, [ShuffleFlag(), RandomSeedFlag(2)])
+ self.assertEqual(tests_in_iteration2, tests_with_seed2)
+
+ # Make sure running the tests with random seed 3 gets the same
+ # order as in iteration 3 above. Success means that Google Test
+ # correctly restores the test order before re-shuffling at the
+ # beginning of iteration 3.
+ [tests_with_seed3] = GetTestsForAllIterations(
+ {}, [ShuffleFlag(), RandomSeedFlag(3)])
+ self.assertEqual(tests_in_iteration3, tests_with_seed3)
+
+ def testShuffleGeneratesNewOrderInEachIteration(self):
+ [tests_in_iteration1, tests_in_iteration2, tests_in_iteration3] = (
+ GetTestsForAllIterations(
+ {}, [ShuffleFlag(), RandomSeedFlag(1), RepeatFlag(3)]))
+
+ self.assert_(tests_in_iteration1 != tests_in_iteration2,
+ tests_in_iteration1)
+ self.assert_(tests_in_iteration1 != tests_in_iteration3,
+ tests_in_iteration1)
+ self.assert_(tests_in_iteration2 != tests_in_iteration3,
+ tests_in_iteration2)
+
+ def testShuffleShardedTestsPreservesPartition(self):
+ # If we run M tests on N shards, the same M tests should be run in
+ # total, regardless of the random seeds used by the shards.
+ [tests1] = GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3',
+ SHARD_INDEX_ENV_VAR: '0'},
+ [ShuffleFlag(), RandomSeedFlag(1)])
+ [tests2] = GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3',
+ SHARD_INDEX_ENV_VAR: '1'},
+ [ShuffleFlag(), RandomSeedFlag(20)])
+ [tests3] = GetTestsForAllIterations({TOTAL_SHARDS_ENV_VAR: '3',
+ SHARD_INDEX_ENV_VAR: '2'},
+ [ShuffleFlag(), RandomSeedFlag(25)])
+ sorted_sharded_tests = tests1 + tests2 + tests3
+ sorted_sharded_tests.sort()
+ sorted_active_tests = []
+ sorted_active_tests.extend(ACTIVE_TESTS)
+ sorted_active_tests.sort()
+ self.assertEqual(sorted_active_tests, sorted_sharded_tests)
+
+if __name__ == '__main__':
+ gtest_test_utils.Main()
diff --git a/test/gtest_shuffle_test_.cc b/test/gtest_shuffle_test_.cc
new file mode 100644
index 0000000..53ecf77
--- /dev/null
+++ b/test/gtest_shuffle_test_.cc
@@ -0,0 +1,104 @@
+// 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Verifies that test shuffling works.
+
+#include <gtest/gtest.h>
+
+namespace {
+
+using ::testing::EmptyTestEventListener;
+using ::testing::InitGoogleTest;
+using ::testing::Message;
+using ::testing::Test;
+using ::testing::TestEventListeners;
+using ::testing::TestInfo;
+using ::testing::UnitTest;
+using ::testing::internal::String;
+using ::testing::internal::scoped_ptr;
+
+// The test methods are empty, as the sole purpose of this program is
+// to print the test names before/after shuffling.
+
+class A : public Test {};
+TEST_F(A, A) {}
+TEST_F(A, B) {}
+
+TEST(ADeathTest, A) {}
+TEST(ADeathTest, B) {}
+TEST(ADeathTest, C) {}
+
+TEST(B, A) {}
+TEST(B, B) {}
+TEST(B, C) {}
+TEST(B, DISABLED_D) {}
+TEST(B, DISABLED_E) {}
+
+TEST(BDeathTest, A) {}
+TEST(BDeathTest, B) {}
+
+TEST(C, A) {}
+TEST(C, B) {}
+TEST(C, C) {}
+TEST(C, DISABLED_D) {}
+
+TEST(CDeathTest, A) {}
+
+TEST(DISABLED_D, A) {}
+TEST(DISABLED_D, DISABLED_B) {}
+
+// This printer prints the full test names only, starting each test
+// iteration with a "----" marker.
+class TestNamePrinter : public EmptyTestEventListener {
+ public:
+ virtual void OnTestIterationStart(const UnitTest& /* unit_test */,
+ int /* iteration */) {
+ printf("----\n");
+ }
+
+ virtual void OnTestStart(const TestInfo& test_info) {
+ printf("%s.%s\n", test_info.test_case_name(), test_info.name());
+ }
+};
+
+} // namespace
+
+int main(int argc, char **argv) {
+ InitGoogleTest(&argc, argv);
+
+ // Replaces the default printer with TestNamePrinter, which prints
+ // the test name only.
+ TestEventListeners& listeners = UnitTest::GetInstance()->listeners();
+ delete listeners.Release(listeners.default_result_printer());
+ listeners.Append(new TestNamePrinter);
+
+ return RUN_ALL_TESTS();
+}
diff --git a/test/gtest_unittest.cc b/test/gtest_unittest.cc
index ba39ae6..5c69b46 100644
--- a/test/gtest_unittest.cc
+++ b/test/gtest_unittest.cc
@@ -180,6 +180,19 @@ using testing::internal::kMaxRandomSeed;
using testing::internal::kTestTypeIdInGoogleTest;
using testing::internal::scoped_ptr;
+class TestingVector : public Vector<int> {
+};
+
+::std::ostream& operator<<(::std::ostream& os,
+ const TestingVector& vector) {
+ os << "{ ";
+ for (int i = 0; i < vector.size(); i++) {
+ os << vector.GetElement(i) << " ";
+ }
+ os << "}";
+ return os;
+}
+
// This line tests that we can define tests in an unnamed namespace.
namespace {
@@ -677,6 +690,53 @@ TEST(VectorTest, GetElementOr) {
EXPECT_EQ('x', a.GetElementOr(2, 'x'));
}
+TEST(VectorTest, Swap) {
+ Vector<int> a;
+ a.PushBack(0);
+ a.PushBack(1);
+ a.PushBack(2);
+
+ // Swaps an element with itself.
+ a.Swap(0, 0);
+ ASSERT_EQ(0, a.GetElement(0));
+ ASSERT_EQ(1, a.GetElement(1));
+ ASSERT_EQ(2, a.GetElement(2));
+
+ // Swaps two different elements where the indices go up.
+ a.Swap(0, 1);
+ ASSERT_EQ(1, a.GetElement(0));
+ ASSERT_EQ(0, a.GetElement(1));
+ ASSERT_EQ(2, a.GetElement(2));
+
+ // Swaps two different elements where the indices go down.
+ a.Swap(2, 0);
+ ASSERT_EQ(2, a.GetElement(0));
+ ASSERT_EQ(0, a.GetElement(1));
+ ASSERT_EQ(1, a.GetElement(2));
+}
+
+TEST(VectorTest, Clone) {
+ // Clones an empty Vector.
+ Vector<int> a;
+ scoped_ptr<Vector<int> > empty(a.Clone());
+ EXPECT_EQ(0, empty->size());
+
+ // Clones a singleton.
+ a.PushBack(42);
+ scoped_ptr<Vector<int> > singleton(a.Clone());
+ ASSERT_EQ(1, singleton->size());
+ EXPECT_EQ(42, singleton->GetElement(0));
+
+ // Clones a Vector with more elements.
+ a.PushBack(43);
+ a.PushBack(44);
+ scoped_ptr<Vector<int> > big(a.Clone());
+ ASSERT_EQ(3, big->size());
+ EXPECT_EQ(42, big->GetElement(0));
+ EXPECT_EQ(43, big->GetElement(1));
+ EXPECT_EQ(44, big->GetElement(2));
+}
+
// Tests Vector::Erase().
TEST(VectorDeathTest, Erase) {
Vector<int> a;
@@ -740,23 +800,252 @@ TEST(VectorDeathTest, Erase) {
}
// Tests the GetElement accessor.
-TEST(ListDeathTest, GetElement) {
+TEST(VectorDeathTest, GetElement) {
Vector<int> a;
a.PushBack(0);
a.PushBack(1);
a.PushBack(2);
+ const Vector<int>& b = a;
+
+ EXPECT_EQ(0, b.GetElement(0));
+ EXPECT_EQ(1, b.GetElement(1));
+ EXPECT_EQ(2, b.GetElement(2));
+ EXPECT_DEATH_IF_SUPPORTED(
+ b.GetElement(3),
+ "Invalid Vector index 3: must be in range \\[0, 2\\]\\.");
+ EXPECT_DEATH_IF_SUPPORTED(
+ b.GetElement(-1),
+ "Invalid Vector index -1: must be in range \\[0, 2\\]\\.");
+}
+
+// Tests the GetMutableElement accessor.
+TEST(VectorDeathTest, GetMutableElement) {
+ Vector<int> a;
+ a.PushBack(0);
+ a.PushBack(1);
+ a.PushBack(2);
+
+ EXPECT_EQ(0, a.GetMutableElement(0));
+ EXPECT_EQ(1, a.GetMutableElement(1));
+ EXPECT_EQ(2, a.GetMutableElement(2));
+
+ a.GetMutableElement(0) = 42;
+ EXPECT_EQ(42, a.GetMutableElement(0));
+ EXPECT_EQ(1, a.GetMutableElement(1));
+ EXPECT_EQ(2, a.GetMutableElement(2));
- EXPECT_EQ(0, a.GetElement(0));
- EXPECT_EQ(1, a.GetElement(1));
- EXPECT_EQ(2, a.GetElement(2));
EXPECT_DEATH_IF_SUPPORTED(
- a.GetElement(3),
+ a.GetMutableElement(3),
"Invalid Vector index 3: must be in range \\[0, 2\\]\\.");
EXPECT_DEATH_IF_SUPPORTED(
- a.GetElement(-1),
+ a.GetMutableElement(-1),
"Invalid Vector index -1: must be in range \\[0, 2\\]\\.");
}
+TEST(VectorDeathTest, Swap) {
+ Vector<int> a;
+ a.PushBack(0);
+ a.PushBack(1);
+ a.PushBack(2);
+
+ EXPECT_DEATH_IF_SUPPORTED(
+ a.Swap(-1, 1),
+ "Invalid first swap element -1: must be in range \\[0, 2\\]");
+ EXPECT_DEATH_IF_SUPPORTED(
+ a.Swap(3, 1),
+ "Invalid first swap element 3: must be in range \\[0, 2\\]");
+ EXPECT_DEATH_IF_SUPPORTED(
+ a.Swap(1, -1),
+ "Invalid second swap element -1: must be in range \\[0, 2\\]");
+ EXPECT_DEATH_IF_SUPPORTED(
+ a.Swap(1, 3),
+ "Invalid second swap element 3: must be in range \\[0, 2\\]");
+}
+
+TEST(VectorDeathTest, ShuffleRange) {
+ Vector<int> a;
+ a.PushBack(0);
+ a.PushBack(1);
+ a.PushBack(2);
+ testing::internal::Random random(1);
+
+ EXPECT_DEATH_IF_SUPPORTED(
+ a.ShuffleRange(&random, -1, 1),
+ "Invalid shuffle range start -1: must be in range \\[0, 3\\]");
+ EXPECT_DEATH_IF_SUPPORTED(
+ a.ShuffleRange(&random, 4, 4),
+ "Invalid shuffle range start 4: must be in range \\[0, 3\\]");
+ EXPECT_DEATH_IF_SUPPORTED(
+ a.ShuffleRange(&random, 3, 2),
+ "Invalid shuffle range finish 2: must be in range \\[3, 3\\]");
+ EXPECT_DEATH_IF_SUPPORTED(
+ a.ShuffleRange(&random, 3, 4),
+ "Invalid shuffle range finish 4: must be in range \\[3, 3\\]");
+}
+
+class VectorShuffleTest : public Test {
+ protected:
+ static const int kVectorSize = 20;
+
+ VectorShuffleTest() : random_(1) {
+ for (int i = 0; i < kVectorSize; i++) {
+ vector_.PushBack(i);
+ }
+ }
+
+ static bool VectorIsCorrupt(const TestingVector& vector) {
+ if (kVectorSize != vector.size()) {
+ return true;
+ }
+
+ bool found_in_vector[kVectorSize] = { false };
+ for (int i = 0; i < vector.size(); i++) {
+ const int e = vector.GetElement(i);
+ if (e < 0 || e >= kVectorSize || found_in_vector[e]) {
+ return true;
+ }
+ found_in_vector[e] = true;
+ }
+
+ // Vector size is correct, elements' range is correct, no
+ // duplicate elements. Therefore no corruption has occurred.
+ return false;
+ }
+
+ static bool VectorIsNotCorrupt(const TestingVector& vector) {
+ return !VectorIsCorrupt(vector);
+ }
+
+ static bool RangeIsShuffled(const TestingVector& vector, int begin, int end) {
+ for (int i = begin; i < end; i++) {
+ if (i != vector.GetElement(i)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static bool RangeIsUnshuffled(
+ const TestingVector& vector, int begin, int end) {
+ return !RangeIsShuffled(vector, begin, end);
+ }
+
+ static bool VectorIsShuffled(const TestingVector& vector) {
+ return RangeIsShuffled(vector, 0, vector.size());
+ }
+
+ static bool VectorIsUnshuffled(const TestingVector& vector) {
+ return !VectorIsShuffled(vector);
+ }
+
+ testing::internal::Random random_;
+ TestingVector vector_;
+}; // class VectorShuffleTest
+
+const int VectorShuffleTest::kVectorSize;
+
+TEST_F(VectorShuffleTest, HandlesEmptyRange) {
+ // Tests an empty range at the beginning...
+ vector_.ShuffleRange(&random_, 0, 0);
+ ASSERT_PRED1(VectorIsNotCorrupt, vector_);
+ ASSERT_PRED1(VectorIsUnshuffled, vector_);
+
+ // ...in the middle...
+ vector_.ShuffleRange(&random_, kVectorSize/2, kVectorSize/2);
+ ASSERT_PRED1(VectorIsNotCorrupt, vector_);
+ ASSERT_PRED1(VectorIsUnshuffled, vector_);
+
+ // ...at the end...
+ vector_.ShuffleRange(&random_, kVectorSize - 1, kVectorSize - 1);
+ ASSERT_PRED1(VectorIsNotCorrupt, vector_);
+ ASSERT_PRED1(VectorIsUnshuffled, vector_);
+
+ // ...and past the end.
+ vector_.ShuffleRange(&random_, kVectorSize, kVectorSize);
+ ASSERT_PRED1(VectorIsNotCorrupt, vector_);
+ ASSERT_PRED1(VectorIsUnshuffled, vector_);
+}
+
+TEST_F(VectorShuffleTest, HandlesRangeOfSizeOne) {
+ // Tests a size one range at the beginning...
+ vector_.ShuffleRange(&random_, 0, 1);
+ ASSERT_PRED1(VectorIsNotCorrupt, vector_);
+ ASSERT_PRED1(VectorIsUnshuffled, vector_);
+
+ // ...in the middle...
+ vector_.ShuffleRange(&random_, kVectorSize/2, kVectorSize/2 + 1);
+ ASSERT_PRED1(VectorIsNotCorrupt, vector_);
+ ASSERT_PRED1(VectorIsUnshuffled, vector_);
+
+ // ...and at the end.
+ vector_.ShuffleRange(&random_, kVectorSize - 1, kVectorSize);
+ ASSERT_PRED1(VectorIsNotCorrupt, vector_);
+ ASSERT_PRED1(VectorIsUnshuffled, vector_);
+}
+
+// Because we use our own random number generator and a fixed seed,
+// we can guarantee that the following "random" tests will succeed.
+
+TEST_F(VectorShuffleTest, ShufflesEntireVector) {
+ vector_.Shuffle(&random_);
+ ASSERT_PRED1(VectorIsNotCorrupt, vector_);
+ EXPECT_FALSE(VectorIsUnshuffled(vector_)) << vector_;
+
+ // Tests the first and last elements in particular to ensure that
+ // there are no off-by-one problems in our shuffle algorithm.
+ EXPECT_NE(0, vector_.GetElement(0));
+ EXPECT_NE(kVectorSize - 1, vector_.GetElement(kVectorSize - 1));
+}
+
+TEST_F(VectorShuffleTest, ShufflesStartOfVector) {
+ const int kRangeSize = kVectorSize/2;
+
+ vector_.ShuffleRange(&random_, 0, kRangeSize);
+
+ ASSERT_PRED1(VectorIsNotCorrupt, vector_);
+ EXPECT_PRED3(RangeIsShuffled, vector_, 0, kRangeSize);
+ EXPECT_PRED3(RangeIsUnshuffled, vector_, kRangeSize, kVectorSize);
+}
+
+TEST_F(VectorShuffleTest, ShufflesEndOfVector) {
+ const int kRangeSize = kVectorSize / 2;
+ vector_.ShuffleRange(&random_, kRangeSize, kVectorSize);
+
+ ASSERT_PRED1(VectorIsNotCorrupt, vector_);
+ EXPECT_PRED3(RangeIsUnshuffled, vector_, 0, kRangeSize);
+ EXPECT_PRED3(RangeIsShuffled, vector_, kRangeSize, kVectorSize);
+}
+
+TEST_F(VectorShuffleTest, ShufflesMiddleOfVector) {
+ int kRangeSize = kVectorSize/3;
+ vector_.ShuffleRange(&random_, kRangeSize, 2*kRangeSize);
+
+ ASSERT_PRED1(VectorIsNotCorrupt, vector_);
+ EXPECT_PRED3(RangeIsUnshuffled, vector_, 0, kRangeSize);
+ EXPECT_PRED3(RangeIsShuffled, vector_, kRangeSize, 2*kRangeSize);
+ EXPECT_PRED3(RangeIsUnshuffled, vector_, 2*kRangeSize, kVectorSize);
+}
+
+TEST_F(VectorShuffleTest, ShufflesRepeatably) {
+ TestingVector vector2;
+ for (int i = 0; i < kVectorSize; i++) {
+ vector2.PushBack(i);
+ }
+
+ random_.Reseed(1234);
+ vector_.Shuffle(&random_);
+ random_.Reseed(1234);
+ vector2.Shuffle(&random_);
+
+ ASSERT_PRED1(VectorIsNotCorrupt, vector_);
+ ASSERT_PRED1(VectorIsNotCorrupt, vector2);
+
+ for (int i = 0; i < kVectorSize; i++) {
+ EXPECT_EQ(vector_.GetElement(i), vector2.GetElement(i))
+ << " where i is " << i;
+ }
+}
+
// Tests the size of the AssertHelper class.
TEST(AssertHelperTest, AssertHelperIsSmall) {