Skip to content

Tests

Tests run assembled program binaries through the Solana VM via Mollusk to verify correctness and measure compute-unit consumption. The test crate lives at tests/ and is structured as a shared harness library with a unified integration-test runner.

Running

sh
make test

This assembles the program (via make asm) then runs the test suite. The DROPSET_DEPLOY_DIR environment variable must point to the directory containing the assembled .so binary and its keypair — make test sets this automatically.

Layout

txt
tests/
├── Cargo.toml
├── src/
│   └── lib.rs    # Shared harness
└── tests/
    ├── run.rs    # Unified meta-runner
    └── cases/
        ├── mod.rs
        ├── entrypoint.rs
        ├── register_market.rs
        └── ...

src/lib.rs is the shared harness. Provides setup() to load a program binary into Mollusk, check() to execute an instruction and compare the result, and run_and_report() to run a batch of cases with a CU table.

tests/run.rs is the single #[test] entry point that calls run_and_report for every case group. Adding a new group means adding one line here and a pub mod in cases/mod.rs.

Anatomy of a case file

Each case file defines an enum of test cases wrapped in the test_cases! macro, which auto-generates an ALL slice and a CaseName impl that derives snake_case display names from the variant identifiers. The file then implements TestCase with only a run() method.

// Verifies: convention

Each match arm in run() carries one or more // Verifies: ALGORITHM-NAME comments that link the case to an algorithm specification. Placing the tag next to the test logic makes it easy to see what each case exercises. A single case may verify multiple algorithms:

rust
// Verifies: ENTRYPOINT
// Verifies: REGISTER-MARKET
Self::SomeCase => check(setup, &[...], ...),

The build-time algorithm index scans these tags and associates each test case with its algorithm. The <Algorithm> component then renders a collapsed Tests section on each algorithm's page, with nested details for every linked case showing syntax-highlighted Rust source.

Harness API

setup() / setup_program(name)

setup() loads the default dropset binary. setup_program(name) loads a named binary for standalone subroutine harnesses assembled as separate .so files.

Both read from DROPSET_DEPLOY_DIR and panic if the environment variable is unset or the binary is missing.

check(setup, data, expected)

Sends an instruction with the given data bytes (no accounts) and compares the result against an optional ErrorCode. Pass None for expected success, or Some(ErrorCode::Variant) for a ProgramError::Custom failure.

check_with_accounts(setup, data, n_accounts, expected)

Like check, but populates the input buffer with n_accounts dummy accounts so the program sees the requested account count.

check_custom(setup, data, account_metas, accounts, expected)

Like check_with_accounts, but accepts pre-built account and meta lists for full control over account contents and keys. Use this when a test needs specific account data or duplicate keys.

test_cases! macro and TestCase trait

Wrap your case enum in test_cases! to auto-generate the ALL slice and CaseName impl (snake_case names derived from variant identifiers). Then implement TestCase on your enum with only run(). The CaseName trait (required by TestCase) provides the display name for the CU table.

run_and_report(heading, cases, setup)

Iterates over cases, prints a formatted table of case names and CU consumption, and panics at the end if any case failed.

Adding a new case group

  1. Create tests/tests/cases/my_feature.rs with a Case enum wrapped in test_cases! and a TestCase impl containing only run().
  2. Add pub mod my_feature; to tests/tests/cases/mod.rs.
  3. Add a run_and_report("MyFeature", cases::my_feature::Case::ALL, &setup); line in tests/tests/run.rs.