Skip to content

Latest commit

 

History

History
56 lines (36 loc) · 3.44 KB

C_TESTING.md

File metadata and controls

56 lines (36 loc) · 3.44 KB

Checking the C NIF

mix test covers the Elixir API. These checks cover c_src/sqlite3_nif.c. A clean run does not prove SQL, transaction, or SQLite correctness.

Strict warnings and static analysis

Install Clang and Erlang/OTP (including erl_nif.h), then run:

make c-check

This checks c_src/sqlite3_nif.c with -Wall -Wextra -Wformat=2 -Wshadow -Werror, then runs Clang Static Analyzer. Compiler warnings and analyzer findings both fail the command. The vendored SQLite amalgamation is not checked. CLANG=clang-18 make c-check selects a particular Clang. ERTS_INCLUDE_DIR overrides the detected OTP headers.

NIF and SQLite callback signatures keep unused parameters, so that warning is disabled. -DNDEBUG also compiles out assert() uses of those parameters.

The c-check CI job runs this on every pull request and push to main. It does not fetch Mix dependencies, and it does not change the normal build.

ASan and UBSan (Linux)

On Ubuntu/Debian, install clang, libclang-rt-dev, and llvm, plus the usual Elixir/OTP, Make, and C build tools:

mix deps.get
sh scripts/test-c-sanitizers.sh

The runner:

  • Builds the NIF and bundled SQLite with AddressSanitizer and UndefinedBehaviorSanitizer, debug symbols, and frame pointers. A report fails the run rather than letting undefined behavior continue. A build already in _build/c-sanitizers is reused when the sources and sanitizer flags match. Otherwise the NIF is rebuilt.
  • Uses _build/c-sanitizers, so the normal dev and test NIFs stay untouched.
  • Preloads the matching Clang ASan runtime before BEAM starts, including during Elixir compilation, when the NIF on_load can run.
  • Uses LLVM's symbolizer for source locations.
  • Passes +Mea min to BEAM so its optional pooling allocators stay off. More allocations then go through the system allocator that ASan tracks.
  • Disables Exqlite's SQLite Erlang allocator (EXQLITE_DISABLE_ERLANG_ALLOCATOR=true) for the same reason.
  • Runs the full test/ suite. test/test_helper.exs excludes :sanitizer and :slow_test; this runner includes both. It does not run the integration suite.

Pass Mix test arguments to select a subset. Those arguments replace the default includes, so add --include sanitizer or --include slow_test when the subset needs that tag:

sh scripts/test-c-sanitizers.sh test/exqlite/sqlite3_nif_test.exs --seed 0

The c-sanitizers CI job uses the same runner. On macOS, use a Linux container or CI. The runner does not guess Darwin's runtime-loading requirements. Do not ship the instrumented NIF.

What a clean run does not establish

Stock BEAM is not sanitizer-instrumented. NIF resource headers, remaining VM allocators, and paths the tests do not hit still limit coverage. The lifecycle tests exercise process exit, garbage collection, explicit cleanup, cross-process resource ownership, binary and integer boundaries, concurrent use of one connection, and reuse after a failed statement. They are not allocation-failure injection tests, and they are not a leak measurement.

LeakSanitizer is off by default (detect_leaks=0). A stock BEAM process reports leaks at exit that are not an Exqlite-only signal. To investigate:

ASAN_OPTIONS=detect_leaks=1 sh scripts/test-c-sanitizers.sh test/exqlite/sqlite3_nif_test.exs

Attribute a finding before you add a suppression. A dedicated leak runner, or Valgrind with an OTP build configured for it, is follow-up work. The current CI jobs do not provide that.