User Data

Passing of the environment context into tests.

Full source code of the example

In previous example we have shown how to integrate the framework with your custom event loop. However, this feature is not complete without any possibility to pass the environment, the test is running in, into the testing code.

The framework allows passing of custom data through a generic container UserData. Firstly, user must fill wanted data into. That’s as simple as one call of the setDatum function of the user data container1.

int main(
    int argc_,
    char* argv_[]) {
  ::OTest2::Examples::MainLoop loop_;

  /* -- prepare the testing environment */
  ::OTest2::DfltEnvironment environment_(argc_, argv_);
  environment_.getUserData().setDatum("loop_", &loop_);

  /* -- run the tests */
  bool result_(loop_.runTest(&environment_.getRunner()));

  return result_ ? 0 : 1;
}

The first argument is a text key used for access to the user datum. It may be any string but usually it’s a C/C++ identifier.

There are two ways how to access user data in tests. The first one is to create a fixture variable:

#include <otest2/otest2.h>

#include "mainloop.h"

OT2_SUITE(UserDataSuite) {
  OT2_CASE(FixtureVariable) {
    extern MainLoop loop OT2_USER_DATA_KEY("loop_");

    OT2_SIMPLE() {
      loop.doSomethingUseful("by fixture");
    }
  }
}

The macro OT2_USER_DATA_KEY links the fixture declaration with a user datum identified by the specified key. If the key is equal to the name of the variable more simple macro OT2_USER_DATA may be used.

The extern linkage is mandatory. Otherwise, the clang parser will report an error searching for non-parametric constructor.

The second way how to access user data is function parameters. If a start-up, tear-down or test state method has some parameters, they are expected to be user data and the pre-processor generates code which passes user data into the function.

#include <otest2/otest2.h>

#include "mainloop.h"

OT2_SUITE(UserDataSuite) {
  OT2_CASE(Parameters) {
    void startUp(
        MainLoop& loop_) OT2_START_UP() {
      loop_.doSomethingUseful("start-up parameter");
    }

    void tearDown(
        MainLoop& l_ OT2_USER_DATA_KEY("loop_")) OT2_TEAR_DOWN() {
      l_.doSomethingUseful("tear-down parameter");
    }

    void firstTestState(
        MainLoop& loop_) OT2_STATE() {
      loop_.doSomethingUseful("in test state");
    }
  }
}

As you can see the full syntax is needed as the compact syntax doesn’t allow definition of function parameters. If the name of the parameter differs the name of the user datum the annotation OT2_USER_DATA_KEY can help.

If the parameter is type const ::OTest2::Context& the OTest2 Context is passed into the function instead of a user datum.

  1. Remark the usage of the full syntax. All previous examples has been written using the compact syntax