From dc4f43322b9545380d71dbd021fcd18f101ef5e0 Mon Sep 17 00:00:00 2001 From: Eli Date: Thu, 16 Jul 2026 23:43:25 -0300 Subject: [PATCH 1/2] Add SonarCloud analysis to CI Adds a sonarqube job to the GitHub Actions workflow that runs Clippy and coverage, then feeds the reports to SonarCloud's Rust analyzer, plus a quality gate badge in the README. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/ci.yml | 37 +++++++++++++++++++++++++++++++++++++ README.md | 1 + sonar-project.properties | 9 +++++++++ 3 files changed, 47 insertions(+) create mode 100644 sonar-project.properties diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa502c9..c85adc8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,3 +50,40 @@ jobs: - name: Format check run: cargo fmt --check + + sonarqube: + name: SonarQube + needs: test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: clippy, llvm-tools-preview + + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Generate Clippy report + run: cargo clippy --features alloc --message-format=json &> clippy-report.json + + - name: Generate coverage report + run: cargo llvm-cov --features alloc --lcov --output-path lcov.info + + - name: SonarQube Scan + uses: SonarSource/sonarqube-scan-action@v5 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/README.md b/README.md index 84055c2..de8d6f1 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ [![Crates.io](https://img.shields.io/crates/v/rustebra?style=flat-square&color=fc8d62)](https://crates.io/crates/rustebra) [![docs.rs](https://img.shields.io/docsrs/rustebra?style=flat-square&label=docs.rs)](https://docs.rs/rustebra) [![CI](https://img.shields.io/github/actions/workflow/status/tec-eli/rustebra/ci.yml?style=flat-square&label=CI)](https://github.com/tec-eli/rustebra/actions/workflows/ci.yml) +[![Quality gate](https://sonarcloud.io/api/project_badges/quality_gate?project=tec-eli_rustebra)](https://sonarcloud.io/summary/new_code?id=tec-eli_rustebra) [![License](https://img.shields.io/badge/license-Apache_2.0-blue?style=flat-square)](https://opensource.org/licenses/Apache-2.0) [![no_std](https://img.shields.io/badge/no__std-compatible-success?style=flat-square)](https://docs.rust-embedded.org/book/) [![MSRV](https://img.shields.io/badge/MSRV-1.85-orange?style=flat-square)](https://blog.rust-lang.org/2025/02/20/Rust-1.85.0.html) diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..5a2319d --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,9 @@ +sonar.projectKey=tec-eli_rustebra +sonar.organization=tec-eli +sonar.projectName=rustebra +sonar.sources=src +sonar.tests=tests + +sonar.rust.clippy.enabled=false +sonar.rust.clippy.reportPaths=clippy-report.json +sonar.rust.lcov.reportPaths=lcov.info From b54b64063340a77614a82f99bc26bfa3c0f2b811 Mon Sep 17 00:00:00 2001 From: Eli Date: Thu, 16 Jul 2026 23:50:59 -0300 Subject: [PATCH 2/2] - Fixed format --- src/krylov/arnoldi.rs | 42 ++++++++++++++++++++++++++------ tests/edge_cases/krylov.rs | 4 ++- tests/numerical_stress.rs | 4 ++- tests/property/krylov/arnoldi.rs | 4 ++- tests/property/krylov/mod.rs | 3 +-- 5 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/krylov/arnoldi.rs b/src/krylov/arnoldi.rs index dd4aa7e..8987450 100644 --- a/src/krylov/arnoldi.rs +++ b/src/krylov/arnoldi.rs @@ -282,9 +282,21 @@ mod tests { // Upper Hessenberg: zero strictly below the first subdiagonal. assert_close(h.entry(2, 0).unwrap(), 0.0, 1e-10); - assert_close(projection_entry(&a, 3, &basis, 0, 0), h.entry(0, 0).unwrap(), 1e-10); - assert_close(projection_entry(&a, 3, &basis, 1, 0), h.entry(1, 0).unwrap(), 1e-10); - assert_close(projection_entry(&a, 3, &basis, 2, 1), h.entry(2, 1).unwrap(), 1e-10); + assert_close( + projection_entry(&a, 3, &basis, 0, 0), + h.entry(0, 0).unwrap(), + 1e-10, + ); + assert_close( + projection_entry(&a, 3, &basis, 1, 0), + h.entry(1, 0).unwrap(), + 1e-10, + ); + assert_close( + projection_entry(&a, 3, &basis, 2, 1), + h.entry(2, 1).unwrap(), + 1e-10, + ); } #[test] @@ -313,10 +325,26 @@ mod tests { let inner: f64 = q_0.iter().zip(q_1.iter()).map(|(x, y)| x * y).sum(); assert_close(inner, 0.0, 1e-12); - assert_close(projection_entry(&a, 3, &basis, 0, 0), h.entry(0, 0).unwrap(), 1e-10); - assert_close(projection_entry(&a, 3, &basis, 0, 1), h.entry(0, 1).unwrap(), 1e-10); - assert_close(projection_entry(&a, 3, &basis, 1, 0), h.entry(1, 0).unwrap(), 1e-10); - assert_close(projection_entry(&a, 3, &basis, 1, 1), h.entry(1, 1).unwrap(), 1e-10); + assert_close( + projection_entry(&a, 3, &basis, 0, 0), + h.entry(0, 0).unwrap(), + 1e-10, + ); + assert_close( + projection_entry(&a, 3, &basis, 0, 1), + h.entry(0, 1).unwrap(), + 1e-10, + ); + assert_close( + projection_entry(&a, 3, &basis, 1, 0), + h.entry(1, 0).unwrap(), + 1e-10, + ); + assert_close( + projection_entry(&a, 3, &basis, 1, 1), + h.entry(1, 1).unwrap(), + 1e-10, + ); } #[test] diff --git a/tests/edge_cases/krylov.rs b/tests/edge_cases/krylov.rs index 43aeec0..d7932e6 100644 --- a/tests/edge_cases/krylov.rs +++ b/tests/edge_cases/krylov.rs @@ -3,7 +3,9 @@ //! dimension mismatches, and the zero vector — cases the property harness deliberately never //! generates. -use rustebra::krylov::{ConvergenceError, arnoldi, inverse_power_iteration, lanczos, power_iteration}; +use rustebra::krylov::{ + ConvergenceError, arnoldi, inverse_power_iteration, lanczos, power_iteration, +}; use rustebra::storage::{Basis, StaticStorage}; use crate::common::{ diff --git a/tests/numerical_stress.rs b/tests/numerical_stress.rs index 98b9e1e..77b42a6 100644 --- a/tests/numerical_stress.rs +++ b/tests/numerical_stress.rs @@ -8,7 +8,9 @@ #[allow(dead_code)] mod common; -use rustebra::krylov::{ConvergenceError, arnoldi, inverse_power_iteration, lanczos, power_iteration}; +use rustebra::krylov::{ + ConvergenceError, arnoldi, inverse_power_iteration, lanczos, power_iteration, +}; use rustebra::storage::{Basis, StaticStorage}; use common::{ diff --git a/tests/property/krylov/arnoldi.rs b/tests/property/krylov/arnoldi.rs index fea0988..4ee66ca 100644 --- a/tests/property/krylov/arnoldi.rs +++ b/tests/property/krylov/arnoldi.rs @@ -7,7 +7,9 @@ use proptest::prelude::*; use rustebra::krylov::arnoldi; use rustebra::storage::{Basis, StaticStorage}; -use super::common::{ALGORITHM_TOL, ASSERTION_TOL, N, nonsymmetric_with_spectrum, spectrum_with_gap}; +use super::common::{ + ALGORITHM_TOL, ASSERTION_TOL, N, nonsymmetric_with_spectrum, spectrum_with_gap, +}; /// `Qᵗ * A * Q` entry `(r, c)` from the basis, against the row-major `N x N` matrix `a`. fn projection_entry(a: &[f64; N * N], basis: &Basis<'_, f64, N>, r: usize, c: usize) -> f64 { diff --git a/tests/property/krylov/mod.rs b/tests/property/krylov/mod.rs index 3ae4c74..a131948 100644 --- a/tests/property/krylov/mod.rs +++ b/tests/property/krylov/mod.rs @@ -1,9 +1,8 @@ // The shared harness also serves the edge-case and stress targets; the fixed-matrix helpers // are unused in this one. +mod arnoldi; #[allow(dead_code)] mod common; -mod arnoldi; mod inverse_power_iteration; mod lanczos; mod power_iteration; -