From 79898e48ed9f94cd65da627f038e572c0d22637c Mon Sep 17 00:00:00 2001 From: dance858 Date: Fri, 28 Aug 2026 09:49:46 -0700 Subject: [PATCH] tighten CI and fix test --- .github/workflows/formatting.yml | 25 +++++++------- .github/workflows/guard-malloc.yml | 28 ++++++++++++++++ .github/workflows/sanitizer.yml | 14 ++++---- .../test_elementwise_mult.h | 33 ++++++++++--------- tests/test_helpers.c | 33 +++++++++++++++++++ tests/test_helpers.h | 5 +++ tests/utils/test_csr_matrix.h | 22 +++++++------ 7 files changed, 115 insertions(+), 45 deletions(-) create mode 100644 .github/workflows/guard-malloc.yml diff --git a/.github/workflows/formatting.yml b/.github/workflows/formatting.yml index 5db2ae95..24e518c6 100644 --- a/.github/workflows/formatting.yml +++ b/.github/workflows/formatting.yml @@ -8,27 +8,24 @@ on: jobs: format: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [macos-latest] + runs-on: macos-latest steps: - uses: actions/checkout@v5 - # Install clang-format if needed - - name: Install clang-format (Linux) - if: matrix.os == 'ubuntu-latest' - run: sudo apt-get update && sudo apt-get install -y clang-format - - - name: Install clang-format (macOS) - if: matrix.os == 'macos-latest' + - name: Install clang-format run: brew install clang-format || true - # Check formatting - - name: Check C/C++ formatting + # Fail loudly if clang-format is missing — otherwise the check below + # would pass vacuously on empty output. + - name: Verify clang-format is available + run: clang-format --version + + # The \( ... \) grouping matters: without it, -print0 binds only to + # the *.h branch and .c files are never checked. + - name: Check C formatting run: | - misformatted=$(find . -name '*.c' -o -name '*.h' -print0 | xargs -0 clang-format -style=file -output-replacements-xml | grep " + MallocScribble=1 MallocGuardEdges=1 + leaks --atExit -- ./build/all_tests diff --git a/.github/workflows/sanitizer.yml b/.github/workflows/sanitizer.yml index 86f3bbc3..de21eca0 100644 --- a/.github/workflows/sanitizer.yml +++ b/.github/workflows/sanitizer.yml @@ -20,17 +20,19 @@ jobs: if: runner.os == 'Linux' run: sudo apt-get update && sudo apt-get install -y libopenblas-dev - # Configure CMake with ASan + UBSan + # Note: a plain-scalar `run: cmake ... \` with backslash continuations + # folds into one line with the backslashes kept, silently dropping the + # -D flags (the job then built a plain default-config binary with no + # sanitizers). The > folded style below makes the flags actually apply. - name: Configure with sanitizers - run: cmake -B build -S . \ - -DCMAKE_BUILD_TYPE=Debug \ - -DCMAKE_C_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -g" \ + run: > + cmake -B build -S . + -DCMAKE_BUILD_TYPE=Debug + -DCMAKE_C_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -g" -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -g" - # Build - name: Build run: cmake --build build --config Debug - # Run tests - name: Run tests under ASan+UBSan run: ./build/all_tests diff --git a/tests/jacobian_tests/bivariate_full_dom/test_elementwise_mult.h b/tests/jacobian_tests/bivariate_full_dom/test_elementwise_mult.h index e7a1c3b3..b42dbe84 100644 --- a/tests/jacobian_tests/bivariate_full_dom/test_elementwise_mult.h +++ b/tests/jacobian_tests/bivariate_full_dom/test_elementwise_mult.h @@ -64,13 +64,14 @@ const char *test_jacobian_elementwise_mult_3(void) 0 0 1 1 3 0 0 0 0 0 0 0 1 -1 1 0 0 0 0 0] */ - CSR_matrix *A = new_CSR_matrix(3, 10, 9); - double Ax_vals[9] = {1.0, 2.0, 1.0, 1.0, 3.0, 1.0, -1.0, 1.0}; - int Ai[9] = {2, 3, 2, 3, 4, 2, 3, 4}; + CSR_matrix *A = new_CSR_matrix(3, 10, 8); + double Ax_vals[8] = {1.0, 2.0, 1.0, 1.0, 3.0, 1.0, -1.0, 1.0}; + int Ai[8] = {2, 3, 2, 3, 4, 2, 3, 4}; int Ap[4] = {0, 2, 5, 8}; - memcpy(A->x, Ax_vals, 9 * sizeof(double)); - memcpy(A->i, Ai, 9 * sizeof(int)); + memcpy(A->x, Ax_vals, 8 * sizeof(double)); + memcpy(A->i, Ai, 8 * sizeof(int)); memcpy(A->p, Ap, 4 * sizeof(int)); + mu_assert("A fixture invalid", csr_is_valid(A)); /* B = [0 0 0 0 0 0 0 1 3 0 @@ -78,13 +79,14 @@ const char *test_jacobian_elementwise_mult_3(void) 0 0 0 0 0 0 0 1 -2 1] */ - CSR_matrix *B = new_CSR_matrix(3, 10, 9); - double Bx_vals[9] = {1.0, 3.0, 1.0, 1.0, 4.0, 1.0, -2.0, 1.0}; - int Bi[9] = {7, 8, 7, 8, 9, 7, 8, 9}; + CSR_matrix *B = new_CSR_matrix(3, 10, 8); + double Bx_vals[8] = {1.0, 3.0, 1.0, 1.0, 4.0, 1.0, -2.0, 1.0}; + int Bi[8] = {7, 8, 7, 8, 9, 7, 8, 9}; int Bp[4] = {0, 2, 5, 8}; - memcpy(B->x, Bx_vals, 9 * sizeof(double)); - memcpy(B->i, Bi, 9 * sizeof(int)); + memcpy(B->x, Bx_vals, 8 * sizeof(double)); + memcpy(B->i, Bi, 8 * sizeof(int)); memcpy(B->p, Bp, 4 * sizeof(int)); + mu_assert("B fixture invalid", csr_is_valid(B)); double u_vals[10] = {0, 0, 1.0, 2.0, 3.0, 0, 0, 4.0, 5.0, 6.0}; expr *x = new_variable(3, 1, 2, 10); @@ -160,13 +162,14 @@ const char *test_jacobian_elementwise_mult_4(void) 0 0 1 1 3 0 0 0 0 0 0 0 1 -1 1 0 0 0 0 0] */ - CSR_matrix *A = new_CSR_matrix(3, 10, 9); - double Ax_vals[9] = {1.0, 2.0, 1.0, 1.0, 3.0, 1.0, -1.0, 1.0}; - int Ai[9] = {2, 3, 2, 3, 4, 2, 3, 4}; + CSR_matrix *A = new_CSR_matrix(3, 10, 8); + double Ax_vals[8] = {1.0, 2.0, 1.0, 1.0, 3.0, 1.0, -1.0, 1.0}; + int Ai[8] = {2, 3, 2, 3, 4, 2, 3, 4}; int Ap[4] = {0, 2, 5, 8}; - memcpy(A->x, Ax_vals, 9 * sizeof(double)); - memcpy(A->i, Ai, 9 * sizeof(int)); + memcpy(A->x, Ax_vals, 8 * sizeof(double)); + memcpy(A->i, Ai, 8 * sizeof(int)); memcpy(A->p, Ap, 4 * sizeof(int)); + mu_assert("A fixture invalid", csr_is_valid(A)); double u_vals[10] = {0, 0, 1.0, 2.0, 3.0, 0, 0, 4.0, 5.0, 6.0}; expr *x = new_variable(3, 1, 2, 10); diff --git a/tests/test_helpers.c b/tests/test_helpers.c index 7d5244a2..5e2498c0 100644 --- a/tests/test_helpers.c +++ b/tests/test_helpers.c @@ -71,6 +71,39 @@ int cmp_values(const matrix *M, const double *exp_x, int nnz) return cmp_double_array(M->x, exp_x, nnz); } +int csr_is_valid(const CSR_matrix *A) +{ + if (A->p[0] != 0) + { + printf(" FAILED: p[0] = %d, expected 0\n", A->p[0]); + return 0; + } + for (int i = 0; i < A->m; i++) + { + if (A->p[i] > A->p[i + 1]) + { + printf(" FAILED: p[%d] = %d > p[%d] = %d\n", i, A->p[i], i + 1, + A->p[i + 1]); + return 0; + } + } + if (A->p[A->m] != A->nnz) + { + printf(" FAILED: p[m] = %d, but nnz = %d\n", A->p[A->m], A->nnz); + return 0; + } + for (int jj = 0; jj < A->nnz; jj++) + { + if (A->i[jj] < 0 || A->i[jj] >= A->n) + { + printf(" FAILED: i[%d] = %d out of range [0, %d)\n", jj, A->i[jj], + A->n); + return 0; + } + } + return 1; +} + #ifndef M_PI #define M_PI 3.14159265358979323846 #endif diff --git a/tests/test_helpers.h b/tests/test_helpers.h index b3e96cef..a16ec8d5 100644 --- a/tests/test_helpers.h +++ b/tests/test_helpers.h @@ -22,6 +22,11 @@ int cmp_sparsity(matrix *M, const int *exp_p, const int *exp_i, int m, int nnz); * length nnz. Returns 1 on full match, 0 otherwise. */ int cmp_values(const matrix *M, const double *exp_x, int nnz); +/* Check the CSR invariants: p[0] == 0, p nondecreasing, p[m] == nnz, and + * all column indices in [0, n). Returns 1 if valid, 0 otherwise. Use on + * hand-built fixtures before handing them to the code under test. */ +int csr_is_valid(const CSR_matrix *A); + /* Create a random m x n CSR_matrix matrix with approximate nonzero density * in [0, 1]. Nonzero values are standard Gaussian (Box-Muller). */ CSR_matrix *new_csr_random(int m, int n, double density); diff --git a/tests/utils/test_csr_matrix.h b/tests/utils/test_csr_matrix.h index 33c11a01..04a281b3 100644 --- a/tests/utils/test_csr_matrix.h +++ b/tests/utils/test_csr_matrix.h @@ -260,9 +260,9 @@ const char *test_sum_block_of_rows_csr(void) * * Result C should be 3x4 matrix with the sums above */ - CSR_matrix *A = new_CSR_matrix(9, 4, 18); + CSR_matrix *A = new_CSR_matrix(9, 4, 16); - double Ax[18] = {1.0, 2.0, /* row 0 */ + double Ax[16] = {1.0, 2.0, /* row 0 */ 3.0, 1.0, /* row 1 */ 4.0, 5.0, /* row 2 */ 2.0, 1.0, /* row 3 */ @@ -272,7 +272,7 @@ const char *test_sum_block_of_rows_csr(void) 1.0, 3.0, /* row 7 */ 2.0, 6.0}; /* row 8 */ - int Ai[18] = {0, 1, /* row 0 */ + int Ai[16] = {0, 1, /* row 0 */ 1, 2, /* row 1 */ 2, 3, /* row 2 */ 0, 3, /* row 3 */ @@ -284,9 +284,10 @@ const char *test_sum_block_of_rows_csr(void) int Ap[10] = {0, 2, 4, 6, 8, 10, 11, 12, 14, 16}; - memcpy(A->x, Ax, 18 * sizeof(double)); - memcpy(A->i, Ai, 18 * sizeof(int)); + memcpy(A->x, Ax, 16 * sizeof(double)); + memcpy(A->i, Ai, 16 * sizeof(int)); memcpy(A->p, Ap, 10 * sizeof(int)); + mu_assert("A fixture invalid", csr_is_valid(A)); /* Allocate C for 3 blocks and enough space for all nonzeros */ CSR_matrix *C = new_CSR_matrix(3, 4, 12); @@ -339,9 +340,9 @@ const char *test_sum_evenly_spaced_rows_csr(void) row 1: sum of rows 1, 4, 7 = [1 4 6 0] row 2: sum of rows 2, 5, 8 = [3 2 4 11] */ - CSR_matrix *A = new_CSR_matrix(9, 4, 18); + CSR_matrix *A = new_CSR_matrix(9, 4, 16); - double Ax[18] = {1.0, 2.0, /* row 0 */ + double Ax[16] = {1.0, 2.0, /* row 0 */ 3.0, 1.0, /* row 1 */ 4.0, 5.0, /* row 2 */ 2.0, 1.0, /* row 3 */ @@ -351,7 +352,7 @@ const char *test_sum_evenly_spaced_rows_csr(void) 1.0, 3.0, /* row 7 */ 2.0, 6.0}; /* row 8 */ - int Ai[18] = {0, 1, /* row 0 */ + int Ai[16] = {0, 1, /* row 0 */ 1, 2, /* row 1 */ 2, 3, /* row 2 */ 0, 3, /* row 3 */ @@ -363,9 +364,10 @@ const char *test_sum_evenly_spaced_rows_csr(void) int Ap[10] = {0, 2, 4, 6, 8, 10, 11, 12, 14, 16}; - memcpy(A->x, Ax, 18 * sizeof(double)); - memcpy(A->i, Ai, 18 * sizeof(int)); + memcpy(A->x, Ax, 16 * sizeof(double)); + memcpy(A->i, Ai, 16 * sizeof(int)); memcpy(A->p, Ap, 10 * sizeof(int)); + mu_assert("A fixture invalid", csr_is_valid(A)); /* Allocate C for 3 rows (row_spacing=3) and enough space for all nonzeros */ CSR_matrix *C = new_CSR_matrix(3, 4, 10);