Test harnesses

The catalog of shared test harnesses: the fakes, fixtures and helpers that a test suite uses instead of writing its own copy. Each section says what one harness gives, the line that includes it, a use from a real suite, when not to use it, and the suite that tests it.

What a shared harness is

A shared harness is test code that more than one suite needs: a fake of an interface, a fixture that builds a chain of real objects, or a helper such as a bounded wait or an image comparison. It lives in one place, so that a defect in it is fixed once, for every suite.

The rules:

  • Never write a private copy. Where a harness in this catalog does what you need, use it. Two copies of a fake drift apart, and the next defect gets fixed in one of them only. When the harness lacks something, add it to the harness.
  • Add to the catalog. When a second suite needs a helper of yours, move it to a shared place in the same commit as the second use, and add a section to this page.
  • Put it beside the library it knows. A harness that knows the types of one library goes into that library's testing/ folder: src/libs/lib<x>/<x>/.../testing/, in the namespace <x>::testing. Only a helper that knows no library goes into libtest, src/libs/libtest/test/.
  • A library's testing/ header never includes libtest. libtest does not depend on the libraries, and a library does not depend on libtest. A harness in libconstruct that must wait, for example, uses QTest::qWaitFor and not test::pumpUntil.
  • Each harness has a suite of its own, which tests the harness. The sections below name it.
  • A testing/ header is test-only by convention. A library exports its include path to every consumer, so production code can include the header and it compiles. The testing/ folder and the testing namespace are the guard. Do not call a harness from shipping code.
  • Keep the shared binary in mind. A harness used by a member of the shared test binary must leave no process-wide state behind for the next class: no registry entry, no installed handler, no global counter. Where a harness owns such state, its section says how it gives it back. See Join the shared binary.

The include path is relative to the library folder. A header at src/libs/libconstruct/construct/eval/testing/AnalyticFields.hpp is included as #include "construct/eval/testing/AnalyticFields.hpp", and one at src/libs/libtest/test/CommsParty.hpp as #include "test/CommsParty.hpp". The suite's test.qbs depends on the library that holds the harness.

Contents

Library Harnesses
libtest Suite entry, arguments, waits, files, images, messages, sources, git, comms parties and mocks
libactivity Activity fakes
libanon IoRunner
libapp ShutdownTaskHarness
libbahn Lane sink, processor context
libbatch Worker driver, fakes, remote transport, advertisements, digest worker
libcatalog Fake catalog widget
libcomms BlobCourier pairs and pumps
libconstruct Documents, overlay, shell, scene render, fields, components, gizmo, mesh, export, settings
libcrypt Hex test vectors
libdeploy ELF fixtures
libfixo Presets, host compile
libfleet Fake provider, fleet fixture, test join
libfs exFAT test images
libglslang SPIR-V compile and validation
libhelp Stub widget render
libhive Hive builder, exFAT mount, sentient hive
libkiln Broker on a known machine, raw client
libllm Fake driver, registry, scripted client
liblockstep CounterSim
libmediacore Audio measurement, signals, nodes, buffer context, WAV files
libmonitor Process samples
libnode Node constellation, courier shuttle
liboci Fake image registry
libopal Run a task, wait for a plan, auth context, grants, vision subject
libopalnet In-process batch nodes, two-executor fleet
libpair Associate maps
libserial socat pseudo-tty pair
libservice MockService
libsing Output devices
libstandalone Settings store, stray-file check
libstudy One-expression study
libswarm Chunk hash, zero key, L4 nodes, content store, shard store
libswarmnode Swarm cluster, transport link
libtoolchain ScopedToolchainState
libtrigger ScriptedClock
libtrove Trove git, front-door request
libwebrequest Local HTTP server
Other shared test support Test libraries and services outside the testing/ folders

libtest

The general helpers, in src/libs/libtest/test/. Every suite built from the Test template links libtest.

Common

The suite entry point: OC_TEST_MAIN and OC_TEST_MAIN_GUILESS make the main of a suite program, or the entry of a shared-binary member, and set up the application, the arguments and the teardown.

  • Header: src/libs/libtest/test/Common.hpp
  • Include: #include "test/Common.hpp"
  • Tested by: every suite, since no suite starts without it.
  • Do not use it in a suite outside the Test template that keeps to Qt Core and Qt Test. Such a suite cannot link libtest; see HarnessArguments.

From testCommsParty:

OC_TEST_MAIN(test, TestCommsParty)

TestArgs

Parses the harness options (--headless, --display, --keep-basedir and the others) before QTest sees them, chooses the Qt platform, and answers isHeadless() and projectRoot(). It also selects Qt's PulseAudio backend on Linux, because Qt 6.10.1's PipeWire backend crashes about one first audio device enumeration in 20. --param audio-backend=pipewire selects PipeWire again.

  • Header: src/libs/libtest/test/TestArgs.hpp
  • Include: #include "test/TestArgs.hpp"
  • Tested by: testCwdIsPinned, for the project root and the working folder.
  • Do not call TestArgs::parse() yourself in a suite that uses OC_TEST_MAIN: the macro already calls it. A new option goes into HarnessArguments in the same change.

From testCwdIsPinned:

const QString declared = test::utility::TestArgs::projectRoot();
QVERIFY2(!declared.isEmpty(),
    "libtest was built without OC_TEST_PROJECT_ROOT, so nothing pins the "

HarnessArguments

Removes the harness options from argv, for a suite that is not built from the Test template and so has no TestArgs. It needs Qt Core only.

  • Header: src/libs/libtest/test/HarnessArguments.hpp
  • Include: #include "test/HarnessArguments.hpp"
  • Tested by: testHarnessArguments
  • Do not use it in a suite built from the Test template: OC_TEST_MAIN and TestArgs already remove the options.

From testLibserial:

argc = test::harness::stripHarnessArguments(argc, argv);
QCoreApplication app(argc, argv);

SharedMain

The one main of a shared test binary: --list, --run, and the reset of the working folder, the data folder and the application name before each member.

  • Header: src/libs/libtest/test/SharedMain.hpp
  • Include: #include "test/SharedMain.hpp"
  • Tested by: testPathResolver, for enterClass().
  • Do not include it in a suite. The build generates the registry that calls it; a suite joins with shared: true in its test.qbs.

From testPathResolver:

const QString saved = QCoreApplication::applicationName();
test::shared::enterClass("testSharedFolderA");
const QString a = PathResolver::dataDir();

Utility

The general helpers of test::utility:

Function What it does
fixturePath() The path of a fixture file in test/<suite>/ or test/fixtures/, independent of the working folder
writeFile() Writes bytes or text to a file, making its folders, and says whether every byte was written
appendFile() Adds bytes to the end of a file, making the file and its folders, and says whether every byte was written
runProgram() Runs a program, waits with a time limit, and returns its exit code and output
builtProgram(), installRoot(), installedProgram() Finds a program of the tree in this build, or in its install root
applicationMains(), classifyMain() The application entry points under src/
runInteractive() Hands a widget to a person and waits for the close; in a headless run it returns at once
tempFilePath() A path in the system temporary folder with the octomy_test_ prefix
realQmake() The qmake of the Qt that the suite runs on
  • Header: src/libs/libtest/test/Utility.hpp
  • Include: #include "test/Utility.hpp"
  • Tested by: testFixturePath, testWriteFile (also appendFile()), testRunProgram, testInstalledPrograms, and testTest (the headless runInteractive() and waits, tempFilePath() and builtProgram()).
  • Do not read data through QFINDTESTDATA or __FILE__; use fixturePath(). Wrap writeFile() and appendFile() in QVERIFY: a check inside a helper leaves only the helper. A suite whose only content is runInteractive() asserts nothing without a person, so it belongs in the interactive category.

From testLayeringCheck and testConstructCli:

return test::utility::runProgram(tool, args, {root, 30000, true});

mConstructBin = test::utility::builtProgram(QStringLiteral("construct"));

Pump

test::pumpUntil(step, done, deadlineMs): runs a step and the event loop until a condition holds or a wall-clock deadline passes.

  • Header: src/libs/libtest/test/Pump.hpp
  • Include: #include "test/Pump.hpp"
  • Tested by: testPump.
  • Use it when a timer produces what you wait for. When the assertion counts iterations, bound the loop by the count and keep a clock only as a hang guard. A library's testing/ header cannot use it.

From testKilnBroker:

test::pumpUntil({}, [&]() {
    snapshot = broker.statsSnapshot();
    return snapshot.value(QStringLiteral("now")).toObject().value(QStringLiteral("linksDone")).toInteger() >= 1;
}, 5000);

SessionProbe

test::utility::canExposeWindows(): whether a shown window becomes exposed in this session, probed once with a real widget.

  • Header: src/libs/libtest/test/SessionProbe.hpp
  • Include: #include "test/SessionProbe.hpp"
  • Tested by: testSessionProbe.
  • Do not skip on the platform name, such as offscreen, instead. Skip with QSKIP on false; do not assert on it.

From testViewportSwap:

if (!test::utility::canExposeWindows()) {
    QSKIP("The viewport swap is a layout-and-show defect class, and this "
        "session cannot expose a window, so it runs neither. See the "
        "class docs.");

TemporaryTree

A temporary folder that a test fills with files by relative path, removed with the object.

  • Header: src/libs/libtest/test/TemporaryTree.hpp
  • Include: #include "test/TemporaryTree.hpp"
  • Tested by: testTemporaryTree
  • Do not use it for one file; QTemporaryDir and writeFile() do that.

From testKilnGraph (TestKilnImport.cpp):

test::TemporaryTree checkout;
QVERIFY(checkout.dir.isValid());
QVERIFY(checkout.write(QStringLiteral(".kiln"), "variants = debug release\n[path=src/libs/lib*]  type = lib\n"));

Git (libtest)

Runs the git command to build a fixture repository, with a test identity, main as the first branch and no signing.

  • Header: src/libs/libtest/test/Git.hpp
  • Include: #include "test/Git.hpp"
  • Tested by: testGitFixture
  • Do not use it for a test of trove's git work; use Git (libtrove), which runs git as trove does.

From testCompression:

if (!test::utility::gitSucceeds(dir.path(), {QStringLiteral("init"), QStringLiteral("-q")})
    || !test::utility::gitSucceeds(dir.path(), {QStringLiteral("add"), QStringLiteral(".")})

ImageCompare

How two images of the same size differ: the fraction of pixels that moved by more than a tolerance, the mean and largest channel difference, and several counts and probes.

  • Header: src/libs/libtest/test/ImageCompare.hpp
  • Include: #include "test/ImageCompare.hpp"
  • Tested by: testImageCompare
  • Two images that cannot be compared (different sizes, or a null image) give an empty answer. Check the answer before you read a number; do not replace it with a sentinel such as 1.0 or -1.

From testGizmoScenes:

const auto iconsVsArcCompared = test::image::compare(
    images.value(QStringLiteral("construct-launcher-icons")),
    images.value(QStringLiteral("construct-graded-arc")), 4);
QVERIFY2(iconsVsArcCompared, "the two captures differ in size");

MessageCapture

Records the Qt messages that a scope logs, so that a test asserts a diagnostic. It is the message handler while it lives, and restores the previous one.

  • Header: src/libs/libtest/test/MessageCapture.hpp
  • Include: #include "test/MessageCapture.hpp"
  • Tested by: testMessageCapture
  • Do not install a message handler of your own. A capture must end before the capture it is nested in, as a scope does; do not keep one in a member that outlives the case.

From testCrtSafety:

const test::MessageCapture capture;

SourceScan

Walks the project's own source files, without the vendored trees, and reads a file with its comments blanked, for a guard suite that checks the tree's code.

  • Header: src/libs/libtest/test/SourceScan.hpp
  • Include: #include "test/SourceScan.hpp"
  • Tested by: testSourceScan
  • An empty walk and a clean one print the same line: assert that the walk found files and print the count. Do not use it to read a fixture; use fixturePath().

From testPathCompliance:

const test::source::SourceTree tree = test::source::sourceFiles();
QStringList out;
if (!tree.error.isEmpty() || tree.files.isEmpty()) {

FreePort

test::freeUdpPort() and test::freeTcpPort(): a loopback port that the OS reported free a moment ago, or 0.

  • Header: src/libs/libtest/test/FreePort.hpp
  • Include: #include "test/FreePort.hpp"
  • Tested by: testCommsParty
  • The port is free when the probe closes, not after. A suite that needs two ports asks for both before it binds either. Do not use a fixed port range.

From testCommsCarrierRebind:

const quint16 first = test::freeUdpPort();
const quint16 second = test::freeUdpPort();
QVERIFY(first != 0 && second != 0 && first != second);

CommsParty

One party of a comms test: its own key store and address book in a temporary folder of the party's own, a UDP address on the loopback, and a Comms over them, with the Associate that another party keeps of it. Port 0 asks for a free port. Two runs in parallel share no file and no port.

  • Header: src/libs/libtest/test/CommsParty.hpp
  • Include: #include "test/CommsParty.hpp"
  • Tested by: testCommsParty
  • Do not drop a party without teardown() or its destructor: an activated key store holds a thread of the global pool until it is deactivated.

From testCourierDataExchange:

test::CommsParty peerA, peerB;
QVERIFY(peerA.setup("courier_exchange_A", 0, policy, {encryptionEnabled}));
QVERIFY(peerB.setup("courier_exchange_B", 0, policy, {encryptionEnabled}));
peerA.know(peerB, TrustLevel::TRUST, ROLE_CONTROL, TYPE_REMOTE);

CommsTester

A sender of test packets over a CommsCarrierUDP and a CommsChannel, with its own key store and address book.

  • Header: src/libs/libtest/test/CommsTester.hpp
  • Include: #include "test/CommsTester.hpp"
  • Tested by: testCommsTesters.
  • For a new two-node comms test, use CommsParty.

From testCommsChannel (testMultiple.cpp):

QList<CommsTester *> senders;
CommsCarrierUDP carrier;
KeyStore keyStore;

CommsSignalLogger

Logs the error, client-added and connection-status signals of a comms object.

  • Header: src/libs/libtest/test/CommsSignalLogger.hpp
  • Include: #include "test/CommsSignalLogger.hpp"
  • Tested by: testCommsTesters.
  • It logs and asserts nothing. To assert a signal, use QSignalSpy.

Used by testCommsChannel, testCourierDataExchange, testDiscoveryToComms and testTrustGatedComms:

#include "test/CommsSignalLogger.hpp"

CourierTester

Tests a pair of couriers without networking, and simulates bad weather on the wire between them.

  • Header: src/libs/libtest/test/CourierTester.hpp
  • Include: #include "test/CourierTester.hpp"
  • Tested by: testCourierTester.
  • Of the network faults it lists, only packet reordering is implemented. For loss, duplication or tampering of BlobCourier frames, use BlobCourierPump.

From testAgentStateCourier:

class AgentStateCourierTester : public CourierTester
{

Corruptifier

Corrupts packets on purpose: bit and byte damage, reordering, duplication, drops and splits, each with a probability, from a seeded random generator.

  • Header: src/libs/libtest/test/Corruptifier.hpp
  • Include: #include "test/Corruptifier.hpp"
  • Tested by: testCorruptifier
  • Give it a seeded RNG so that a failure repeats; the constructor without one is the old form.

From testCorruptifier:

Corruptifier c;
QCOMPARE(c.get(None), 1.0);
QCOMPARE(c.get(ScrambleBit), 0.0);

EncryptionTestConfig

The data columns and rows that run a comms case with encryption on and off, for RSA and Ed25519 keys.

  • Header: src/libs/libtest/test/EncryptionTestConfig.hpp
  • Include: #include "test/EncryptionTestConfig.hpp"
  • Tested by: testLibtestKeys.
  • Use it in a per-function _data() function. initTestCase_data() with QFETCH_GLOBAL does not iterate the rows.

From testTrustGatedComms:

void TestTrustGatedComms::testBlockedPeerCannotInitiateSession_data() { EncryptionTestConfig::addTestData(); }

Keys (libtest)

TestKeys: a fixed private key, public key and key id, and a key id that is wrong on purpose.

  • Header: src/libs/libtest/test/Keys.hpp
  • Include: #include "test/Keys.hpp"
  • Tested by: testLibtestKeys.
  • Do not use it where a case needs a key that it generates itself, such as a key-generation test.

From testKeyStore:

const TestKeys tk;
QVariantMap keyMap;
keyMap["publicKey"]=tk.pubKeyPEM;

SyncTestHelper

A test clock and a TreePair, a local and a remote SyncTree that sync deltas in either direction and assert that they converged. generateRandomOps() applies random sets and removes, drawn from a QRandomGenerator that the caller seeds and prints the seed of.

  • Header: src/libs/libtest/test/SyncTestHelper.hpp
  • Include: #include "test/SyncTestHelper.hpp"
  • Tested by: testSyncTestHelper.
  • The clock is a static variable of the header. Call test::sync::reset() at the start of each case, so that no case starts at another case's time.

From testConfigSyncConvergence:

TreePair pair("test", SyncOwnership::LAST_WRITER_WINS, SyncOwnership::LAST_WRITER_WINS);
advance(10);
pair.syncBoth();
pair.assertConverged();

UDPTester

Sends and receives raw UDP datagrams between two CarrierAddressUDP addresses.

  • Header: src/libs/libtest/test/UDPTester.hpp
  • Include: #include "test/UDPTester.hpp"
  • Tested by: testCommsTesters.
  • It opens a real UDP socket. For a comms test that needs no network, use MockCommsCarrier.

From testCommsCarrierRebind:

UDPTester dataSender(fromAddress, address);

MockAddressBook

An AddressBook that adds associates without persistence and sends its change signals when the test says.

  • Header: src/libs/libtest/test/mock/MockAddressBook.hpp
  • Include: #include "test/mock/MockAddressBook.hpp"
  • Tested by: testLibtestMocks.
  • Do not use it to test persistence; it can keep everything in memory.

From testAssociateListItemIntegration:

mAddressBook = QSharedPointer<MockAddressBook>::create();
mAddressBook->mockEmitTrustChanged(mAssociate->id());

MockAssociate

Makes Associate objects with test data: trusted, blocked, discovered by camera or by NFC, or with a given channel history.

  • Header: src/libs/libtest/test/mock/MockAssociate.hpp
  • Include: #include "test/mock/MockAssociate.hpp"
  • Tested by: testLibtestMocks.
  • For the stored map of an associate, as the serializer reads it, use AssociateMaps.

From testAssociateListItemIntegration:

mAssociate = MockAssociate::createWithChannels(
    "TestAgent",
    TYPE_AGENT,
    TrustLevel::TRUST,

MockCommsSession

A real CommsSession with QObject signals beside it, which the test sends, so that a widget can be tested against a session.

  • Header: src/libs/libtest/test/mock/MockCommsSession.hpp
  • Include: #include "test/mock/MockCommsSession.hpp"
  • Tested by: testLibtestMocks.
  • Do not use it to test the session protocol: the test sets the connected and established states with mockSetConnected() and mockSetEstablished().

From testAssociateListItemIntegration:

mSession = QSharedPointer<MockCommsSession>::create();
mSession->mockConfigure();
mSession->mockSetAssociateId(mAssociate->id());

MockCommsCarrier

A CommsCarrier with no socket: the test writes the data it reads and reads the data it writes, and controls the sending-opportunity timer.

  • Header: src/libs/libtest/test/mock/MockCommsCarrier.hpp
  • Include: #include "test/mock/MockCommsCarrier.hpp"
  • Tested by: testLibtestMocks.
  • Do not use it to test a real carrier's socket handling.

From testCommsChannel (testCommsMock.cpp):

auto carrierA = QSharedPointer<MockCommsCarrier>::create();
auto commsA = QSharedPointer<Comms>::create();
auto chanA = QSharedPointer<CommsChannel>::create();
carrierA->configure();

MockCommsCarrierLog

Logs the signals of a carrier: ready-read, error, sending opportunity and connection status.

  • Header: src/libs/libtest/test/mock/MockCommsCarrierLog.hpp
  • Include: #include "test/mock/MockCommsCarrierLog.hpp"
  • Tested by: testLibtestMocks.
  • It asserts nothing. To assert a signal, use QSignalSpy.

From testCommsCarrierRebind:

MockCommsCarrierLog mocalo;
commsCarrier.setHookCarrierSignals(mocalo, true);

MockCourier

A Courier that sends one datagram a number of times and counts what it sends and receives, within a budget.

  • Header: src/libs/libtest/test/mock/MockCourier.hpp
  • Include: #include "test/mock/MockCourier.hpp"
  • Tested by: testLibtestMocks.
  • The courier retires when it reaches its budget. A test that measures a fresh exchange calls reArm(), which resets the counters and the mandate; a reset of the counters alone measures a courier that stopped.

From testDiscoveryToComms:

QSharedPointer<MockCourier> courA(OC_NEW MockCourier(
    "CommsStepCourierA", peerB.id, "Data from Agent", peerA.comms, maxSends, maxRecs
));
chanA->registerCourier(courA, true);

libactivity

Fakes (libactivity)

The activity doubles: Pushable<T> makes the protected pushImpl of an activity reachable, RecordingActivity records what reaches it, FakeActivityContext holds facilities in a hash, and StubHeader records what the stack drives into a header.

  • Header: src/libs/libactivity/activity/testing/Fakes.hpp
  • Include: #include "activity/testing/Fakes.hpp"
  • Tested by: testActivityTesting
  • Do not use Pushable<T> to test navigation; push through a real ActivityStack.

From testActivityStack:

auto a = QSharedPointer<RecordingActivity>::create(nullptr, QStringLiteral("A"));
stack.registerActivity(a);
QVERIFY(stack.push("A", QStringList{"first"}));
QCOMPARE(a->reinvokeCount, 0);

libanon

IoRunner

An i2p IOService that runs on a thread of its own, and waitFuture(), a bounded wait for a std::future.

  • Header: src/libs/libanon/anon/testing/IoRunner.hpp
  • Include: #include "anon/testing/IoRunner.hpp"
  • Tested by: testAnonTesting
  • Do not wait on a future without a bound; waitFuture() gives one.

From testAnonQtTcp:

IoRunner io;
QtTcpAcceptor acceptor(io.svc);
QtTcpSocket server(io.svc), client(io.svc);

libapp

ShutdownTaskHarness

A synthetic ShutdownLifecycle task with a controllable force-cancel, which deregisters itself when it is destroyed.

  • Header: src/libs/libapp/lifecycle/testing/ShutdownTaskHarness.hpp
  • Include: #include "lifecycle/testing/ShutdownTaskHarness.hpp"
  • Tested by: testShutdownTaskHarness
  • Assert wasForceCancelled(); do not spy on the manager's internal state.

From testShutdownLifecycle:

ShutdownTaskHarness h("test.basic");
QVERIFY(h.token() != 0);
QCOMPARE(mgr.activeCount(), 1);

libbahn

FakeLaneSink

A LaneSink that counts, so that every lane rule runs without a sound card. The counting mode keeps the last block and the peaks; the null mode only fills a lane.

  • Header: src/libs/libbahn/bahn/testing/FakeLaneSink.hpp
  • Include: #include "bahn/testing/FakeLaneSink.hpp"
  • Tested by: testFakeLaneSink
  • writable is in samples, not frames. Do not use it to test a device output.

From testBahnOutputLanes:

auto sink = QSharedPointer<FakeLaneSink>::create();
sinks.append(sink);
return QSharedPointer<bahn::LaneSink>(sink);

FakeProcessorContext

A ProcessorContext over a buffer of its own, for a test that drives a processor without the engine.

  • Header: src/libs/libbahn/bahn/testing/FakeProcessorContext.hpp
  • Include: #include "bahn/testing/FakeProcessorContext.hpp"
  • Tested by: testFakeProcessorContext
  • The default sample rate 0 is a context with no device rate. Set a rate when the processor reads one.

From testDrumMachine:

FakeProcessorContext ctx(bufSize);
proc.process(ctx);
QVERIFY2(hasNonZeroSamples(ctx.buffer(), bufSize),

libbatch

WorkerDriver

Drives a worker's job the way the dispatcher does: driveJob(), runJob(), waitForTerminal() and submitAndWaitTerminal().

  • Header: src/libs/libbatch/batch/testing/WorkerDriver.hpp
  • Include: #include "batch/testing/WorkerDriver.hpp"
  • Tested by: testBatchTesting
  • Never call it from shipping code: it builds a stop flag that nothing can set.

From testMeshExportWorker:

batch::testing::driveJob(queue, worker, id);
QVERIFY(worker.canAccept());

Fakes (libbatch)

FixedProvider, a resource of a fixed capacity; jobRequesting(), a job that requests resources; and MockBatchWorker, a worker that completes what it is given.

  • Header: src/libs/libbatch/batch/testing/Fakes.hpp
  • Include: #include "batch/testing/Fakes.hpp"
  • Tested by: testBatchTesting
  • For real compute that a test can kill in the middle, use DigestWorker.

From testBatchAdmission:

mgr->registerProvider(QSharedPointer<IResourceProvider>(new FixedProvider(CPU, cpu)));
mgr->setMode(Locality::Local, NodeMode::Drone);
const auto result = mgr->admit(jobRequesting(1, {{CPU, 2000}}), Locality::Local);

FakeRemoteTransport

A remote batch transport whose faults a test injects (drop, refuse, defer) and whose calls it counts, in order.

  • Header: src/libs/libbatch/batch/testing/FakeRemoteTransport.hpp
  • Include: #include "batch/testing/FakeRemoteTransport.hpp"
  • Tested by: testBatchTesting
  • A case that asserts it survived a dropped call must also assert that the call was dropped, from the counters; otherwise it passes on a working network and proves nothing.

From testBatchRemoteWorker:

FakeRemoteTransport transport;
RemoteBatchWorker worker{&transport, BatchPolicy::local()};
BatchDispatcher dispatcher{&queue, &registry};

Advertisements

An executor advertisement for a test: a reachable Drone peer with a deterministic, ordered id.

  • Header: src/libs/libbatch/batch/testing/Advertisements.hpp
  • Include: #include "batch/testing/Advertisements.hpp"
  • Tested by: testBatchTesting
  • Change only the fields that the case is about on the returned value.

From testBatchExecutorView:

auto ad = executorAdvertisement(1, QStringLiteral("alpha"), {{CPU, 8000}});

DigestWorker

Real compute for a test: a SHA-256 over a generated stream, with expectedDigest(), which the test computes on its own side.

  • Header: src/libs/libbatch/batch/testing/DigestWorker.hpp
  • Include: #include "batch/testing/DigestWorker.hpp"
  • Tested by: testOpalNetTesting, through TwoExecutorFleet.
  • Do not use it where the result does not matter; MockBatchWorker is cheaper.

From testOpalNetNodeBatch:

DigestWorker worker(kDigestType);

libcatalog

FakeWidget

registerFakeWidget(): registers a plain widget of one size and one background color in WidgetRegistry, under test/<id>.

  • Header: src/libs/libcatalog/catalog/testing/FakeWidget.hpp
  • Include: #include "catalog/testing/FakeWidget.hpp"
  • Tested by: testCatalogTesting
  • A second call with the same id does nothing, so every case can call it. Do not use it to test a real catalog widget.

From testOpalUIDriver:

registerFakeWidget({QStringLiteral("UIDriverTestWidget"), {QStringLiteral("test")}, {QStringLiteral("default")},
                    {80, 60}, QStringLiteral("#336")});

libcomms

BlobCourierPump

Two BlobCouriers wired in this process and the pumps that move their frames: makeCourierPair(), CourierLink, pumpUntilQuiet(), exchangeFrames(), pumpMeshUntilQuiet(), and PipeOptions for loss, reordering, duplication and tampering.

  • Header: src/libs/libcomms/comms/testing/BlobCourierPump.hpp
  • Include: #include "comms/testing/BlobCourierPump.hpp"
  • Tested by: testBlobCourierPump
  • QTRY_VERIFY alone moves no frame; step the link until the condition holds.

From testBlobCourier:

auto p = makeCourierPair();
QSignalSpy received(p.receiver.data(), &BlobCourier::blobReceived);
// ...
pumpUntilQuiet(*p.sender, p.senderId, *p.receiver, p.receiverId);

libconstruct

The construct harnesses are in the testing/ folder beside the code they know.

ScopedDocument

Open documents, published for OPAL tasks, and torn down at the end of the scope, also on an early return or a failed check.

  • Header: src/libs/libconstruct/construct/document/testing/ScopedDocument.hpp
  • Include: #include "construct/document/testing/ScopedDocument.hpp"
  • Tested by: testConstructDocumentTesting
  • Do not install an executor or a scene into ConstructServiceLocator by hand.

From testDocumentTabs:

ScopedDocument docs;
DocumentRegistry &registry{docs.registry()};
QList<QUuid> ids{docs.ids()};

OverlayModelFixture

The model half of the scene tree overlay over the production document chain, with no widget: the document, the expression manager and the panel adapter.

  • Header: src/libs/libconstruct/construct/ui/testing/OverlayModelFixture.hpp
  • Include: #include "construct/ui/testing/OverlayModelFixture.hpp"
  • Tested by: testOverlayModelFixture
  • For a suite that needs the browser widget, use OverlayTestFixture.

From testWrapSelection:

struct Fixture : construct::ui::testing::OverlayModelFixture
{
    QUuid add(const QString &def, const QUuid &parent = {})

OverlayTestFixture

One scene tree overlay over the production document chain, with the browser, the rebuild nudge and the drag harness.

  • Header: src/libs/libconstruct/construct/ui/testing/OverlayTestFixture.hpp
  • Include: #include "construct/ui/testing/OverlayTestFixture.hpp"
  • Tested by: no suite of its own; its base is tested by testOverlayModelFixture.
  • A unit suite without widgets uses OverlayModelFixture. Do not write a private nudge or drag helper.

From testOverlaySelection:

OverlayTestFixture fx;
auto &scene = fx.scene;
auto &adapter = fx.adapter;

ConstructShellFixture

A ConstructWidget with settings of its own in a temporary folder, configured as the application configures it, with addDocument() to open a tab.

  • Header: src/libs/libconstruct/construct/ui/testing/ConstructShellFixture.hpp
  • Include: #include "construct/ui/testing/ConstructShellFixture.hpp"
  • Tested by: testConstructShellFixture
  • Configuration is spread across event-loop turns: wait for configure(), do not assume it finished.

From testDocumentRenderRelease:

mShell = std::make_unique<construct::ui::testing::ConstructShellFixture>();
mConfigured = mShell->configure();
QVERIFY(mConfigured);

SceneRender

One frame of a scene the way the render export makes it: prepareSceneRender(), renderScene(), and the cameras lookAtOrigin() and sphereCamera().

  • Header: src/libs/libconstruct/construct/batch/rendering/testing/SceneRender.hpp
  • Include: #include "construct/batch/rendering/testing/SceneRender.hpp"
  • Tested by: testConstructRenderTesting
  • A host with no usable GPU leaves the renderer uninitialized: skip on that, and fail on a bad snapshot. It creates a GPU device, so a suite that uses it is not a member of the shared binary.

From testRenderQuality:

const SceneRender run = prepareSceneRender(mFixturePath, sphereCamera(), preset, options);
QVERIFY2(run.ready(), qPrintable(run.error));

MarchDiagnostic

marchDiagnosticHit(): whether the debug-view 7 frame says that the ray of a pixel hit something.

  • Header: src/libs/libconstruct/construct/batch/rendering/testing/MarchDiagnostic.hpp
  • Include: #include "construct/batch/rendering/testing/MarchDiagnostic.hpp"
  • Tested by: testConstructRenderTesting
  • Read only the blue channel. Do not compare blue with red and green: that inverts for an expensive tracer.

From testTracerOnRealScenes:

if (marchDiagnosticHit(img, x, y)) { ++hits; }

RenderContextFixture

baseRenderContext(): the render context a filename template test expands against, with the session time fixed.

  • Header: src/libs/libconstruct/construct/batch/rendering/testing/RenderContextFixture.hpp
  • Include: #include "construct/batch/rendering/testing/RenderContextFixture.hpp"
  • Tested by: testConstructRenderTesting
  • Do not use it for a render; it holds only the values a template expands.

From testFilenameTemplate:

QCOMPARE(t.expand(baseRenderContext(), &err), QStringLiteral("/tmp/no_tokens_here.png"));

AnalyticFields

Signed distance fields with a closed form: SphereField, BoxField, PlaneField, EmptyField and InvalidField.

  • Header: src/libs/libconstruct/construct/eval/testing/AnalyticFields.hpp
  • Include: #include "construct/eval/testing/AnalyticFields.hpp"
  • Tested by: testAnalyticFields
  • For the field of a real scene, use SceneEvaluator.

From testDualContouring:

SphereField f(0.8f);
const Grid g = sampleGrid(f, 0.0f, 0.1f, -12, 25); // [-1.2, 1.2]

SceneEvaluator

The chain from a scene to a CPU field, as the bounds worker and the export path run it: sceneNodes(), compileScene(), fieldOf() and evaluatorFor().

  • Header: src/libs/libconstruct/construct/eval/testing/SceneEvaluator.hpp
  • Include: #include "construct/eval/testing/SceneEvaluator.hpp"
  • Tested by: testAnalyticFields
  • When the result names a failed stage, report it; do not treat a null evaluator as an empty field.

From testBoundsDetector:

const auto nodes = construct::eval::testing::sceneNodes(scene);
data.nodes = nodes.nodes;
data.rootNodeIndex = nodes.rootNodeIndex;

FieldDelta

The largest difference between two fields over a seeded sample of a cube: maxDelta() and maxDeltaOnGrid().

  • Header: src/libs/libconstruct/construct/eval/testing/FieldDelta.hpp
  • Include: #include "construct/eval/testing/FieldDelta.hpp"
  • Tested by: testAnalyticFields
  • Both return -1 when a value is not finite. Assert that the result is at least 0 before you compare it with a tolerance.

From testComponentParity:

float d = maxDeltaOnGrid(*oracle, *live, 140.0f, 16, 11u, &at);
QVERIFY2(d >= 0.0f && d < 1.0e-3f, qPrintable(describeDelta("component vs plain", d, at)));

OneOpKernel

One position-transform op compiled as a kernel in the GPU wrapper's shape (compileOp()), a timed batch call of it (batchCallCost()), and the cost of one drag tick from that call (dragTickMs()), as gate G1 of the leaf probe measures them.

  • Header: src/libs/libconstruct/construct/eval/testing/OneOpKernel.hpp
  • Include: #include "construct/eval/testing/OneOpKernel.hpp"
  • Tested by: testAnalyticFields
  • A null program means that the compile failed; report error.
  • A cost in wall time is a property of the machine. Assert it only in a perf suite, as testEvalPerf does; a unit suite compares two costs measured side by side.

From testLeafProbe:

const OneOpKernel k = compileOp(*def, ::eval::Backend::Native);
const auto cost = construct::eval::testing::batchCallCost(k, *def, kChildren, kCalls);
worstBatchMs = std::max(worstBatchMs, construct::eval::testing::dragTickMs(cost));

DefineComponent

defineComponent() and parameter(): a component definition made from a branch built in a scratch scene, and a component parameter, in one call.

  • Header: src/libs/libconstruct/construct/component/testing/DefineComponent.hpp
  • Include: #include "construct/component/testing/DefineComponent.hpp"
  • Tested by: testComponentTesting
  • An empty id means that the registry refused the definition; check it.

From testComponentParity (testComponentScopes.inc.cpp):

return defineComponent(doc.scene, doc.target.definitionSource(), build, params);

ScopedPrimitives

Test primitives in the process-wide registry for as long as the object lives; the destructor removes what it added.

  • Header: src/libs/libconstruct/construct/registry/testing/ScopedPrimitives.hpp
  • Include: #include "construct/registry/testing/ScopedPrimitives.hpp"
  • Tested by: testScopedPrimitives
  • Do not add a primitive to the registry without it: in the shared binary, the next class finds it among the built-ins.

From testRapidEditCompileLoop:

ScopedPrimitives prims;
prims.ensure(QStringLiteral("translate"));
prims.ensure(QStringLiteral("rotate"));

CompiledPath

coreNameOf() and pathToCore(): the compiled core of a logical item, and the path to it as the SDF node builder walks the tree.

  • Header: src/libs/libconstruct/construct/model/testing/CompiledPath.hpp
  • Include: #include "construct/model/testing/CompiledPath.hpp"
  • Tested by: testConstructModelTesting.
  • The position sum is right only when translates are in play. A case that rotates or scales measures something else.

From testSDFTransform:

QVERIFY2(!pathToCore(target, torus).isEmpty(), "the parent is still drawn");
const QList<Instance> path = pathToCore(target, sphere);

CameraChain

The scene-and-camera settings chain that the viewport resolves through, and SceneCameraChain, a scene of its own with one camera over it.

  • Header: src/libs/libconstruct/construct/settings/testing/CameraChain.hpp
  • Include: #include "construct/settings/testing/CameraChain.hpp"
  • Tested by: testConstructSettingsTesting
  • CameraChain does not own the scene; keep the scene alive longer.

From testSettingsScopesE2E:

CameraChain a(&s.scene, s.camA);

GlslRotations

The GLSL rotations of the rotate primitives, written again in C++ as the shader evaluates them: the independent ground truth for RotationMath.

  • Header: src/libs/libconstruct/construct/gizmo/testing/GlslRotations.hpp
  • Include: #include "construct/gizmo/testing/GlslRotations.hpp"
  • Tested by: testGizmoTesting
  • Do not derive them from RotationMath; then the test compares the code with itself.

From testRotationModes:

case 0:  return glslRotateX(p, a);
case 1:  return glslRotateY(p, a);
default: return glslRotateZ(p, a);

RotateRings

The camera, rings and screen points that a gizmo drag test needs; a world point goes to the screen through Camera::worldToScreen.

  • Header: src/libs/libconstruct/construct/gizmo/testing/RotateRings.hpp
  • Include: #include "construct/gizmo/testing/RotateRings.hpp"
  • Tested by: testGizmoTesting
  • Do not write a projection of your own; the hit test uses Camera::worldToScreen.

From testGradedArcDrag:

const Camera cam = ringCamera();
const QSize vp = testViewport();
GizmoInteractor gi;
gi.setActiveHandles(rotateRings(), QUuid::createUuid(), QMatrix4x4(), params);

WeldedMesh

weldedMesh(): the triangle soup of a mesher as one indexed mesh, with shared corners merged, so that a test measures it as a surface.

  • Header: src/libs/libconstruct/construct/mesh/testing/WeldedMesh.hpp
  • Include: #include "construct/mesh/testing/WeldedMesh.hpp"
  • Tested by: testConstructMeshTesting
  • The weld tolerance is 1e-6. Do not use it to test the welding of the export path.

From testDualContouring:

const auto mesh = weldedMesh(tris);
const MetricReport r = computeMeshMetrics(mesh, f);
QVERIFY(r.closed);

FakeMeshExportWorker

A mesh export worker that runs no pipeline: the real MeshExportWorker up to the meshing, which completes or fails at once with the result the test set.

  • Header: src/libs/libconstruct/construct/export/testing/FakeMeshExportWorker.hpp
  • Include: #include "construct/export/testing/FakeMeshExportWorker.hpp"
  • Tested by: testConstructExportTesting
  • Do not use it to test meshing; it never compiles the shader.

From testExportUx:

FakeMeshExportWorker worker(&store);
worker.m_shouldSucceed = true;
BatchQueue queue;
BatchWorkerRegistry registry;
registry.registerWorker(&worker);

libcrypt

Hex

fromHex<Array>() and toHex(): hex text to a fixed-size key array and back, for test vectors.

  • Header: src/libs/libcrypt/crypt/testing/Hex.hpp
  • Include: #include "crypt/testing/Hex.hpp"
  • Tested by: testCryptTesting
  • The text must decode to exactly the size of the array. A release build copies what fits and leaves the rest zero, so do not rely on it to check a length.

From testCryptCurve25519:

const auto seed = fromHex<Ed25519::Seed>(
    "9d61b19deffd5a60ba844af492ec2cc4"
    "4449c5697b326919703bac031cae7f60");

libdeploy

ElfFixture

ElfFixtureSpec, makeElf() and writeElf(): a minimal 64-bit ELF object with the dynamic facts a closure test needs, made on any host without a compiler.

  • Header: src/libs/libdeploy/deploy/testing/ElfFixture.hpp
  • Include: #include "deploy/testing/ElfFixture.hpp"
  • Tested by: testDeployTesting.
  • The file has no code and cannot run. Do not use it where a test loads or executes the library.

From testDeployStaging:

writeElf(lib + QStringLiteral("libQt6Core.so.6"), so(QStringLiteral("libQt6Core.so.6")));

libfixo

Preset

Presets and their parts for a fixo test: identityPreset(), hexapodVariantsPreset(), idleChannel(), idleStanza() and addIdleChannel().

  • Header: src/libs/libfixo/fixo/testing/Preset.hpp
  • Include: #include "fixo/testing/Preset.hpp"
  • Tested by: testFixoTesting
  • Do not use it to test the preset file format; build the file in the test.

From testNativeCompile:

QVERIFY(rt.setPreset(identityPreset(QStringLiteral("NativeCompileTest"), 2, {QStringLiteral("drive")})));

HostCompile

Compiles emitted firmware source for this host with the product JIT (NativeCompiler, the vendored Clang and lld), to a shared library that a test loads. It makes the same request as Runtime::buildNative(), with the fixo pantry bundles mounted.

  • Header: src/libs/libfixo/fixo/testing/HostCompile.hpp
  • Include: #include "fixo/testing/HostCompile.hpp"
  • Tested by: testFixoTesting
  • It does not start a compiler process and it does not skip. A failed compile is a finding. Until 2026-10-07 it started g++ or clang++ from PATH, which no machine the product ships to has.
  • compileToSo() takes a JIT cache tag as an optional last argument. Put the generator version in it when the source comes from FirmwareGenerator.
  • It writes a library to the JIT cache and loads it, so keep a suite that uses it out of the shared binary.

From testCodegenDispatch:

const bool ok = !compileToSo(src, errorOut, cacheTag()).isEmpty();
if (!ok) {
    qWarning().noquote() << "Compiler diagnostics:\n" << errorOut;

libfleet

FakeProvider

An in-memory provider that can be told to fail, time out, reclaim and lie. It answers as a provider answers, and costs nothing.

  • Header: src/libs/libfleet/fleet/testing/FakeProvider.hpp
  • Include: #include "fleet/testing/FakeProvider.hpp"
  • Tested by: testFleetTesting, through FleetFixture.
  • It is a fake of the provider contract, not a mock of the fleet's code. Do not use it to test a real provider's wire format; that is LocalHttpServer's job.

From testFleetReconcile:

FakeProvider provider;
MemoryFleetStore store;
Fleet::Config config;

FleetFixture

A Fleet against FakeProvider and a memory store, open and with a rail, with testFleetConfig() and testPurchase().

  • Header: src/libs/libfleet/fleet/testing/FleetFixture.hpp
  • Include: #include "fleet/testing/FleetFixture.hpp"
  • Tested by: testFleetTesting
  • The retry does not sleep: it records each wait. A fleet that does not open is a warning and opened is false; check it.
  • Options::provider gives the fleet another provider in place of the fake, for example an adapter against a local stub (testFleetAws, testFleetHetzner). The case keeps it alive. provisionAndJoin() needs the fake.

From testFleetReconcile:

struct Harness : fleet::testing::FleetFixture
{
    Harness()
        : FleetFixture(options())

TestJoin

useTestCoordinator(), joinTokenOf() and joinAsMachine(): what a rented machine does at first start, with the token it was handed.

  • Header: src/libs/libfleet/fleet/testing/TestJoin.hpp
  • Include: #include "fleet/testing/TestJoin.hpp"
  • Tested by: testFleetTesting.
  • The coordinator identity is for tests; libfleet never parses its key.

From testFleetOpalTasks:

const auto machine = fleet::testing::joinAsMachine(fleet, provider, *i, QDateTime::currentDateTimeUtc(), &err);
if (!machine) {

libfs

ExFatTestImage

Formatted exFAT images in memory: formatExFat(), writeExFatFile() and formatExFatWithFiles(), the input of the corruptifier and the checker.

  • Header: src/libs/libfs/fuzzer/ExFatTestImage.hpp
  • Include: #include "fuzzer/ExFatTestImage.hpp"
  • Tested by: testExFatTestImage
  • It is in the fuzzer folder, not a testing/ folder. Do not use it to test the formatter itself.

From testSemanticCorruptifier:

QVERIFY(formatExFat(device, QStringLiteral("TEST")));

libglslang

Spirv

GLSL to validated SPIR-V through the vendored glslang and SPIRV-Tools: ensureProcess(), compileToSpirv(), validateSpirv() and compileAndValidate().

  • Header: src/libs/libglslang/glslang/testing/Spirv.hpp
  • Include: #include "glslang/testing/Spirv.hpp"
  • Tested by: testGlslangFrontEnd
  • Call ensureProcess() before the first compile: glslang keeps process-wide pools.

From testSpir:

const auto r = octomy::glslang::testing::compileAndValidate(full, lang);
if (!r.ok) {
    qWarning() << "GLSL to validated SPIR-V failed:" << r.log;

libhelp

StubRender

A stand-in for the widget renderer of HelpWidgetImageProvider: a small, deterministic image per widget and mode.

  • Header: src/libs/libhelp/help/testing/StubRender.hpp
  • Include: #include "help/testing/StubRender.hpp"
  • Tested by: testHelpTesting
  • Do not use it to test what a real catalog widget looks like.

From testHelpWidgetImageProvider, which counts the renders of its cache cases:

configureStubProvider(p, diskDir, memCap);
p.setRenderFn(countingStubRender(rendered));

libhive

BuildHive

buildHive(): a hive on disk, made headlessly through HiveCreator, the code path that the application runs.

  • Header: src/libs/libhive/hive/testing/BuildHive.hpp
  • Include: #include "hive/testing/BuildHive.hpp"
  • Tested by: testHiveTesting
  • On failure it names the step that failed; print it.

From testHiveMountRoundTrip:

QVERIFY2(hive::testing::buildHive(storePath, keyPath, PASSPHRASE, why), qPrintable(why));
QVERIFY(QFile::exists(storePath));

ExFatMount

The filesystem callbacks that a hive session mounts a hive with, so that hivecluster-N:/ paths reach the in-memory volume: exFatMount(), and processExFatMount(), one pair for the whole process.

  • Header: src/libs/libhive/hive/testing/ExFatMount.hpp
  • Include: #include "hive/testing/ExFatMount.hpp"
  • Tested by: testHiveTesting
  • Use processExFatMount() only when a hive is mounted through one session and unmounted through another; it is shared by every class in the process.

From testExternalProvisioning:

hive->setFilesystemCallbacks(hive::testing::processExFatMount().mount, hive::testing::processExFatMount().unmount);
hive->setDriveNumber(Hive::allocateDriveNumber());

SentientHive

A hive registry and a session in one base folder, with the initial sentient hive mounted as the node mounts it, and an optional sentient identity.

  • Header: src/libs/libhive/hive/testing/SentientHive.hpp
  • Include: #include "hive/testing/SentientHive.hpp"
  • Tested by: testHiveTesting
  • A suite that needs a second session over the same registry makes one itself.

From testCaptureTheFlag:

hive::testing::SentientHive fixture(tmpDir.path());
QVERIFY(fixture.withSentient(kShadowPassphrase, QStringLiteral("TestSentient")));
auto session = fixture.session;

libkiln

Broker (libkiln)

A kiln broker on a known machine, with shapeMachine(), uniqueEndpoint() and RawClient, a client that writes protocol frames itself.

  • Header: src/libs/libkiln/kiln/testing/Broker.hpp
  • Include: #include "kiln/testing/Broker.hpp"
  • Tested by: testKilnTesting
  • Give each case its own endpoint, so that no case meets another's broker.

From testKilnBroker:

shapeMachine(broker, 4);
QString error;
const QString endpoint = uniqueEndpoint();
QVERIFY2(broker.listenOn(endpoint, &error), qPrintable(error));

libllm

FakeDriver

A driver, its session and completion facets, and the factory that makes it, for a test that drives an LLM consumer without a model. Every action is recorded.

  • Header: src/libs/libllm/llm/driver/testing/FakeDriver.hpp
  • Include: #include "llm/driver/testing/FakeDriver.hpp"
  • Tested by: testFakeDriver
  • Call FakeDriver::resetCapture() before each case. Without it, a class of the shared binary sees the last driver of another.

From testTroveSessionManager:

FakeDriver::resetCapture();
DriverManager dm;
dm.registerFactory(std::make_unique<FakeDriverFactory>());

Registry (libllm)

An LLM registry and manager in a known state, without a node: registryFromMap(), managerFor(), emptyManager() and addDefaultModel().

  • Header: src/libs/libllm/llm/testing/Registry.hpp
  • Include: #include "llm/testing/Registry.hpp"
  • Tested by: testLLMTesting
  • The providers and models that the bootstrap seeds are dropped. Do not use it to test the seeding.

From testLLMRegistry:

auto reg = registryFromMap();
QSignalSpy addedSpy(reg.data(), &LLMRegistry::providerAdded);

ScriptedClient

An LLMClient with no transport, which answers each request from a script and records every request.

  • Header: src/libs/libllm/llm/testing/ScriptedClient.hpp
  • Include: #include "llm/testing/ScriptedClient.hpp"
  • Tested by: testLLMTesting
  • For a consumer that talks to a driver rather than a client, use FakeDriver.
  • Set mInputTokens, mOutputTokens and mCostPer1kTokens to give each reply a known spend, as testLLMIntegration does for the budget path of OpalConversationDriver.

From testOpalConversationStore:

auto client = QSharedPointer<ScriptedClient>::create();
client->mFreshHandle = QStringLiteral("fresh-handle");

liblockstep

CounterSim

A small deterministic simulation for the lockstep engine: an order-sensitive fold over the command deltas, with a heartbeat each turn.

  • Header: src/libs/liblockstep/lockstep/testing/CounterSim.hpp
  • Include: #include "lockstep/testing/CounterSim.hpp"
  • Tested by: testLockstepTesting
  • Do not use it to test a real game's simulation.

From testLockstep:

CounterSim simA;
CounterSim simB;
LockstepEngine<CounterCommand> engineA;
LockstepEngine<CounterCommand> engineB;

libmediacore

Measure

What an audio test measures in a block of samples: binMagnitude(), amplitudeAt(), responseAt(), render(), rms(), peak(), measuredPitch(), centsBetween() and midiToHz().

  • Header: src/libs/libmediacore/mediacore/audio/testing/Measure.hpp
  • Include: #include "mediacore/audio/testing/Measure.hpp"
  • Tested by: testAudioMeasure
  • binMagnitude() is not scaled; compare two bins of one window, or use amplitudeAt() for a value in amplitude units.

From testPianoTuning:

const auto got = measuredPitch(out, expected, SR, 12000, 36000);
const auto err = centsBetween(got, expected);

Signal

The test signals that a DSP test feeds a unit: sineWave(), uniformNoise() and addUniformNoise(), the noise from a given seed.

  • Header: src/libs/libmediacore/mediacore/audio/testing/Signal.hpp
  • Include: #include "mediacore/audio/testing/Signal.hpp"
  • Tested by: testAudioMeasure
  • Give the noise a constant seed, so that a run repeats.

From testSnrMeasurement:

const std::vector<float> tone = mediacore::audio::testing::sineWave(static_cast<float>(freq),
    static_cast<float>(rate), static_cast<float>(amp), static_cast<std::size_t>(frames));

BufferContext

An AudioContext over a test's own mono buffer, so that a node or a processor renders into it.

  • Header: src/libs/libmediacore/mediacore/audio/testing/BufferContext.hpp
  • Include: #include "mediacore/audio/testing/BufferContext.hpp"
  • Tested by: testAudioMeasure
  • The buffer must outlive the context. For a bahn processor, use FakeProcessorContext.

From testGuitarTuning:

BufferContext ctx(out, SR);
g.process(ctx);
return out;

Nodes

The audio test nodes: sources, a gain and sinks with known output, for a schedule, a job or an engine under test.

  • Header: src/libs/libmediacore/mediacore/audio/testing/Nodes.hpp
  • Include: #include "mediacore/audio/testing/Nodes.hpp"
  • Tested by: testAudioTestNodes
  • A SineSource that is not channel aware is refused by a wide schedule, on purpose. Use it only to test that refusal.

From testMediacoreGraph:

auto scale = node("scale", QSharedPointer<Gain>::create(0.37));
auto noise = node("noise", QSharedPointer<NoiseSource>::create());
auto sinkA = node("sinkA", QSharedPointer<Sink>::create(SinkKind::Intrinsic));

WavFile

writeToneWav(): a test tone on disk, a 16-bit PCM stereo WAV file that a media player decodes.

  • Header: src/libs/libmediacore/mediacore/audio/testing/WavFile.hpp
  • Include: #include "mediacore/audio/testing/WavFile.hpp"
  • Tested by: testAudioMeasure
  • Write it into a temporary folder; check the return value.

From testMediaPlayerSource:

QVERIFY(writeToneWav(mWav, WavTone{3.0, LEFT_HZ, RIGHT_HZ, 12000.0, 44100}));

libmonitor

ProcessSamples

processSample() and kernelThread(): the process samples that the process-list suites share.

  • Header: src/libs/libmonitor/monitor/testing/ProcessSamples.hpp
  • Include: #include "monitor/testing/ProcessSamples.hpp"
  • Tested by: testMonitorTesting
  • Do not use it to test the sampler, which reads real processes.

From testProcessGrouping:

const auto p = processSample(101, QStringLiteral("/usr/bin/clang"),
    {QStringLiteral("/usr/bin/clang"), QStringLiteral("-c")});
QCOMPARE(groupKeyFor(p), QStringLiteral("/usr/bin/clang"));

libnode

CourierShuttle

Moves the datagrams of a LockstepCourier or a MatchmakingCourier into its peer through real QDataStream framing, as CommsChannel does per sending opportunity, without the scheduling and the encryption. shuttle() is a perfect wire. LossyLink is one direction of a wire that drops one datagram in dropEvery and doubles one in dupEvery. It counts the datagrams, so a run repeats exactly.

  • Header: src/libs/libnode/node/testing/CourierShuttle.hpp
  • Include: #include "node/testing/CourierShuttle.hpp"
  • Tested by: testLockstepCourier
  • LossyLink counts across every pump(). Use one link per direction.

From testRtsNetworkMatch:

LossyLink aToB{3, 5, 0}; // drop 1/3, duplicate 1/5
LossyLink bToA{4, 6, 0}; // drop 1/4, duplicate 1/6
// ...
aToB.pump(*sessionA.courier(), *sessionB.courier());
bToA.pump(*sessionB.courier(), *sessionA.courier());

NodeConstellation

A zoo server and a number of Agents and Remotes, each made through AppLauncher as the application makes it. Each node and the zoo get a temporary base dir. The last constructor argument, ConstellationMode, says how far a node comes up:

  • Configured (the default): the node is configured only. It has no NID, so its id is empty, and it discovers nothing. Use it when a suite needs the node objects, for example to bind widgets to them.
  • Live: the node gets a NID (IdentityFactory::provisionNode), it is activated, and it discovers through the constellation's zoo (zooUrl(), http://127.0.0.1:8123/api). An Agent and a Remote find each other and hold each other as associates. useZoo must be set. A Live node needs about two seconds more to start and to stop.

init(true) does not wait. Wait with a bound (QTRY_*) for what you assert. The other channel switches (useNetwork and so on) only log.

ConstellationNode<T> is one such node with its base dir and command line. Use it to bring up a node with a zoo of your own.

  • Header: src/libs/libnode/node/testing/NodeConstellation.hpp
  • Include: #include "node/testing/NodeConstellation.hpp"
  • Tested by: testConnectionUI, testDiscovery
  • A derived class with its own hooks must call init(false) in its own destructor; the base destructor reaches only the base hooks.

From testDiscovery:

using DiscoveryTestCase = node::testing::NodeConstellation<Agent, Remote, ZooServer>;
// ...
DiscoveryTestCase dtc(QStringLiteral("DiscoveryTestCase"), 1, 1, true, true, true, true, true, true, node::testing::ConstellationMode::Live);
dtc.init(true);
QTRY_VERIFY_WITH_TIMEOUT(!dtc.agents[0]->nodeIdentity()->id().isEmpty(), 20000);

liboci

FakeRegistry

An in-process image registry that implements the distribution protocol on 127.0.0.1, with switches for the faults of a real one.

  • Header: src/libs/liboci/oci/registry/testing/FakeRegistry.hpp
  • Include: #include "oci/registry/testing/FakeRegistry.hpp"
  • Tested by: no suite of its own; testOciRegistry asserts the client against it.
  • It keeps everything in memory. Do not use it for large images.

From testOciRegistry:

FakeRegistry registry;
QVERIFY(registry.listen());
RegistryClient client(registry.host());

libopal

RunTask

Runs one OPAL task in a test and returns its result: runTask() by name or by task, and runTaskOnThread(), with a context and the log, as a plan runs it.

  • Header: src/libs/libopal/opal/testing/RunTask.hpp
  • Include: #include "opal/testing/RunTask.hpp"
  • Tested by: testOpalTesting
  • Do not build an OpalTaskContext by hand to run one task.

From testStudyOpal:

const OpalTaskResult result = runTask(name);
QVERIFY2(!result.success, qPrintable(
    name + QStringLiteral(" answered with no project open")));

WaitForPlan

waitForPlan(): delivers events until the runtime completes a plan, and returns its result; a time-out returns a result that is not a success.

  • Header: src/libs/libopal/opal/testing/WaitForPlan.hpp
  • Include: #include "opal/testing/WaitForPlan.hpp"
  • Tested by: testOpalTesting
  • Call it after submit(). A plan that completes inside submit() sends its signal before the wait connects.

From testOpalTrigger:

waitForPlan(runtime);

FakeAuthContext

An OpalAuthContext whose every answer the test sets.

  • Header: src/libs/libopal/opal/testing/FakeAuthContext.hpp
  • Include: #include "opal/testing/FakeAuthContext.hpp"
  • Tested by: testOpalTesting
  • The swarm extensions deny, as the base class does. Override them in a subclass when a case needs them.

From testOpalTaskRuntime:

FakeAuthContext auth;
auth.sid = true;
auth.nodeActive = true;

Grants

sessionGrants(): a permission store that allows each of a list of task scopes for the session.

  • Header: src/libs/libopal/opal/testing/Grants.hpp
  • Include: #include "opal/testing/Grants.hpp"
  • Tested by: testOpalTesting
  • Do not use it to test the grant prompt; it grants without asking.

From testSpecService:

return ::opal::testing::sessionGrants(octomy::trove::opal::specTaskScopes());

Subject

makeSubject(): a black widget with one white square in its top-left quadrant, so that a crop can be told from a full grab.

  • Header: src/libs/libopal/opal/vision/testing/Subject.hpp
  • Include: #include "opal/vision/testing/Subject.hpp"
  • Tested by: testOpalVisionTesting
  • The caller owns the widget.

From testOpalVisionCapture:

QScopedPointer<QWidget> a(makeSubject(40, 40));
QScopedPointer<QWidget> b(makeSubject(40, 40));

libopalnet

InProcessBatchNode

InProcessSwitch, the comms courier reduced to what the batch control plane needs, and BatchNode, one node's batch half, built as a running node builds it.

  • Header: src/libs/libopalnet/opalnet/testing/InProcessBatchNode.hpp
  • Include: #include "opalnet/testing/InProcessBatchNode.hpp"
  • Tested by: testOpalNetTesting, through TwoExecutorFleet.
  • A killed node's blobs go nowhere: the sender learns it only from a time-out. The caller owns a node's worker and destroys it after the node.

Used through TwoExecutorFleet, as in testFleetExecutorJoin:

#include "opalnet/testing/TwoExecutorFleet.hpp"

TwoExecutorFleet

A coordinator and two executors on one InProcessSwitch, running DigestWorker jobs, with every placement and every returned job recorded.

  • Header: src/libs/libopalnet/opalnet/testing/TwoExecutorFleet.hpp
  • Include: #include "opalnet/testing/TwoExecutorFleet.hpp"
  • Tested by: testOpalNetTesting
  • Submit through submit(), so that wrongResults() can check each result.

From testFleetEndToEnd:

nodes = std::make_unique<TwoExecutorFleet>(kDigestType, BatchPolicy::local(), executorIds.at(0),
    executorIds.at(1));
QVERIFY2(nodes->advertise(10000), "both executors must advertise");

libpair

AssociateMaps

associateMap() and udpAddressList(): the stored shape of an Associate, as a test writes it.

  • Header: src/libs/libpair/address/testing/AssociateMaps.hpp
  • Include: #include "address/testing/AssociateMaps.hpp"
  • Tested by: testPairTesting
  • Do not use it in a test of the Associate serializer: testAssociate keeps its own map on purpose, as the independent test vector.

From testAddressBook:

address::testing::AssociateMapSpec spec;
spec.id = TestKeys().keyID;
spec.name = "hogboll";

libserial

SocatPtyPair

Two connected pseudo-ttys made by socat: a serial port and its peer.

  • Header: src/libs/libserial/serial/testing/SocatPtyPair.hpp
  • Include: #include "serial/testing/SocatPtyPair.hpp"
  • Tested by: testSerialTesting
  • POSIX only, and not compiled into libserial. Skip when start() fails: socat can be missing.

From testLibserialPty:

mSocatAvailable = mPty.start(&why);
if (!mSocatAvailable) {
    qWarning("%s", qPrintable(why));
    QSKIP("socat not available or pty creation failed; skipping pty tests");

libservice

MockService

A Service whose activation answer the test controls: immediate, manual, delayed, or a deactivation that never comes.

  • Header: src/libs/libservice/service/testing/MockService.hpp
  • Include: #include "service/testing/MockService.hpp"
  • Tested by: testServiceTesting
  • In Manual mode, call finishActivation(), or the activation never ends.

From testServiceManager:

QSharedPointer<MockService> a(OC_NEW MockService("Service-A", QStringList(), MockService::Mode::Manual));
QSharedPointer<MockService> b(OC_NEW MockService("Service-B", QStringList{"Service-A"}, MockService::Mode::Manual));

libsing

Devices

Output devices for a test of the audio graph and its settings: outputDevice(), audioBox(), builtInStereo() and boxAndStereo().

  • Header: src/libs/libsing/audio/testing/Devices.hpp
  • Include: #include "audio/testing/Devices.hpp"
  • Tested by: testSingAudioTesting
  • Do not use them to test device discovery; they open no device.

From testSingularityPlaybackE2E:

const auto devices = sing::audio::testing::boxAndStereo();

libstandalone

Store

storeIn(), the settings store that a standalone app is handed, and filesWrittenBesideTheProcess(), the check that the app writes nothing in the working folder.

  • Header: src/libs/libstandalone/standalone/testing/Store.hpp
  • Include: #include "standalone/testing/Store.hpp"
  • Tested by: testStandaloneTesting
  • filesWrittenBesideTheProcess() changes the working folder while it runs. Do not run it on two threads at once.

From testStandaloneBahn:

const auto strays = filesWrittenBesideTheProcess([&dir] {
    auto settings = storeIn(dir);
    Bahn bahn;

libstudy

OneExpressionStudy

oneExpressionStudy(): the smallest study, one ExpressionComponent bound to one free variable.

  • Header: src/libs/libstudy/study/testing/OneExpressionStudy.hpp
  • Include: #include "study/testing/OneExpressionStudy.hpp"
  • Tested by: testStudyTesting
  • For a model graph of more than one component, build the study in the test.

From testStudyStatus:

study::testing::OneExpression spec;
spec.expression = rangeExpression;
return study::testing::oneExpressionStudy(spec);

libswarm

ChunkHash

chunkHashOf(): the BLAKE3 hash of a label, a fixed and readable chunk hash.

  • Header: src/libs/libswarm/swarm/testing/ChunkHash.hpp
  • Include: #include "swarm/testing/ChunkHash.hpp"
  • Tested by: testSwarmNodeTesting
  • Do not use it where the chunk's content must match its hash; hash the content.

From testSwarmStorageTasks:

return QString::fromLatin1(chunkHashOf(label).toHex());

Keys (libswarm)

zeroKey(): the all-zero chunk key, for a test that seals and opens its own shards.

  • Header: src/libs/libswarm/swarm/testing/Keys.hpp
  • Include: #include "swarm/testing/Keys.hpp"
  • Tested by: testSwarmNodeTesting
  • Never use it for a test of key handling or of secrecy.

From testSwarmNetworkedFetch:

const PublishResult r = net.svcA->publish(hash, /*n*/ 1, /*k*/ 1, zeroKey(), {net.peerB});
QVERIFY(r.allShardsPresent);

L4Nodes

Fully wired L4 nodes for a test that moves shards between peers: L4Node, L4StreamNode, L4NodeOptions, and FakeResourceGate, a scripted resource gate.

  • Header: src/libs/libswarm/swarm/testing/L4Nodes.hpp
  • Include: #include "swarm/testing/L4Nodes.hpp"
  • Tested by: testSwarmNodeTesting
  • For whole SwarmService nodes, use Cluster.

From testSwarmL4Driver and testSwarmPublisher:

const L4NodeOptions kPlain{.lazyThreshold = 11};
const L4NodeOptions kGated{.lazyThreshold = 11, .gated = true};
// ...
L4Node pub("pub");

MemoryContentStore

An in-memory IContentStore that keeps what a test puts and binds, and records the calls.

  • Header: src/libs/libswarm/swarm/testing/MemoryContentStore.hpp
  • Include: #include "swarm/testing/MemoryContentStore.hpp"
  • Tested by: testSwarmNodeTesting
  • history() and snapshot() return what the test sets. Do not use it to test the history of a real store.

From testSwarmStorageTasks:

auto store = QSharedPointer<MemoryContentStore>::create();
SwarmOpalContext::instance().setStore(store);

StubShardStore

An IShardStore that stores nothing and reports a hosted-bytes figure that the test sets.

  • Header: src/libs/libswarm/swarm/testing/StubShardStore.hpp
  • Include: #include "swarm/testing/StubShardStore.hpp"
  • Tested by: testSwarmNodeTesting
  • It keeps the interface's default pin API, so nothing pins. Use a real store for a pin test.

From testSwarmServiceBoot:

auto shardStore = std::make_shared<StubShardStore>(2 * SwarmService::kClaimBytesPerTile);
SwarmService svc(makeStore(dir.path()), makeTransport(QByteArrayLiteral("self-SG")), shardStore);

libswarmnode

Cluster

SwarmCluster: SwarmService nodes over the process-wide fake anonymity transport, which delivers synchronously, each with a content store in a temporary folder of its own.

  • Header: src/libs/libswarmnode/swarmnode/testing/Cluster.hpp
  • Include: #include "swarmnode/testing/Cluster.hpp"
  • Tested by: testSwarmNodeTesting
  • For nodes over real BlobCouriers, use TransportLink.

From testSwarmNetworkedFetch:

octomy::swarmnode::testing::SwarmCluster cluster;
PeerId peerA = QByteArrayLiteral("publisher-A");
QSharedPointer<SwarmService> svcA = cluster.add(peerA, false);

Two swarm peers over two BlobCouriers in this process, each with a CommsBlobAnonTransport whose one reachable peer is the other.

  • Header: src/libs/libswarmnode/swarmnode/testing/TransportLink.hpp
  • Include: #include "swarmnode/testing/TransportLink.hpp"
  • Tested by: testSwarmNodeTesting
  • A suite that wants its own names for the two sides derives and binds them as references.

From testSwarmCommsTransport:

struct Net : octomy::swarmnode::testing::TransportLink {
    QUuid &uuidA = firstId;
    QUuid &uuidB = secondId;

libtoolchain

ScopedToolchainState

The process-wide toolchain state as it was when the object was made, restored when it goes: the registry's components and toolchains, and the bootstrap service's root folder and compiler.

  • Header: src/libs/libtoolchain/toolchain/testing/ScopedToolchainState.hpp
  • Include: #include "toolchain/testing/ScopedToolchainState.hpp"
  • Tested by: testScopedToolchainState
  • Hold one for the whole class, from initTestCase to cleanupTestCase. Do not reset the registry or the service without it.

From testCompilerInterfacePlumbing:

mToolchainState = std::make_unique<toolchain::testing::ScopedToolchainState>();
toolchain::ToolchainBootstrapService::instance().resetForTest();

libtrigger

ScriptedClock

An ITriggerClock whose time the test sets, with an optional calendar.

  • Header: src/libs/libtrigger/trigger/testing/ScriptedClock.hpp
  • Include: #include "trigger/testing/ScriptedClock.hpp"
  • Tested by: testScriptedClock
  • Step the clock past an interval; do not wait for wall time.

From testTroveScheduler:

auto clock = QSharedPointer<ScriptedClock>::create();
batch::BatchQueue q;
Scheduler sched(&q, clock);

libtrove

Git (libtrove)

Runs git with trove's non-interactive environment, and seedRepo(), a repository with one commit, optionally cloned from a bare origin.

  • Header: src/libs/libtrove/trove/testing/Git.hpp
  • Include: #include "trove/testing/Git.hpp"
  • Tested by: testTroveTesting
  • For a fixture repository that is not about trove, use Git (libtest). Skip when gitAvailable() is false.

From testTroveGit:

const QString repo = seedRepo(tmp.path(), Origin::Bare, QStringLiteral("repo"), nullptr);
QVERIFY2(!repo.isEmpty(), "the seed repository could not be written");

FrontDoor

postTcp() and postTask(): a request to a loopback SpecFrontDoor, as a session sends it, with a time-out.

  • Header: src/libs/libtrove/trove/net/testing/FrontDoor.hpp
  • Include: #include "trove/net/testing/FrontDoor.hpp"
  • Tested by: testSpecFrontDoor
  • A status of 0 means that no answer came. Assert the status before the body.

From testSpecFrontDoor:

const auto a = postTcp(door.port(), R"({"task":"test.frontdoor.echo","args":{"say":"hi"}})", {});
QCOMPARE(a.status, 401);

libwebrequest

LocalHttpServer

A minimal HTTP/1.1 server on 127.0.0.1 that produces wire faults on purpose and keeps a transcript of every request.

  • Header: src/libs/libwebrequest/webrequest/testing/LocalHttpServer.hpp
  • Include: #include "webrequest/testing/LocalHttpServer.hpp"
  • Tested by: no suite of its own; testWebRequestStreaming asserts the client against it.
  • It speaks cleartext and trusts every request. Never use it outside a test.

From testWebRequestStreaming:

LocalHttpServer server;
QVERIFY(server.listen());
server.setHandler([](const LocalHttpServer::Request &, LocalHttpServer::Response *r) {
    r->status = 201;

Other shared test support

Test code that is not a header in a testing/ folder, but that a suite uses in place of a copy:

What Where
Offscreen QRhi, rhi::createOffscreenRhi() src/libs/librhi/rhi/OffscreenBootstrap.hpp
Widget and scene screenshots, WidgetScreenshotService and SceneScreenshotService. A widget that the case built is taken with captureLiveWidget(), and a scene widget that the case already built with captureSceneWidget(). Tested by testWidgetScreenshotServiceInMemory and testSceneScreenshotService src/libs/libcatalog/catalog/
Block devices in memory and on file, MemoryBlockDevice and FileBlockDevice src/libs/libfs/fs/
The exFAT fuzzer: FuzzerEngine, FaultInjectingDevice, SemanticCorruptifier src/libs/libfs/fuzzer/
Experiment runs, sweeps and convergence src/libs/liblab/lab/
Network namespaces, NAT and link impairment src/libs/libnettest/
CPU, memory, disk and VM load src/libs/libstress/stress/
Mock nodes and personalities src/libs/libmocknode/
TestWidget src/libs/libwidgets/test/TestWidget.hpp
UI automation scripts and snapshots src/libs/libuiautomation/
Suite templates, copied when you write a new suite test/templates/
Fixture data that two or more suites read test/fixtures/

See also

In this section