# Test Parametrization

Some of CUB's tests are very slow to build and are capable of exhausting RAM
during compilation/linking. To avoid such issues, large tests are split into
multiple executables to take advantage of parallel computation and reduce memory
usage.

CUB facilitates this by checking for special `%PARAM%` comments in each test's
source code, and then uses this information to generate multiple executables
with different configurations.

## Using `%PARAM%`

The `%PARAM%` hint provides an automated method of generating multiple test
executables from a single source file. To use it, add one or more special
comments to the test source file:

```cpp
// %PARAM% [definition] [label] [values]
```

CMake will parse the source file and extract these comments, using them to
generate multiple test executables for the full cartesian product of values.

- `definition` will be used as a preprocessor definition name. By convention,
  these begin with `TEST_`.
- `label` is a short, human-readable label that will be used in the test
  executable's name to identify the test variant.
- `values` is a colon-separated list of values used during test generation. Only
  numeric values have been tested.

## Special Labels

### CDP / RDC Testing

If a `label` is `cdp`, it is assumed that the parameter is used to explicitly
test variants built with and without CDP support. The `values` for such a
parameter must be `0:1`, with `0` indicating CDP disabled (RDC off) and `1`
indicating CDP enabled (RDC on).

Tests that do not contain a variant labeled `cdp` will only enable RDC if
the CMake config enables them.

## Example

For example, if `test_baz.cu` contains the following lines:

```cpp
// %PARAM% TEST_FOO foo 0:1:2
// %PARAM% TEST_CDP cdp 0:1
```

Six executables and CTest targets will be generated with unique definitions
(only c++17 targets shown):

| Executable Name                  | Preprocessor Definitions    | RDC State |
|----------------------------------|-----------------------------|-----------|
| `cub.cpp17.test.baz.foo_0.cdp_0` | `-DTEST_FOO=0 -DTEST_CDP=0` | Disabled  |
| `cub.cpp17.test.baz.foo_0.cdp_1` | `-DTEST_FOO=0 -DTEST_CDP=1` | Enabled   |
| `cub.cpp17.test.baz.foo_1.cdp_0` | `-DTEST_FOO=1 -DTEST_CDP=0` | Disabled  |
| `cub.cpp17.test.baz.foo_1.cdp_1` | `-DTEST_FOO=1 -DTEST_CDP=1` | Enabled   |
| `cub.cpp17.test.baz.foo_2.cdp_0` | `-DTEST_FOO=2 -DTEST_CDP=0` | Disabled  |
| `cub.cpp17.test.baz.foo_2.cdp_1` | `-DTEST_FOO=2 -DTEST_CDP=1` | Enabled   |

## Changing `%PARAM%` Hints

Since CMake does not automatically reconfigure the build when source files are
modified, CMake will need to be rerun manually whenever the `%PARAM%` comments
change.

## Building and Running Split Tests

CMake will generate individual build and test targets for each test variant, and
also provides build "metatargets" that compile all variants of a given test.

The variants follow the usual naming convention for CUB's tests, but include a
suffix that differentiates them (e.g. `.foo_X.bar_Y` in the example above).

### Individual Test Variants

Continuing with the `test_baz.cu` example, the test variant that uses
`-DTEST_FOO=1 -DTEST_BAR=4` can be built and run alone:

```bash
# Build a single variant:
make cub.cpp17.test.baz.foo_1.bar_4

# Run a single variant
bin/cub.cpp17.test.baz.foo_1.bar_4

# Run a single variant using CTest regex:
ctest -R cub\.cpp17\.test\.baz\.foo_1\.bar_4
```

### All Variants of a Test

Using a metatarget and the proper regex, all variants of a test can be built and
executed without listing all variants explicitly:

```bash
# Build all variants using the `.all` metatarget
make cub.cpp17.test.baz.all

# Run all variants:
ctest -R cub\.cpp17\.test\.baz\.
```

## Debugging

Running CMake with `--log-level=VERBOSE` will print out extra information about
all detected test variants.

## Additional Info

Ideally, only parameters that directly influence kernel template instantiations
should be split out in this way. If changing a parameter doesn't change the
kernel template type, the same kernel will be compiled into multiple
executables. This defeats the purpose of splitting up the test since the
compiler will generate redundant code across the new split executables.

The best candidate parameters for splitting are input value types, rather than
integral parameters like BLOCK_THREADS, etc. Splitting by value type allows more
infrastructure (data generation, validation) to be reused. Splitting other
parameters can cause build times to increase since type-related infrastructure
has to be rebuilt for each test variant.
