From 02c86dd6b3ece6cc8b5089086bf1218683781327 Mon Sep 17 00:00:00 2001 From: Per Larsen Date: Mon, 7 Sep 2026 20:33:31 -0700 Subject: [PATCH 1/5] tests: integration: add `sqlite` submodule Pin SQLite 3.53.4 from the official source mirror to provide the canonical source tree and upstream workloads for integration coverage. --- .gitmodules | 5 +++++ tests/integration/tests/sqlite/repo | 1 + 2 files changed, 6 insertions(+) create mode 160000 tests/integration/tests/sqlite/repo diff --git a/.gitmodules b/.gitmodules index d13de09b28..9c42861074 100644 --- a/.gitmodules +++ b/.gitmodules @@ -106,3 +106,8 @@ url = https://github.com/libgit2/libgit2 ignore = all update = none +[submodule "tests/integration/tests/sqlite/repo"] + path = tests/integration/tests/sqlite/repo + url = https://github.com/sqlite/sqlite.git + update = none + ignore = all diff --git a/tests/integration/tests/sqlite/repo b/tests/integration/tests/sqlite/repo new file mode 160000 index 0000000000..b09c88c140 --- /dev/null +++ b/tests/integration/tests/sqlite/repo @@ -0,0 +1 @@ +Subproject commit b09c88c14082339b66c7b7158d609a771e64ca69 From 6effa8dba7f4eaafccc72977b151994183cfe925 Mon Sep 17 00:00:00 2001 From: Per Larsen Date: Mon, 7 Sep 2026 20:33:31 -0700 Subject: [PATCH 2/5] tests: integration: add `sqlite` transpile support Build libsqlite3.a with --disable-amalgamation so the integration harness translates the individual library source files. Generate sources before recording the compilation database to exclude host build tools. Enable R-tree support for the spatial workload. --- tests/integration/tests/sqlite/README.md | 23 +++++++++++++++++++++ tests/integration/tests/sqlite/conf.yml | 6 ++++++ tests/integration/tests/sqlite/configure.sh | 9 ++++++++ tests/integration/tests/sqlite/make.sh | 14 +++++++++++++ 4 files changed, 52 insertions(+) create mode 100644 tests/integration/tests/sqlite/README.md create mode 100644 tests/integration/tests/sqlite/conf.yml create mode 100755 tests/integration/tests/sqlite/configure.sh create mode 100755 tests/integration/tests/sqlite/make.sh diff --git a/tests/integration/tests/sqlite/README.md b/tests/integration/tests/sqlite/README.md new file mode 100644 index 0000000000..33dcaf62e0 --- /dev/null +++ b/tests/integration/tests/sqlite/README.md @@ -0,0 +1,23 @@ +# SQLite + +The source submodule is pinned to SQLite 3.53.4 from the official Git mirror. +Initialize it with: + +```sh +git submodule update --init --checkout tests/integration/tests/sqlite/repo +``` + +Run from the C2Rust checkout with the integration harness prerequisites installed: + +```sh +export PATH="$PWD/target/release:$PWD/c2rust-postprocess:$PATH" +export C2RUST_DIR="$PWD" +./tests/integration/test.py sqlite +``` + +The build uses `--disable-amalgamation` and the `libsqlite3.a` target to compile +the library's individual translation units. SQLite's `sqlite3` and `speedtest1` +make targets compile the amalgamation even with that configure option, so they +are deliberately not used. Source generation runs before Bear to exclude build +tools from the compilation database. R-tree support is enabled for its workload. + diff --git a/tests/integration/tests/sqlite/conf.yml b/tests/integration/tests/sqlite/conf.yml new file mode 100644 index 0000000000..6d2720ec42 --- /dev/null +++ b/tests/integration/tests/sqlite/conf.yml @@ -0,0 +1,6 @@ +transpile: + autogen: true + tflags: --reorganize-definitions --disable-refactoring + +cargo.transpile: + autogen: true diff --git a/tests/integration/tests/sqlite/configure.sh b/tests/integration/tests/sqlite/configure.sh new file mode 100755 index 0000000000..ed8dacac7b --- /dev/null +++ b/tests/integration/tests/sqlite/configure.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +cd "$SCRIPT_DIR/repo" + +# Compile separate translation units to exercise cross-file translation. +./configure --disable-amalgamation --disable-shared --disable-readline \ + --disable-tcl --enable-rtree 2>&1 | tee "$SCRIPT_DIR/$(basename "$0").log" diff --git a/tests/integration/tests/sqlite/make.sh b/tests/integration/tests/sqlite/make.sh new file mode 100755 index 0000000000..59c1ca92ea --- /dev/null +++ b/tests/integration/tests/sqlite/make.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +cd "$SCRIPT_DIR" + +{ + make -C repo clean + rm -f compile_commands.json + # Generate the parser, opcodes and headers before recording compilation, + # so host tools such as lemon are not included in the translated library. + make -C repo -j"$(nproc)" .target_source + bear -- make -C repo -j"$(nproc)" libsqlite3.a +} 2>&1 | tee "$SCRIPT_DIR/$(basename "$0").log" From dcbc1e0837aef1b0aadfa47e46639917fb1a8a69 Mon Sep 17 00:00:00 2001 From: Per Larsen Date: Mon, 7 Sep 2026 20:33:31 -0700 Subject: [PATCH 3/5] tests: integration: add `sqlite` refactor support Run definition reorganization and the name, import, label, cast, and literal cleanup transforms on translated SQLite, then compile the result to exercise refactoring across the library modules. --- tests/integration/tests/sqlite/conf.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/integration/tests/sqlite/conf.yml b/tests/integration/tests/sqlite/conf.yml index 6d2720ec42..3ec76ff56e 100644 --- a/tests/integration/tests/sqlite/conf.yml +++ b/tests/integration/tests/sqlite/conf.yml @@ -4,3 +4,16 @@ transpile: cargo.transpile: autogen: true + +refactor: + autogen: true + transforms: + - rename_unnamed + - reorganize_definitions + - shorten_imported_paths + - remove_unused_labels + - remove_redundant_casts + - remove_literal_suffixes + +cargo.refactor: + autogen: true From 4ccf7fde2a9a35b000ad33eb21d43210419cd61b Mon Sep 17 00:00:00 2001 From: Per Larsen Date: Mon, 7 Sep 2026 20:33:31 -0700 Subject: [PATCH 4/5] tests: integration: add `sqlite` test support Run five upstream speedtest workloads against native and Rust archives before and after refactoring, comparing result counts and hashes. Use the same native C callers to exercise both libraries' public C ABI. Add always-active SQL checks for transactions, triggers, foreign keys, blobs, JSON/JSONB, numeric parsing, window functions, and integrity. Check data and WAL journal-mode persistence after reopening. Explicit JSON and numeric assertions replace upstream workloads that do not hash results or reliably report SQL errors. Use jq to read Cargo's configured target directory and rustc to determine the Rust archive's native link dependencies. --- tests/integration/tests/sqlite/README.md | 19 ++++- tests/integration/tests/sqlite/conf.yml | 10 +++ tests/integration/tests/sqlite/smoke.c | 91 ++++++++++++++++++++++++ tests/integration/tests/sqlite/test.sh | 58 +++++++++++++++ 4 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 tests/integration/tests/sqlite/smoke.c create mode 100755 tests/integration/tests/sqlite/test.sh diff --git a/tests/integration/tests/sqlite/README.md b/tests/integration/tests/sqlite/README.md index 33dcaf62e0..94b3d562d9 100644 --- a/tests/integration/tests/sqlite/README.md +++ b/tests/integration/tests/sqlite/README.md @@ -7,7 +7,8 @@ Initialize it with: git submodule update --init --checkout tests/integration/tests/sqlite/repo ``` -Run from the C2Rust checkout with the integration harness prerequisites installed: +Run from the C2Rust checkout with the integration harness prerequisites and `jq` +installed: ```sh export PATH="$PWD/target/release:$PWD/c2rust-postprocess:$PATH" @@ -21,3 +22,19 @@ make targets compile the amalgamation even with that configure option, so they are deliberately not used. Source generation runs before Bear to exclude build tools from the compilation database. R-tree support is enabled for its workload. +After transpilation and again after refactoring, `test.sh` compiles SQLite's +upstream `test/speedtest1.c` against each of the native and translated static +libraries. It runs the `main`, `cte`, `orm`, `fp`, and `rtree` workloads with +`--size 1 --verify` on temporary on-disk databases. +Each workload must exit successfully and produce the same result byte count and +verification hash as native SQLite. Timing output is ignored. The C test driver +stays native so the same caller exercises both libraries' public C ABI. +The Rust archive's native link dependencies come from rustc's +`--print native-static-libs` output. + +`smoke.c` also checks fixed SQL results, transaction/savepoint rollback, triggers, +foreign keys, blobs, JSON/JSONB operations, numeric parsing (including large +integers and floating-point values), window functions, data and WAL journal-mode +persistence after reopening, and an integrity check. The upstream `json` and +`parsenumber` workloads do not hash result rows, and `parsenumber` ignores SQL +errors, so JSON and numeric parsing use these explicit assertions instead. diff --git a/tests/integration/tests/sqlite/conf.yml b/tests/integration/tests/sqlite/conf.yml index 3ec76ff56e..fe88b85230 100644 --- a/tests/integration/tests/sqlite/conf.yml +++ b/tests/integration/tests/sqlite/conf.yml @@ -1,3 +1,13 @@ +requirements: + generic: + programs: + in_path: + - jq + ubuntu: + apt: + packages: + - jq + transpile: autogen: true tflags: --reorganize-definitions --disable-refactoring diff --git a/tests/integration/tests/sqlite/smoke.c b/tests/integration/tests/sqlite/smoke.c new file mode 100644 index 0000000000..2e60d48448 --- /dev/null +++ b/tests/integration/tests/sqlite/smoke.c @@ -0,0 +1,91 @@ +#include "sqlite3.h" +#include +#include + +#define CHECK(condition) do { \ + if (!(condition)) { \ + fprintf(stderr, "%s:%d: %s failed\n", __FILE__, __LINE__, #condition); \ + exit(1); \ + } \ +} while (0) + +static void exec(sqlite3 *db, const char *sql) { + char *error = NULL; + if (sqlite3_exec(db, sql, NULL, NULL, &error) != SQLITE_OK) { + fprintf(stderr, "%s: %s\n", sql, error); + sqlite3_free(error); + exit(1); + } +} + +static void expect_int(sqlite3 *db, const char *sql, int expected) { + sqlite3_stmt *stmt = NULL; + CHECK(sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK); + CHECK(sqlite3_step(stmt) == SQLITE_ROW); + int actual = sqlite3_column_int(stmt, 0); + if (actual != expected) { + fprintf(stderr, "%s: expected %d, got %d\n", sql, expected, actual); + exit(1); + } + CHECK(sqlite3_step(stmt) == SQLITE_DONE); + CHECK(sqlite3_finalize(stmt) == SQLITE_OK); +} + +int main(int argc, char **argv) { + CHECK(argc == 2); + sqlite3 *db = NULL; + CHECK(sqlite3_open(argv[1], &db) == SQLITE_OK); + exec(db, + "PRAGMA journal_mode=WAL;" + "PRAGMA foreign_keys=ON;" + "CREATE TABLE parent(id INTEGER PRIMARY KEY);" + "CREATE TABLE child(id INTEGER REFERENCES parent, value TEXT, data BLOB);" + "CREATE TABLE audit(value TEXT);" + "CREATE TRIGGER inserted AFTER INSERT ON child BEGIN " + " INSERT INTO audit VALUES(new.value); END;" + "BEGIN;" + "INSERT INTO parent VALUES(1);" + "INSERT INTO child VALUES(1, 'hello', x'00ff80');" + "SAVEPOINT s;" + "INSERT INTO child VALUES(1, 'rolled back', NULL);" + "ROLLBACK TO s;" + "RELEASE s;" + "COMMIT;"); + expect_int(db, "SELECT journal_mode = 'wal' FROM pragma_journal_mode", 1); + expect_int(db, "SELECT count(*) FROM audit", 1); + expect_int(db, "SELECT hex(data) = '00FF80' AND value = 'hello' FROM child", 1); + CHECK(sqlite3_exec(db, "INSERT INTO child VALUES(2, 'invalid', NULL)", + NULL, NULL, NULL) == SQLITE_CONSTRAINT); + expect_int(db, "SELECT json_extract('{\"a\":[1,2,3]}', '$.a[2]')", 3); + expect_int(db, + "SELECT json(jsonb_set(jsonb('{\"a\":[1,2,3]}'), '$.a[1]', 9)) " + "= '{\"a\":[1,9,3]}'", 1); + expect_int(db, + "SELECT json(jsonb_remove(jsonb_insert(jsonb('{}'), '$.x', 7, '$.y', 8), '$.x')) " + "= '{\"y\":8}'", 1); + expect_int(db, + "WITH t(x) AS (VALUES(1),(2),(3)) " + "SELECT json(jsonb_group_array(x)) = '[1,2,3]' FROM t", 1); + expect_int(db, "SELECT CAST('1_000' AS INTEGER)", 1); + expect_int(db, "SELECT 1_000 + CAST('2.5e2' AS INTEGER)", 1002); + expect_int(db, "SELECT CAST(CAST('2.5e2' AS REAL) AS INTEGER)", 250); + expect_int(db, + "SELECT printf('%lld', 8_227_256_643_844_975_616) = '8227256643844975616' " + "AND printf('%lld', -9223372036854775808) = '-9223372036854775808'", 1); + expect_int(db, + "SELECT printf('%.6f', 8.227256643844975616) = '8.227257' " + "AND printf('%.8f', CAST('1.23456e-3' AS REAL)) = '0.00123456' " + "AND printf('%.4f', 1_234.5_6e-2) = '12.3456'", 1); + expect_int(db, + "WITH t(x) AS (VALUES(1),(2),(3)) " + "SELECT sum(s) FROM (SELECT sum(x) OVER (ORDER BY x) AS s FROM t)", 10); + CHECK(sqlite3_close(db) == SQLITE_OK); + + CHECK(sqlite3_open(argv[1], &db) == SQLITE_OK); + expect_int(db, "SELECT journal_mode = 'wal' FROM pragma_journal_mode", 1); + expect_int(db, "SELECT count(*) FROM audit", 1); + expect_int(db, "SELECT integrity_check = 'ok' FROM pragma_integrity_check", 1); + CHECK(sqlite3_close(db) == SQLITE_OK); + puts("SQLite SQL and persistence checks passed"); + return 0; +} diff --git a/tests/integration/tests/sqlite/test.sh b/tests/integration/tests/sqlite/test.sh new file mode 100755 index 0000000000..6c7e22c4bb --- /dev/null +++ b/tests/integration/tests/sqlite/test.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +cd "$SCRIPT_DIR/repo" + +{ + TARGET_DIR=$(cargo ${TOOLCHAIN:+"$TOOLCHAIN"} metadata --no-deps --format-version 1 \ + | jq -er .target_directory) + RUST_ARCHIVE="$TARGET_DIR/release/librepo.a" + if [[ ! -f "$RUST_ARCHIVE" ]]; then + echo "Rust archive not found: $RUST_ARCHIVE" >&2 + exit 1 + fi + WORK_DIR=$(mktemp -d) + trap 'rm -rf "$WORK_DIR"' EXIT + + # Ask rustc for the native dependencies of this static archive. + RUSTFLAGS="${RUSTFLAGS:--Awarnings}" cargo ${TOOLCHAIN:+"$TOOLCHAIN"} rustc \ + --release --lib --color never -- --print native-static-libs \ + 2>&1 | tee "$WORK_DIR/native-libs.log" + LIBS=$(sed -n 's/^note: native-static-libs: //p' "$WORK_DIR/native-libs.log") + if [[ -z "$LIBS" ]]; then + echo "rustc did not report native static libraries" >&2 + exit 1 + fi + read -r -a RUST_LIBS <<< "$LIBS" + + # Explicit archives keep both C callers from picking up a system SQLite. + for implementation in native rust; do + if [[ "$implementation" == native ]]; then + archive="$SCRIPT_DIR/repo/libsqlite3.a" + libs=(-lm -ldl -lpthread) + else + archive="$RUST_ARCHIVE" + libs=("${RUST_LIBS[@]}") + fi + cc -O2 -I. "$SCRIPT_DIR/smoke.c" "$archive" "${libs[@]}" \ + -o "$WORK_DIR/smoke-$implementation" + cc -O2 -DSQLITE_ENABLE_RTREE -I. test/speedtest1.c "$archive" "${libs[@]}" \ + -o "$WORK_DIR/$implementation" + "$WORK_DIR/smoke-$implementation" "$WORK_DIR/smoke-$implementation.db" + done + + # Only these workloads hash result rows. JSON and numeric parsing have + # explicit assertions in smoke.c instead of comparisons of empty hashes. + for testset in main cte orm fp rtree; do + for implementation in native rust; do + "$WORK_DIR/$implementation" --size 1 --verify --testset "$testset" \ + "$WORK_DIR/$implementation.db" \ + | tee "$WORK_DIR/$implementation.out" + # Timings differ; the result count and verification hash must match. + grep '^Verification Hash: ' "$WORK_DIR/$implementation.out" \ + > "$WORK_DIR/$implementation.hash" + done + diff -u "$WORK_DIR/native.hash" "$WORK_DIR/rust.hash" + done +} 2>&1 | tee "$SCRIPT_DIR/$(basename "$0").log" From 98b8a8efd8140add8d8f0113347a084883171a0f Mon Sep 17 00:00:00 2001 From: Per Larsen Date: Mon, 7 Sep 2026 20:33:31 -0700 Subject: [PATCH 5/5] tests: integration: enable `sqlite` in CI Check out the SQLite submodule and add it to the integration project list so both Clang configurations exercise its translation, refactoring, and runtime behavior. Install jq for the test driver's Cargo metadata lookup. --- .github/workflows/internal-testsuite.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/internal-testsuite.yml b/.github/workflows/internal-testsuite.yml index c054c8d375..9425d50828 100644 --- a/.github/workflows/internal-testsuite.yml +++ b/.github/workflows/internal-testsuite.yml @@ -54,6 +54,7 @@ jobs: git submodule update --init --checkout tests/integration/tests/libxml2/repo git submodule update --init --checkout tests/integration/tests/python2/repo git submodule update --init --checkout tests/integration/tests/libmcs/repo + git submodule update --init --checkout tests/integration/tests/sqlite/repo - uses: astral-sh/setup-uv@v6 @@ -75,6 +76,7 @@ jobs: bear \ build-essential \ cmake \ + jq \ libbrotli-dev \ libbz2-dev \ libclang-${{ matrix.clang-version }}-dev \ @@ -126,7 +128,7 @@ jobs: export PATH=$PWD/target/release:$PWD/c2rust-postprocess:$HOME/.local/bin:$PATH echo "PATH=$PATH" export C2RUST_DIR=$PWD - ./tests/integration/test.py --refactor-jobs 1 curl json-c libgit2 lua nginx zstd libxml2 python2 libmcs + ./tests/integration/test.py --refactor-jobs 1 curl json-c libgit2 lua nginx zstd libxml2 python2 libmcs sqlite - uses: actions/upload-artifact@v4 with: