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-sanitizersis 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_loadcan run. - Uses LLVM's symbolizer for source locations.
- Passes
+Mea minto 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.exsexcludes:sanitizerand: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.