summaryrefslogtreecommitdiffstats
path: root/docs/reference/testing.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/reference/testing.md')
-rw-r--r--docs/reference/testing.md99
1 files changed, 98 insertions, 1 deletions
diff --git a/docs/reference/testing.md b/docs/reference/testing.md
index 3ed5211..ea43721 100644
--- a/docs/reference/testing.md
+++ b/docs/reference/testing.md
@@ -110,7 +110,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`. |
+| `ConvertGenerator<T>(g)` or `ConvertGenerator(g, func)` | Yields values generated by generator `g`, `static_cast` from `T`. (Note: `T` might not be what you expect. See [*Using ConvertGenerator*](#using-convertgenerator) below.) The second overload uses `func` to perform the conversion. |
The optional last argument *`name_generator`* is a function or functor that
generates custom test name suffixes based on the test parameters. The function
@@ -137,6 +137,103 @@ For more information, see
See also
[`GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST`](#GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST).
+###### Using `ConvertGenerator`
+
+The functions listed in the table above appear to return generators that create
+values of the desired types, but this is not generally the case. Rather, they
+typically return factory objects that convert to the the desired generators.
+This affords some flexibility in allowing you to specify values of types that
+are different from, yet implicitly convertible to, the actual parameter type
+required by your fixture class.
+
+For example, you can do the following with a fixture that requires an `int`
+parameter:
+
+```cpp
+INSTANTIATE_TEST_SUITE_P(MyInstantiation, MyTestSuite,
+ testing::Values(1, 1.2)); // Yes, Values() supports heterogeneous argument types.
+```
+
+It might seem obvious that `1.2` &mdash; a `double` &mdash; will be converted to
+an `int` but in actuality it requires some template gymnastics involving the
+indirection described in the previous paragraph.
+
+What if your parameter type is not implicitly convertible from the generated
+type but is *explicitly* convertible? There will be no automatic conversion, but
+you can force it by applying `ConvertGenerator<T>`. The compiler can
+automatically deduce the target type (your fixture's parameter type), but
+because of the aforementioned indirection it cannot decide what the generated
+type should be. You need to tell it, by providing the type `T` explicitly. Thus
+`T` should not be your fixture's parameter type, but rather an intermediate type
+that is supported by the factory object, and which can be `static_cast` to the
+fixture's parameter type:
+
+```cpp
+// The fixture's parameter type.
+class MyParam {
+ public:
+ // Explicit converting ctor.
+ explicit MyParam(const std::tuple<int, bool>& t);
+ ...
+};
+
+INSTANTIATE_TEST_SUITE_P(MyInstantiation, MyTestSuite,
+ ConvertGenerator<std::tuple<int, bool>>(Combine(Values(0.1, 1.2), Bool())));
+```
+
+In this example `Combine` supports the generation of `std::tuple<int, bool>>`
+objects (even though the provided values for the first tuple element are
+`double`s) and those `tuple`s get converted into `MyParam` objects by virtue of
+the call to `ConvertGenerator`.
+
+For parameter types that are not convertible from the generated types you can
+provide a callable that does the conversion. The callable accepts an object of
+the generated type and returns an object of the fixture's parameter type. The
+generated type can often be deduced by the compiler from the callable's call
+signature so you do not usually need specify it explicitly (but see a caveat
+below).
+
+```cpp
+// The fixture's parameter type.
+class MyParam {
+ public:
+ MyParam(int, bool);
+ ...
+};
+
+INSTANTIATE_TEST_SUITE_P(MyInstantiation, MyTestSuite,
+ ConvertGenerator(Combine(Values(1, 1.2), Bool()),
+ [](const std::tuple<int, bool>& t){
+ const auto [i, b] = t;
+ return MyParam(i, b);
+ }));
+```
+
+The callable may be anything that can be used to initialize a `std::function`
+with the appropriate call signature. Note the callable's return object gets
+`static_cast` to the fixture's parameter type, so it does not have to be of that
+exact type, only convertible to it.
+
+**Caveat:** Consider the following example.
+
+```cpp
+INSTANTIATE_TEST_SUITE_P(MyInstantiation, MyTestSuite,
+ ConvertGenerator(Values(std::string("s")), [](std::string_view s) { ... }));
+```
+
+The `string` argument gets copied into the factory object returned by `Values`.
+Then, because the generated type deduced from the lambda is `string_view`, the
+factory object spawns a generator that holds a `string_view` referencing that
+`string`. Unfortunately, by the time this generator gets invoked, the factory
+object is gone and the `string_view` is dangling.
+
+To overcome this problem you can specify the generated type explicitly:
+`ConvertGenerator<std::string>(Values(std::string("s")), [](std::string_view s)
+{ ... })`. Alternatively, you can change the lambda's signature to take a
+`std::string` or a `const std::string&` (the latter will not leave you with a
+dangling reference because the type deduction strips off the reference and the
+`const`).
+
### TYPED_TEST_SUITE {#TYPED_TEST_SUITE}
`TYPED_TEST_SUITE(`*`TestFixtureName`*`,`*`Types`*`)`