SingularityDocs
Testing & performance

Testing & performance · How-to guide

Validation strategy

Choose the smallest check that can falsify the behavior you are changing, then expand only when the affected boundary requires it. A parser change needs actual parser cases; a cross-VM rollback change needs state before and after failure; a durability change needs restart evidence. Compiling more unrelated targets does not substitute for those checks.

The repository has focused local checks, archived CPU suites, specialized GPU acceptance and separate SDK/contract/operational tests. Their scopes overlap, but their guarantees differ.

Start with the changed behavior, its production caller and a candidate test target. Select a row below, run the applicable narrow check, and retain its complete result. The task is complete when the relevant cases actually execute under the required features and demonstrate the expected success and failure behavior; a zero-test result or type check alone cannot establish an execution regression is fixed.

Select by the changed boundary

Change or questionRelevant evidenceEssential failure or variation
Transaction admissionCanonical decoding, signature and recent-blockhash/nonce testsMalformed wire, missing/invalid signatures, replay, expired hash
Compute or feesBudget sanitization, fee calculation and receipt testsDuplicate metadata, clamp, exhaustion, executed failure
Cross-VM executionShared-journal/authority tests and real-engine integrationUndeclared/readonly access, later failure, nested revert and fee/nonce survival
BPFCC/CuEVM adapterDifferential fixtures and real CUDA acceptanceState, logs, return data and compute parity on success and failure
Account persistenceWAL, snapshot, deletion, height/marker and restart testsTruncation, bad checksum, interrupted commit, large-account data
Validator membershipRegistry transition and membership simulationPending/sync gate, epoch rotation, deregistration and incompatible peers
HILSA verifierNative proof/signature vectors and anchor/registry checksWrong chain/domain, malformed proof, weak class, stale sequence
HILSA settlementLocal state tests plus destination contract/relay testsReplay, threshold, pause, revert and receipt reconciliation
SDK/CLISerialization, config, command and error testsMissing wallet, version mismatch, rejected input, timeout and output semantics
Deployment/configurationManifest/ownership/drift and cutover testsMismatched epoch/hash, unauthorized artifact, interrupted remote action

Do not stop at a helper test if production uses a different caller, linker, native table or commit path. The repository map identifies those entrypoints.

Local checks before a full runtime build

For HILSA core changes, the provided check wrapper type-checks the smaller verification crate:

./scripts/hilsa-check
./scripts/hilsa-check --all-targets

The second command includes additional targets in type checking. It does not execute their tests. For HILSA's node-bound state/RPC integration, ./scripts/hilsa-check --full checks the runtime library with default features disabled. The wrapper uses separate incremental target directories and does no code generation or linking. See the wrapper.

For a Rust change requiring executable tests, run from runtime/ with the pinned toolchain and appropriate feature set. A focused source-derived example is:

cd runtime
../scripts/eto-build cargo test --no-default-features --lib \
  programs::compute_budget::tests

That may still compile a substantial library graph on a cold machine. It is an example selection, not a command executed for this wiki. Avoid broad --all-targets compilation merely to run a small test when a library filter provides the needed evidence.

Formatting uses cargo fmt -- --check, with no --all. The repository's dependency layout includes vendored source that broad formatting can rewrite. The runtime's CI Clippy invocation is feature/profile-specific and should be read from the workflow when reproducing a lint failure, rather than guessed from a shorter local command.

Required CPU CI builds once

The main CI producer creates one Nextest archive with:

cargo nextest archive --profile ci --cargo-profile quick --timings   --locked --no-default-features --lib --tests --archive-file <archive>

Unit and integration shards download that archive and execute their partitions. They do not compile again. Ordinary doctests run in the producer because stable Nextest does not archive them. This keeps the required CPU consumers execution-only and makes each shard use the same compiled test graph.

The ci Nextest profile bounds test concurrency to two and preserves failure output in JUnit. Unit shards select kind(lib); integration shards select kind(test) and not binary(deterministic_sim). Deterministic simulation has its own seed matrix but reuses the same archive.

For a downloaded archive, an illustrative replay of one seed is:

SIM_SEED=42 cargo-nextest nextest run \
  --archive-file /path/to/cpu-tests.tar.zst \
  --workspace-remap /path/to/checkout/runtime \
  --profile ci -E 'binary(deterministic_sim)' --no-capture

Use an archive from the source revision under investigation. Preserve the seed and output. Adding a Cargo build to each consumer defeats the design and is not required to reproduce the tests. Source: archive producer, shards and simulation, Nextest profiles.

Know what deterministic simulation covers

The current deterministic_sim harness runs four independently scheduled state replicas over twenty-four heights. It checks reproducibility for one seed, convergence across replicas and state independence across different schedules. CI runs seeds 1, 2, 3, 42 and a run-derived dynamic seed.

Its state path is InMemoryQMDBState::new(). It does not run the QMDB drain, WAL or GPU merkle machinery. It also does not replace a full live-network consensus or crash-recovery test merely because it uses Commonware's deterministic runtime family.

The update/delete workload is valuable for detecting schedule-sensitive state behavior. For disk durability, use the actual persistence/restart path; for membership, use the registry and membership simulation rather than assuming every “deterministic sim” exercises epoch-engine rotation. See harness scope.

GPU and ignored tests require deliberate selection

The GPU verification workflow consolidates related work on its intended GPU runner and shares a target directory. It exercises the relevant PTX, EVM, differential, acceptance and BPFCC suites under their required features and environment. Some tests are only included in the library under cfg(test), cuda and/or rbpf-oracle.

An ordinary CPU test result does not establish that those cases ran. Likewise, #[ignore] is an execution gate: a test existing in source is not a passing test. Examples include:

  • wasm_and_move_share_one_cuevm_journal_commit_and_rollback, ignored by default because it requires the CuEVM shared library and CUDA.
  • differential_state_roots_match in the older WAL rotation differential test, ignored because its baseline path is marked unimplemented and needs CUDA.
  • A companion reference-path test in that file, also ignored due to GPU-backed storage requirements.

Do not remove an ignore marker merely to claim coverage; establish the prerequisites and intended path. Nor should the older incomplete baseline test be used to claim that all modern recovery paths are absent. Follow the node's actual snapshot/WAL recovery implementation and its current tests. Source: CuEVM integration gate, WAL differential gates, GPU workflow.

Byte compatibility, contracts and client behavior

Conformance tests use names ending in _conformance_. The workflow first detects whether such a corpus exists. An empty corpus is a successful no-op and must not trigger a release-graph build. When fixtures exist, the conformance profile runs them and checks the committed fixture directory for unexpected changes. A green empty gate establishes no byte-exact corpus coverage. See conformance detection.

The SDK workflow separately installs, type-checks, tests and builds both singularity-web3 and singularity-cli on Node.js 22. Tests commonly replace network/process dependencies, which is appropriate for command contracts but does not establish that a public endpoint or runtime release is available. The present source-package dependency gap is documented in availability; do not silently substitute another version while claiming a source suite passed.

For destination contract changes, Foundry tests under contracts/evm/test/ exercise bridge custody, gateways, certificates and BLS behavior:

cd contracts/evm
forge test

A local contract test still needs deployment-specific key/threshold/address and integration acceptance before supporting a live settlement claim.

Fuzz targets live in runtime/fuzz/fuzz_targets/ and are a local workflow, not a CI fuzzing service. Select the target for the changed decoder or state transition, preserve the seed/corpus and minimized reproducer, and avoid adding an unrelated CI gate.

Specialized Miri, MSRV, cross-platform, coverage and release-profile checks retain their own builds because their toolchains, instrumentation or targets differ from the shared CPU archive. Coverage measures executed code under its filters; it is not proof of a missing deployment or trust assumption.

Operational checks and evidence from this run

The configuration tooling has a bounded Python suite that does not compile Cargo:

PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover \
  -s infra/tests -p test_eto_config.py

This command was executed during documentation generation: all seventeen tests passed. They validate fixture-based configuration/inventory/audit behavior, not a live fleet.

Other executed checks were:

  • Public CLI 0.3.0 installation and existing source CLI 0.4.0 version/help and parser behavior.
  • Disposable wallet creation, public-address display, restrictive file permissions and overwrite rejection.
  • Offline SDK 0.2.0 construction/signing/serialization of two transfer instructions; decoded signatures verified, then a changed amount invalidated the signature. No transaction was submitted.
  • Existing debug svm-vm help, version, --check-config and --print-effective-config.
  • Bounded public RPC/package reads, including the unhealthy RPC and HILSA pause observations.

No full Cargo build, GPU acceptance suite, Forge suite, live payment, node deployment or recovery rehearsal was executed for this wiki. Keep that evidence boundary when reusing its examples.

When a check fails, preserve complete stdout/stderr, exit status, revision, feature set, seed and fixture identifiers. Fix the cause or explain the exact missing prerequisite. Do not weaken a test filter, remove a security assertion or label a skipped test as passing to complete a change. Continue with the contribution guide for the implementation-to-review path, or benchmarks after correctness is established.

Updated Sep 8, 2026View source

On this page