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, usesQTest::qWaitForand nottest::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. Thetesting/folder and thetestingnamespace 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
Testtemplate 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 usesOC_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
Testtemplate:OC_TEST_MAINandTestArgsalready 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, forenterClass(). - Do not include it in a suite. The build generates the registry that calls it;
a suite joins with
shared: truein itstest.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(alsoappendFile()),testRunProgram,testInstalledPrograms, andtestTest(the headlessrunInteractive()and waits,tempFilePath()andbuiltProgram()). - Do not read data through
QFINDTESTDATAor__FILE__; usefixturePath(). WrapwriteFile()andappendFile()inQVERIFY: a check inside a helper leaves only the helper. A suite whose only content isrunInteractive()asserts nothing without a person, so it belongs in theinteractivecategory.
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 withQSKIPonfalse; 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;
QTemporaryDirandwriteFile()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
BlobCourierframes, 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
RNGso 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()withQFETCH_GLOBALdoes 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()andmockSetEstablished().
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 realActivityStack.
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 writableis 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, ®istry};
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;
MockBatchWorkeris 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_VERIFYalone 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
ConstructServiceLocatorby hand.
From testDocumentTabs:
ScopedDocument docs;
DocumentRegistry ®istry{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
programmeans that the compile failed; reporterror. - A cost in wall time is a property of the machine. Assert it only in a
perfsuite, astestEvalPerfdoes; 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 CameraChaindoes 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 fromFirmwareGenerator.- 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
openedis false; check it. Options::providergives 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,mOutputTokensandmCostPer1kTokensto give each reply a known spend, astestLLMIntegrationdoes for the budget path ofOpalConversationDriver.
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 useamplitudeAt()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
SineSourcethat 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 LossyLinkcounts across everypump(). 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.useZoomust 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;
testOciRegistryasserts 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
OpalTaskContextby 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 insidesubmit()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 thatwrongResults()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
Associateserializer:testAssociatekeeps 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:socatcan 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
Manualmode, callfinishActivation(), 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
SwarmServicenodes, 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()andsnapshot()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);
TransportLink
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
initTestCasetocleanupTestCase. 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;
testWebRequestStreamingasserts 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
- Write a test: step 4 chooses the harness.
- Run tests, and the shared binary.