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.
Install Clang and Erlang/OTP (including erl_nif.h), then run:
make c-checkThis 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.
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.shThe 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 0The 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.
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.exsAttribute 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.