diff options
author | Baruch Burstein <bmburstein@gmail.com> | 2022-07-31 19:50:04 (GMT) |
---|---|---|
committer | Baruch Burstein <bmburstein@gmail.com> | 2022-07-31 19:50:04 (GMT) |
commit | 3280a930bf3f2fdb77da9a6cdf5866ef22708ff1 (patch) | |
tree | 592fb787261482da935fd0286d306510b67688a1 | |
parent | dd7a9d29a33de34836c345c3b753d4eba15c5f44 (diff) | |
download | googletest-3280a930bf3f2fdb77da9a6cdf5866ef22708ff1.zip googletest-3280a930bf3f2fdb77da9a6cdf5866ef22708ff1.tar.gz googletest-3280a930bf3f2fdb77da9a6cdf5866ef22708ff1.tar.bz2 |
Custom type with Combine(). Fix for #3781
-rw-r--r-- | docs/reference/testing.md | 1 | ||||
-rw-r--r-- | googletest/include/gtest/gtest-param-test.h | 40 | ||||
-rw-r--r-- | googletest/include/gtest/gtest.h | 2 | ||||
-rw-r--r-- | googletest/include/gtest/internal/gtest-param-util.h | 71 | ||||
-rw-r--r-- | googletest/test/googletest-param-test-test.cc | 76 |
5 files changed, 180 insertions, 10 deletions
diff --git a/docs/reference/testing.md b/docs/reference/testing.md index dc47942..877dfa3 100644 --- a/docs/reference/testing.md +++ b/docs/reference/testing.md @@ -109,6 +109,7 @@ namespace: | `ValuesIn(container)` or `ValuesIn(begin,end)` | Yields values from a C-style array, an STL-style container, or an iterator range `[begin, end)`. | | `Bool()` | Yields sequence `{false, true}`. | | `Combine(g1, g2, ..., gN)` | Yields as `std::tuple` *n*-tuples all combinations (Cartesian product) of the values generated by the given *n* generators `g1`, `g2`, ..., `gN`. | +| `ConvertGenerator<T>(g)` | Yields values generated by generator `g`, `static_cast` to `T`. | The optional last argument *`name_generator`* is a function or functor that generates custom test name suffixes based on the test parameters. The function diff --git a/googletest/include/gtest/gtest-param-test.h b/googletest/include/gtest/gtest-param-test.h index b55119a..d59e552 100644 --- a/googletest/include/gtest/gtest-param-test.h +++ b/googletest/include/gtest/gtest-param-test.h @@ -407,6 +407,46 @@ internal::CartesianProductHolder<Generator...> Combine(const Generator&... g) { return internal::CartesianProductHolder<Generator...>(g...); } +// ConvertGenerator() wraps a parameter generator in order to cast each prduced +// value through a known type before supplying it to the test suite +// +// Synopsis: +// ConvertGenerator<T>(gen) +// - returns a generator producing the same elements as generated by gen, but +// each element is static_cast to type T before being returned +// +// It is useful when using the Combine() function to get the generated +// parameters in a custom type instead of std::tuple +// +// Example: +// +// This will instantiate tests in test suite AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// struct ParamType { +// using TupleT = std::tuple<const char*, Color>; +// std::string animal; +// Color color; +// ParamType(TupleT t) : animal(std::get<0>(t)), color(std::get<1>(t)) {} +// }; +// class AnimalTest +// : public testing::TestWithParam<ParamType> {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest, +// ConvertGenerator<ParamType::TupleT>( +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE)))); +// +template <typename T> +internal::ParamConverterGenerator<T> ConvertGenerator( + internal::ParamGenerator<T> gen) { + return internal::ParamConverterGenerator<T>(gen); +} + #define TEST_P(test_suite_name, test_name) \ class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \ : public test_suite_name { \ diff --git a/googletest/include/gtest/gtest.h b/googletest/include/gtest/gtest.h index d19a587..0c97f6c 100644 --- a/googletest/include/gtest/gtest.h +++ b/googletest/include/gtest/gtest.h @@ -1625,7 +1625,7 @@ class GTEST_API_ AssertHelper { // the GetParam() method. // // Use it with one of the parameter generator defining functions, like Range(), -// Values(), ValuesIn(), Bool(), and Combine(). +// Values(), ValuesIn(), Bool(), Combine(), and ConvertGenerator<T>(). // // class FooTest : public ::testing::TestWithParam<int> { // protected: diff --git a/googletest/include/gtest/internal/gtest-param-util.h b/googletest/include/gtest/internal/gtest-param-util.h index e7af2f9..c643a3b 100644 --- a/googletest/include/gtest/internal/gtest-param-util.h +++ b/googletest/include/gtest/internal/gtest-param-util.h @@ -950,6 +950,77 @@ class CartesianProductHolder { std::tuple<Gen...> generators_; }; +template <typename From, typename To> +class ParamGeneratorConverter : public ParamGeneratorInterface<To> { + public: + ParamGeneratorConverter(ParamGenerator<From> gen) + : generator_(std::move(gen)) {} + + ParamIteratorInterface<To>* Begin() const override { + return new Iterator(this, generator_.begin(), generator_.end()); + } + ParamIteratorInterface<To>* End() const override { + return new Iterator(this, generator_.end(), generator_.end()); + } + + private: + class Iterator : public ParamIteratorInterface<To> { + public: + Iterator(const ParamGeneratorInterface<To>* base, ParamIterator<From> it, + ParamIterator<From> end) + : base_(base), it_(it), end_(end) { + if (it_ != end_) value_ = std::make_shared<To>(static_cast<To>(*it_)); + } + ~Iterator() override {} + + const ParamGeneratorInterface<To>* BaseGenerator() const override { + return base_; + } + void Advance() override { + ++it_; + if (it_ != end_) value_ = std::make_shared<To>(static_cast<To>(*it_)); + } + ParamIteratorInterface<To>* Clone() const override { + return new Iterator(*this); + } + const To* Current() const override { return value_.get(); } + bool Equals(const ParamIteratorInterface<To>& other) const override { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const ParamIterator<From> other_it = + CheckedDowncastToActualType<const Iterator>(&other)->it_; + return it_ == other_it; + } + + private: + Iterator(const Iterator& other) = default; + + const ParamGeneratorInterface<To>* const base_; + ParamIterator<From> it_; + ParamIterator<From> end_; + std::shared_ptr<To> value_; + }; // class ParamGeneratorConverter::Iterator + + ParamGenerator<From> generator_; +}; // class ParamGeneratorConverter + +template <class Gen> +class ParamConverterGenerator { + public: + ParamConverterGenerator(ParamGenerator<Gen> g) : generator(g) {} + + template <typename T> + operator ParamGenerator<T>() const { + return ParamGenerator<T>(new ParamGeneratorConverter<Gen, T>(generator)); + } + + private: + ParamGenerator<Gen> generator; +}; + } // namespace internal } // namespace testing diff --git a/googletest/test/googletest-param-test-test.cc b/googletest/test/googletest-param-test-test.cc index e3090ae..a1d2786 100644 --- a/googletest/test/googletest-param-test-test.cc +++ b/googletest/test/googletest-param-test-test.cc @@ -51,6 +51,7 @@ using ::std::vector; using ::testing::AddGlobalTestEnvironment; using ::testing::Bool; using ::testing::Combine; +using ::testing::ConvertGenerator; using ::testing::Message; using ::testing::Range; using ::testing::TestWithParam; @@ -402,7 +403,7 @@ TEST(BoolTest, BoolWorks) { TEST(CombineTest, CombineWithTwoParameters) { const char* foo = "foo"; const char* bar = "bar"; - const ParamGenerator<std::tuple<const char*, int> > gen = + const ParamGenerator<std::tuple<const char*, int>> gen = Combine(Values(foo, bar), Values(3, 4)); std::tuple<const char*, int> expected_values[] = { @@ -413,7 +414,7 @@ TEST(CombineTest, CombineWithTwoParameters) { // Tests that Combine() with three parameters generates the expected sequence. TEST(CombineTest, CombineWithThreeParameters) { - const ParamGenerator<std::tuple<int, int, int> > gen = + const ParamGenerator<std::tuple<int, int, int>> gen = Combine(Values(0, 1), Values(3, 4), Values(5, 6)); std::tuple<int, int, int> expected_values[] = { std::make_tuple(0, 3, 5), std::make_tuple(0, 3, 6), @@ -427,7 +428,7 @@ TEST(CombineTest, CombineWithThreeParameters) { // sequence generates a sequence with the number of elements equal to the // number of elements in the sequence generated by the second parameter. TEST(CombineTest, CombineWithFirstParameterSingleValue) { - const ParamGenerator<std::tuple<int, int> > gen = + const ParamGenerator<std::tuple<int, int>> gen = Combine(Values(42), Values(0, 1)); std::tuple<int, int> expected_values[] = {std::make_tuple(42, 0), @@ -439,7 +440,7 @@ TEST(CombineTest, CombineWithFirstParameterSingleValue) { // sequence generates a sequence with the number of elements equal to the // number of elements in the sequence generated by the first parameter. TEST(CombineTest, CombineWithSecondParameterSingleValue) { - const ParamGenerator<std::tuple<int, int> > gen = + const ParamGenerator<std::tuple<int, int>> gen = Combine(Values(0, 1), Values(42)); std::tuple<int, int> expected_values[] = {std::make_tuple(0, 42), @@ -450,7 +451,7 @@ TEST(CombineTest, CombineWithSecondParameterSingleValue) { // Tests that when the first parameter produces an empty sequence, // Combine() produces an empty sequence, too. TEST(CombineTest, CombineWithFirstParameterEmptyRange) { - const ParamGenerator<std::tuple<int, int> > gen = + const ParamGenerator<std::tuple<int, int>> gen = Combine(Range(0, 0), Values(0, 1)); VerifyGeneratorIsEmpty(gen); } @@ -458,7 +459,7 @@ TEST(CombineTest, CombineWithFirstParameterEmptyRange) { // Tests that when the second parameter produces an empty sequence, // Combine() produces an empty sequence, too. TEST(CombineTest, CombineWithSecondParameterEmptyRange) { - const ParamGenerator<std::tuple<int, int> > gen = + const ParamGenerator<std::tuple<int, int>> gen = Combine(Values(0, 1), Range(1, 1)); VerifyGeneratorIsEmpty(gen); } @@ -469,7 +470,7 @@ TEST(CombineTest, CombineWithMaxNumberOfParameters) { const char* foo = "foo"; const char* bar = "bar"; const ParamGenerator< - std::tuple<const char*, int, int, int, int, int, int, int, int, int> > + std::tuple<const char*, int, int, int, int, int, int, int, int, int>> gen = Combine(Values(foo, bar), Values(1), Values(2), Values(3), Values(4), Values(5), Values(6), Values(7), Values(8), Values(9)); @@ -497,11 +498,11 @@ class NonDefaultConstructAssignString { }; TEST(CombineTest, NonDefaultConstructAssign) { - const ParamGenerator<std::tuple<int, NonDefaultConstructAssignString> > gen = + const ParamGenerator<std::tuple<int, NonDefaultConstructAssignString>> gen = Combine(Values(0, 1), Values(NonDefaultConstructAssignString("A"), NonDefaultConstructAssignString("B"))); - ParamGenerator<std::tuple<int, NonDefaultConstructAssignString> >::iterator + ParamGenerator<std::tuple<int, NonDefaultConstructAssignString>>::iterator it = gen.begin(); EXPECT_EQ(0, std::get<0>(*it)); @@ -523,6 +524,63 @@ TEST(CombineTest, NonDefaultConstructAssign) { EXPECT_TRUE(it == gen.end()); } +template <typename T> +class ConstructFromT { + public: + ConstructFromT(const T& t) : t_(t) {} + template <typename... Args> + ConstructFromT(Args&&... args) : t_(std::forward<Args>(args)...) {} + + bool operator==(const ConstructFromT& other) const { return other.t_ == t_; } + + const T& get() const { return t_; } + + private: + T t_; +}; + +TEST(ConvertTest, CombineWithTwoParameters) { + const char* foo = "foo"; + const char* bar = "bar"; + const ParamGenerator<ConstructFromT<std::tuple<const char*, int>>> gen = + ConvertGenerator<std::tuple<const char*, int>>( + Combine(Values(foo, bar), Values(3, 4))); + + ConstructFromT<std::tuple<const char*, int>> expected_values[] = { + {foo, 3}, {foo, 4}, {bar, 3}, {bar, 4}}; + VerifyGenerator(gen, expected_values); +} + +TEST(ConvertTest, NonDefaultConstructAssign) { + const ParamGenerator< + ConstructFromT<std::tuple<int, NonDefaultConstructAssignString>>> + gen = ConvertGenerator<std::tuple<int, NonDefaultConstructAssignString>>( + Combine(Values(0, 1), Values(NonDefaultConstructAssignString("A"), + NonDefaultConstructAssignString("B")))); + + ParamGenerator<ConstructFromT< + std::tuple<int, NonDefaultConstructAssignString>>>::iterator it = + gen.begin(); + + EXPECT_EQ(0, std::get<0>(it->get())); + EXPECT_EQ("A", std::get<1>(it->get()).str()); + ++it; + + EXPECT_EQ(0, std::get<0>(it->get())); + EXPECT_EQ("B", std::get<1>(it->get()).str()); + ++it; + + EXPECT_EQ(1, std::get<0>(it->get())); + EXPECT_EQ("A", std::get<1>(it->get()).str()); + ++it; + + EXPECT_EQ(1, std::get<0>(it->get())); + EXPECT_EQ("B", std::get<1>(it->get()).str()); + ++it; + + EXPECT_TRUE(it == gen.end()); +} + // Tests that an generator produces correct sequence after being // assigned from another generator. TEST(ParamGeneratorTest, AssignmentWorks) { |