Skip to main content
Firedancer has a comprehensive test suite including unit tests, fuzz tests, and various sanitizer builds to ensure code quality and security.

Quick Start

1

Configure huge pages

Allocate shared memory for tests:
2

Build the project

3

Run unit tests

If your MEMLOCK limit is low, increase it with:

Golden Configuration

The most reliable system configuration for testing:
  • Kernel version: Linux 4.18 or newer
  • Operating system: RHEL 8 or Ubuntu 22.04 (or Fedora/Debian equivalents)
  • Compiler versions: GCC 12 or Clang 15
  • CPU: Icelake Server or Epyc 2 (or newer)
  • Memory: 2 gigantic pages (2x1 GiB) per core, reserved via fd_shmem_cfg
While Firedancer aims to support tests on various hosts, the golden configuration is what the team uses internally. It helps eliminate system noise such as page table walks, page faults, allocation failures, and OOM kills.

Unit Tests

Overview

Unit tests are C programs that contain test logic for Firedancer’s modules. They can be found adjacent to source code in the src/ directory and are titled test_{...}.c.

Creating Unit Tests

Example Local.mk configuration:

Automatic Unit Tests

Automatic unit tests run without any command-line parameters and:
  • May only run on the main thread
  • Complete successfully given 2 GiB memory (backed by any page type)
  • Are tested on every commit as-is
  • Run at least weekly with extended instrumentation
  • Typically finish in under 5 minutes
Example Local.mk configuration:

Fuzz Tests

Overview

Fuzz tests verify the behavior of a component given a large number of arbitrary byte sequences. Fuzzing is particularly effective at finding bugs in:
  • Parsers
  • Protocol handlers
  • Serialization/deserialization logic
Differential fuzz tests can detect diverging behavior when comparing a module to a reference implementation (e.g., the virtual machine).

Creating Fuzz Tests

Example Local.mk configuration:

Fuzzing Engines

Multiple fuzzing engines are supported:

Installing AFL++

Stub Fuzzing Engine

If no fuzzing engine is provided, fuzz tests are built with a stub engine. The stub engine cannot find new inputs but can regression test against old inputs:

Combining Fuzzers with Sanitizers

For improved error detection, combine fuzzers with sanitizers:

Sanitizers

The codebase supports several sanitizers that add runtime checks for various error conditions.
Using sanitizers is not recommended for production builds.

AddressSanitizer (ASan)

Detects invalid memory accesses:
  • Use-after-free
  • Heap buffer overflow
  • Stack buffer overflow
  • Use-after-return
  • Memory leaks

UndefinedBehaviorSanitizer (UBSan)

Detects various kinds of undefined behavior:
  • Integer overflow
  • Misaligned pointers
  • Invalid shifts
  • Null pointer dereference

MemorySanitizer (MSan)

Detects reads of uninitialized memory.
MemorySanitizer requires all dependencies to be recompiled:

Test Vectors

Firedancer runs a large set of test vectors in CI that test conformance between Firedancer and Agave’s execution down to the same error code. Test vector inputs are sourced from:
  • Vectors generated by past fuzzing campaigns
  • Hand-written / manually generated unit tests
  • Fixed mismatches, added as regression tests

Adding New Test Vectors

1

Create fixtures

Make a pull request into the test-vectors repository with your new fixtures.See solana-conformance for information on generating fixtures.
2

Update commit SHA

Once merged, make a pull request into Firedancer updating:
with the latest test vectors GitHub commit SHA containing your change.

Best Practices

Determinism

Running the same test program (with unvarying inputs) should result in predictable behavior.
  • Use deterministic pseudorandom number generators like fd_rng_t
  • Allow users to change RNG seed or iteration count via command-line flags
  • Avoid using current time as a random value
  • Don’t depend on test execution order

No Inputs Required

Unit tests should support automatic configuration:
  • Bundle inputs using FD_IMPORT_BINARY
  • Run without additional command-line arguments
  • Default to sensible configuration values

Memory Management

DO NOT CALL MALLOC() IN TESTS.
Instead, use one of these approaches:

Static Variables

For small-ish amounts of memory (e.g., 4 MiB), use .bss by declaring uninitialized static variables:
This has better support for embedded targets like on-chain virtual machines.

Workspace Allocation

For larger amounts of memory, allocate an anonymous workspace from shmem:
Supported command-line flags:
  • --page-sz: Size of memory pages (normal/huge/gigantic)
  • --page-cnt: Number of pages to request
  • --numa-idx: NUMA node for memory allocation
Most tests default to 1 “gigantic” page (1 GiB) as per the recommendation to use x86 1 GiB pages.

Using fd_scratch

Using fd_scratch over “raw” shmem pages or static uchar[] is also acceptable.

Memory Configuration

For detailed large page and NUMA configuration:

Running Tests

Continuous Testing

Firedancer runs tests:
  • On every commit (automatic unit tests)
  • At least weekly with extended instrumentation
  • With various sanitizers and fuzzing engines
  • Against test vectors for conformance with Agave
The test suite is designed to be reliable and run on a wide variety of hosts to encourage in-depth testing.