Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CombineTo<R>() parameters generator #4727

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 50 additions & 6 deletions googletest/include/gtest/gtest-param-test.h
Original file line number Diff line number Diff line change
Expand Up @@ -332,9 +332,10 @@ internal::ParamGenerator<typename Container::value_type> ValuesIn(
// INSTANTIATE_TEST_SUITE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5));
//
//
template <typename... T>
internal::ValueArray<T...> Values(T... v) {
return internal::ValueArray<T...>(std::move(v)...);
template <typename... Ts>
internal::ParamGenerator<std::common_type_t<Ts...>> Values(Ts... vs) {
return ValuesIn(
std::array<std::common_type_t<Ts...>, sizeof...(Ts)>{std::move(vs)...});
}

// Bool() allows generating tests with parameters in a set of (false, true).
Expand All @@ -359,6 +360,47 @@ internal::ValueArray<T...> Values(T... v) {
//
inline internal::ParamGenerator<bool> Bool() { return Values(false, true); }

// CombineTo() allows the user to combine two or more sequences to produce
// values of a Cartesian product of those sequences' elements converted to
// the required type.
//
// Synopsis:
// CombineTo<MyClass>(gen1, gen2, ..., genN)
// - returns a generator producing sequences with elements coming from
// the Cartesian product of elements from the sequences generated by
// gen1, gen2, ..., genN. The sequence elements will have a type of
// Myclass where elements from sequences produced by gen1, gen2, ..., genN
// was provided to the MyClass constructor parameters.
//
// Example:
//
// This will instantiate tests in test suite AnimalTest each one with
// the parameter values Animal("cat", BLACK), Animal("cat", WHITE),
// Animal("dog", BLACK), and Animal("dog", WHITE):
// enum Color { BLACK, GRAY, WHITE };
//
// struct Animal {
// std::string name;
// Color color;
// };
//
// class AnimalTest
// : public testing::TestWithParam<Animal> {...};
//
// TEST_P(AnimalTest, AnimalLooksNice) {...}
//
// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest,
// CombineTo<Animal>(Values("cat", "dog"),
// Values(BLACK, WHITE)));
//
template <typename R, typename... T>
internal::ParamGenerator<R> CombineTo(
internal::ParamGenerator<T>&&... generators) {
return internal::ParamGenerator<R>(
new internal::CartesianProductGenerator<R, T...>(
std::forward<decltype(generators)>(generators)...));
}

// Combine() allows the user to combine two or more sequences to produce
// values of a Cartesian product of those sequences' elements.
//
Expand Down Expand Up @@ -403,9 +445,11 @@ inline internal::ParamGenerator<bool> Bool() { return Values(false, true); }
// INSTANTIATE_TEST_SUITE_P(TwoBoolSequence, FlagDependentTest,
// Combine(Bool(), Bool()));
//
template <typename... Generator>
internal::CartesianProductHolder<Generator...> Combine(const Generator&... g) {
return internal::CartesianProductHolder<Generator...>(g...);
template <typename... T>
internal::ParamGenerator<std::tuple<T...>> Combine(
internal::ParamGenerator<T>&&... generators) {
return CombineTo<std::tuple<T...>, T...>(
std::forward<decltype(generators)>(generators)...);
}

// ConvertGenerator() wraps a parameter generator in order to cast each produced
Expand Down
65 changes: 18 additions & 47 deletions googletest/include/gtest/internal/gtest-param-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ class ParamGeneratorInterface {
virtual ParamIteratorInterface<T>* End() const = 0;
};

template <class GeneratedT,
typename StdFunction =
std::function<const GeneratedT&(const GeneratedT&)>>
class ParamConverterGenerator;

// Wraps ParamGeneratorInterface<T> and provides general generator syntax
// compatible with the STL Container concept.
// This class implements copy initialization semantics and the contained
Expand All @@ -201,6 +206,11 @@ class ParamGenerator {
iterator begin() const { return iterator(impl_->Begin()); }
iterator end() const { return iterator(impl_->End()); }

template <typename R>
operator ParamGenerator<R>() {
return ParamConverterGenerator<T>(*this);
}

private:
std::shared_ptr<const ParamGeneratorInterface<T>> impl_;
};
Expand Down Expand Up @@ -796,39 +806,15 @@ internal::ParamGenerator<typename Container::value_type> ValuesIn(
const Container& container);

namespace internal {
// Used in the Values() function to provide polymorphic capabilities.

GTEST_DISABLE_MSC_WARNINGS_PUSH_(4100)

template <typename... Ts>
class ValueArray {
template <typename R, typename... T>
class CartesianProductGenerator : public ParamGeneratorInterface<R> {
public:
explicit ValueArray(Ts... v) : v_(FlatTupleConstructTag{}, std::move(v)...) {}
using ParamType = R;

template <typename T>
operator ParamGenerator<T>() const { // NOLINT
return ValuesIn(MakeVector<T>(std::make_index_sequence<sizeof...(Ts)>()));
}
explicit CartesianProductGenerator(ParamGenerator<T>&&... g)
: generators_(std::forward<decltype(g)>(g)...) {}

private:
template <typename T, size_t... I>
std::vector<T> MakeVector(std::index_sequence<I...>) const {
return std::vector<T>{static_cast<T>(v_.template Get<I>())...};
}

FlatTuple<Ts...> v_;
};

GTEST_DISABLE_MSC_WARNINGS_POP_() // 4100

template <typename... T>
class CartesianProductGenerator
: public ParamGeneratorInterface<::std::tuple<T...>> {
public:
typedef ::std::tuple<T...> ParamType;

CartesianProductGenerator(const std::tuple<ParamGenerator<T>...>& g)
: generators_(g) {}
~CartesianProductGenerator() override = default;

ParamIteratorInterface<ParamType>* Begin() const override {
Expand Down Expand Up @@ -916,7 +902,8 @@ class CartesianProductGenerator

void ComputeCurrentValue() {
if (!AtEnd())
current_value_ = std::make_shared<ParamType>(*std::get<I>(current_)...);
current_value_ =
std::make_shared<ParamType>(ParamType{*std::get<I>(current_)...});
}
bool AtEnd() const {
bool at_end = false;
Expand All @@ -938,20 +925,6 @@ class CartesianProductGenerator
std::tuple<ParamGenerator<T>...> generators_;
};

template <class... Gen>
class CartesianProductHolder {
public:
CartesianProductHolder(const Gen&... g) : generators_(g...) {}
template <typename... T>
operator ParamGenerator<::std::tuple<T...>>() const {
return ParamGenerator<::std::tuple<T...>>(
new CartesianProductGenerator<T...>(generators_));
}

private:
std::tuple<Gen...> generators_;
};

template <typename From, typename To, typename Func>
class ParamGeneratorConverter : public ParamGeneratorInterface<To> {
public:
Expand Down Expand Up @@ -1020,9 +993,7 @@ class ParamGeneratorConverter : public ParamGeneratorInterface<To> {
Func converter_;
}; // class ParamGeneratorConverter

template <class GeneratedT,
typename StdFunction =
std::function<const GeneratedT&(const GeneratedT&)>>
template <class GeneratedT, typename StdFunction>
class ParamConverterGenerator {
public:
ParamConverterGenerator(ParamGenerator<GeneratedT> g) // NOLINT
Expand Down
64 changes: 64 additions & 0 deletions googletest/test/googletest-param-test-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,70 @@ TEST(ConvertTest, NonDefaultConstructAssign) {
EXPECT_TRUE(it == gen.end());
}

TEST(CombineToTest, DefaultConstructible) {
struct DefaultConstructible {
int x;
std::string s;

bool operator==(const DefaultConstructible& other) const {
return x == other.x && s == other.s;
}
};

static_assert(std::is_default_constructible_v<DefaultConstructible>);
ParamGenerator<DefaultConstructible> gen =
testing::CombineTo<DefaultConstructible>(Values(0, 1), Values("A", "B"));

DefaultConstructible expected_values[] = {
{0, "A"}, {0, "B"}, {1, "A"}, {1, "B"}};
VerifyGenerator(gen, expected_values);
}

TEST(CombineToTest, NonDefaultConstructible) {
class NonDefaultConstructible {
public:
NonDefaultConstructible(const int xArg, std::string sArg)
: x(xArg), s(std::move(sArg)) {}

bool operator==(const NonDefaultConstructible& other) const {
return x == other.x && s == other.s;
}

private:
int x;
std::string s;
};

static_assert(not std::is_default_constructible_v<NonDefaultConstructible>);
ParamGenerator<NonDefaultConstructible> gen =
testing::CombineTo<NonDefaultConstructible>(Values(0, 1),
Values("A", "B"));

NonDefaultConstructible expected_values[] = {
{0, "A"}, {0, "B"}, {1, "A"}, {1, "B"}};
VerifyGenerator(gen, expected_values);
}

TEST(CombineToTest, CopyConstructible) {
struct CopyConstructible {
CopyConstructible(const CopyConstructible& other) = default;

bool operator==(const CopyConstructible& other) const {
return x == other.x && s == other.s;
}

int x;
std::string s;
};

static_assert(std::is_copy_constructible_v<CopyConstructible>);
ParamGenerator<CopyConstructible> gen = testing::CombineTo<CopyConstructible>(
Values(CopyConstructible{0, "A"}, CopyConstructible{1, "B"}));
CopyConstructible expected_values[] = {CopyConstructible{0, "A"},
CopyConstructible{1, "B"}};
VerifyGenerator(gen, expected_values);
}

TEST(ConvertTest, WithConverterLambdaAndDeducedType) {
const ParamGenerator<ConstructFromT<int8_t>> gen =
ConvertGenerator(Values("0", std::string("1")), [](const std::string& s) {
Expand Down