Lumiera  0.pre.03
»edit your freedom«
session-command-function-test.cpp File Reference

Go to the source code of this file.

Description

Function(integration) test of command dispatch into session thread.

This is a test combining several components to operate similar as in the real application, while still relying upon an unit-test like setup. The goal is to cover how session commands are issued from an access point (CoreService) in the UI backbone, passed on through an abstraction interface (the SessionCommand facade), handed over to the SteamDispatcher, which, running within a dedicated thread (the »session loop thread«), enqueues all these commands and dispatches them one by one.

the test operation

This test setup defines a specifically rigged test command, which does not actually operate on the session. Instead, it performs some time calculations and adds the resulting time offset to a global variable, which can be observed from the test methods. The generated values are controlled by the command arguments and thus predictable, which allows to verify the expected number of invocations happened, using the right arguments.

massively multithreaded stress test

The last test case performs a massively multithreaded torture test to scrutinise the sanity of locking and state management. It creates several threads, each of which produces several instances of the common test command used in this test, and binds each instance with different execution arguments. All these operations are sent as command messages, interspersed with short random pauses, which causes them to arrive in arbitrary order within the dispatcher queue. Moreover, while the "command producer threads" are running, the main thread temporarily disables command dispatch, which causes the command queue to build up. After re-enabling dispatch, the main thread spins to wait for the queue to become empty. The important point to note is that the test command function itself contains no locking. But since all command operations are triggered in a single dedicated thread, albeit in arbitrary order, at the end the checksum must add up to the expected value.

parametrisation

It is possible to change the actual setup with the following positional commandline arguments

  • the number of threads to start
  • the number of consecutive command instances produced in each thread
  • the maximum delay (in µs) between each step in each thread Astute readers might have noticed, that the test fixture is sloppy with respect to proper locking and synchronisation. Rather, some explicit sleep commands are interspersed in a way tuned to work satisfactory in practice. This whole approach can only work, because each Posix locking call actually requires the runtime system to issue a read/write barrier, which are known to have global effects on the relevant platforms (x86 and x86_64). And because the production relevant code in SteamDispatcher uses sufficient (in fact even excessive) locking, the state variables of the test fixture are properly synced by sideeffect.

This test case can fail when, by bad coincidence, the command queue is temporarily emptied, while some producer threads are still alive – because in this case the main thread might verify the checksum before all command instances have been triggered. To avoid this situation, make sure the delay between actions in the threads is not too long and start a sufficiently high number of producer threads.

Definition in file session-command-function-test.cpp.

#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "common/interfaceregistry.h"
#include "steam/control/steam-dispatcher.hpp"
#include "steam/control/command-def.hpp"
#include "include/session-command-facade.h"
#include "lib/typed-counter.hpp"
#include "lib/format-string.hpp"
#include "lib/sync-barrier.hpp"
#include "lib/thread.hpp"
#include "lib/symbol.hpp"
#include "lib/util.hpp"
#include <boost/lexical_cast.hpp>
#include <chrono>
#include <string>
#include <vector>
#include <deque>

Classes

class  SessionCommandFunction_test
 

Macros

#define __DELAY__   sleep_for (20ms);
 

Functions

Time capture (Duration, Offset, int)
 
 LAUNCHER (SessionCommandFunction_test, "function controller")
 Register this test class... More...
 
void maybeOverride (uint &configSetting, Arg cmdline, uint paramNr)
 
void operate (Duration dur, Offset offset, int factor)
 
void undoIt (Duration, Offset, int, Time oldState)
 

Variables

const Symbol COMMAND_I1 {"test.dispatch.function.command.instance-1"}
 
const Symbol COMMAND_I2 {"test.dispatch.function.command.instance-2"}
 
const Symbol COMMAND_ID {"test.dispatch.function.command"}
 
uint MAX_RAND_DELAY_us = 50
 
uint NUM_INVOC_PER_THRED = 10
 
uint NUM_THREADS_DEFAULT = 50
 
TimeVar testCommandState = randTime()
 

Namespaces

 steam
 Steam-Layer implementation namespace root.
 
 steam::control
 Steam-Layer dispatcher, controller and administrative facilities.
 

Function Documentation

◆ LAUNCHER()

steam::control::test::LAUNCHER ( SessionCommandFunction_test  ,
"function controller"   
)

Register this test class...