Skip to content

Basic usage

Elwardi edited this page Nov 6, 2022 · 2 revisions

If you're new to Unit Testing, please read through these docs first:

Proper test case usage

Catch2 supports both regular Test Case and BDD-style (Given, When, Then...) test cases. Although it's recommended to stick to a single style throughout your test codebase; there is nothing stopping you from mixing and matching - especially if you want type-based test cases.

This section details on how you should decorate your test case so it plays nice with the ./Alltest script.

Tag all your test cases

The ./Alltest script uses tags to execute your test case:

  • In serial or parallel.
  • On an associated OpenFOAM case (from the cases directory).

A typical test case, which runs both in serial and in parallel, on the cavity case, will usually look like this:

// BTW you still can use other tags here and it won't affect how tests run
// The same applies to BBD-style test cases. See exampleTests for inspiration
TEST_CASE("Case title", "[serial][parallel][cavity][myOwnTag]")
{
    // This test case will get executed both in serial and in parallel
    // It will get executed when the cases/cavity case gets processed
    // But if there is no cases/myOwnTag, the script won't try to treat that tag
    // as an OpenFOAM case.
    // ...
}

Parallel runs of the OpenFOAM cases

It's recommended to run on at least 4 processes if you're testing MPI-related code. In CI containers, you usually have access to only 2 CPUs, so you have to call mpirun with the --oversubscribe flag.

The provided test driver includes the regular setRootCase.H from whatever OpenFOAM version you're compiling with. So, it manages parallel communications in the same way your solvers do.

Supply your own tests

Writing code for your tests

Your tests for a custom library are supposed to be put in tests/libName and they need to have a tests/libName/Make directory which is used by wmake to compile them. Your Make/files should look like this:

../testDriver.C
/*
    Your .C files to compile here
*/

EXE = testDriver

and your Make/options should link to your library you want to test.

Sooner or later, your Make/options file will start diverging if you're writing cross-forks code!

Every test case in those .C files should have three parts: Setup, check, tear-down. The following details on what to do in each part:

TEST_CASE("Case title", "[serial][cavity]") {
    // Setup
    // Check
    // Tear-down
}

Setup the test case environment

This almost always includes:

  • Grabbing a reference to the time object, which has a global pointer extern'd from the test driver.
    Time& runTime = *timePtr;
  • Reading the mesh. This is done on each test case setup (instead of doing it in the test driver) because you need some freedom in what type of mesh you want to create (createMesh.H, createDynamicFvMesh.H, ..., etc).
    // argsPtr is also extern'd from the test driver
    argList& args = *argsPtr;
    #include "createMesh.H"
  • It's also recommended that you capture some vars at this stage (Pstream::myProcNo(), Pstream::nProcs()).
  • Prepare any parameters your class's constructor might need.
  • Avoid reading dictionaries from the OpenFOAM case directory at all coasts
    • Instead; make your construct take in a mesh and either a dictionary or an Istream for configuration.
    • If there is no way to avoid reading from the case's directory; Use IStringStream to create the desired dictionary state and write to disk just before constructing your objects.

For examples, skim through the example mesh tests. These are Foam-Extend-specific but illustrate the idea.

Check if an expression is true

  • Use Catch2 assertions to write your checks.
  • Hard-code the desired state of your objects (try to not write code to do so; or use external libraries for this).
  • You almost always can use your objects' interfaces in those expressions:
    // Use Foam::List<label>::operator== to check
    REQUIRE(computedLabelList == correctLabelList);
  • You can also use Catch2 matchers for matching strings, (std::) vectors and floating point numbers.

Tear-down of the test case environment

Only one thing to remember doing in here:

  • If you incremented the time during setup or checking stages; remember to set it back to 0 with:
    runTime.setTime
    (
        dimensionedScalar("", dimTime, 0.0), 0
    );
  • Keep this time.C test active so it tells you if/when you forget to do so.

Ship your own OpenFOAM cases

This highly depends on what you're trying to test, but here are my recommendations for a good OpenFOAM test case:

  • A minimal system/controlDict with a modifiable deltaT, ascii write format and no compression.
  • An already built mesh.
  • Empty fvSchemes and fvSolution (you don't need these if you're only testing on Foam-Extend)