Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 11 additions & 14 deletions .github/workflows/formatting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 "<replacement " || true)
misformatted=$(find . \( -name '*.c' -o -name '*.h' \) -print0 | xargs -0 clang-format -style=file -output-replacements-xml | grep "<replacement " || true)
if [ -n "$misformatted" ]; then
echo "ERROR: Some files are not properly formatted. Run clang-format -i."
exit 1
Expand Down
28 changes: 28 additions & 0 deletions .github/workflows/guard-malloc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Guard Malloc

on:
push:
branches: [main, development]
pull_request:
branches: [main, development]

jobs:
guard-malloc:
runs-on: macos-latest

steps:
- uses: actions/checkout@v5

- name: Configure
run: cmake -B build -S . -DCMAKE_BUILD_TYPE=Debug

- name: Build
run: cmake --build build --config Debug

# MallocScribble poisons freed memory (0x55), MallocGuardEdges adds
# guard pages around large allocations, and leaks --atExit reports
# memory still leaked at exit and fails the step if any is found.
- name: Run tests under guard-malloc + leaks
run: >
MallocScribble=1 MallocGuardEdges=1
leaks --atExit -- ./build/all_tests
14 changes: 8 additions & 6 deletions .github/workflows/sanitizer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
33 changes: 18 additions & 15 deletions tests/jacobian_tests/bivariate_full_dom/test_elementwise_mult.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,27 +64,29 @@ 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
0 0 0 0 0 0 0 1 1 4
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);
Expand Down Expand Up @@ -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);
Expand Down
33 changes: 33 additions & 0 deletions tests/test_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions tests/test_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
22 changes: 12 additions & 10 deletions tests/utils/test_csr_matrix.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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 */
Expand All @@ -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);
Expand Down Expand Up @@ -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 */
Expand All @@ -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 */
Expand All @@ -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);
Expand Down
Loading