diff --git a/.github/actions/install-conjure/action.yml b/.github/actions/install-conjure/action.yml index 4c7f73432a..5e34eac75a 100644 --- a/.github/actions/install-conjure/action.yml +++ b/.github/actions/install-conjure/action.yml @@ -31,7 +31,7 @@ runs: shell: bash - name: Check that conjure is installed correctly - run: | + run: | if [ "$(conjure --version | head -n2 | tail -n1)" != "Release version ${{ inputs.version }}" ]; then echo "Correct conjure not found in path." conjure --version diff --git a/.github/workflows/code-coverage-deploy.yml b/.github/workflows/code-coverage-deploy.yml index 4de2eff5e2..0f88b11b4d 100644 --- a/.github/workflows/code-coverage-deploy.yml +++ b/.github/workflows/code-coverage-deploy.yml @@ -16,12 +16,12 @@ env: rust_release: nightly jobs: - deploy-coverage: + deploy-coverage: name: "Info: Code Coverage" runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - + - name: Set shas id: sha uses: actions/github-script@v6 @@ -37,10 +37,10 @@ jobs: console.log(callee_run); // from manual inspection in jq, seems to hold head.sha for PR, not - // whatever GITHUB_SHA is. + // whatever GITHUB_SHA is. return callee_run.head_sha; - + - name: Download artifact uses: dawidd6/action-download-artifact@v2 with: @@ -48,14 +48,14 @@ jobs: workflow: code-coverage.yml path: ./deploy - - name: Deploy to Github Pages + - name: Deploy to Github Pages uses: JamesIves/github-pages-deploy-action@v4 with: folder: ./deploy target-folder: "coverage/${{ steps.sha.outputs.result }}" branch: gh-pages commit-message: "Actions: Code Coverage for ${{ steps.sha.outputs.result }}" - + indexes: needs: deploy-coverage name: "Regenerate indexes for coverage" @@ -132,7 +132,7 @@ jobs: console.log(callee_run); // from manual inspection in jq, seems to hold head.sha for PR, not - // whatever GITHUB_SHA is. + // whatever GITHUB_SHA is. return callee_run.head_sha; - name: Download artifact @@ -141,10 +141,10 @@ jobs: name: code-coverage-${{ steps.sha.outputs.result }} workflow: code-coverage.yml path: ./deploy - + - name: Get PR number id: prnum - run: | + run: | echo "num=$(cat deploy/prnumber)" > $GITHUB_OUTPUT - name: Retrieve list of artifacts for the PR @@ -164,8 +164,8 @@ jobs: name: artifact.name, created_at: artifact.created_at })); - - # WARNING: Artifacts are deleted after 90 days (or per configured retention policy) + + # WARNING: Artifacts are deleted after 90 days (or per configured retention policy) - name: Find previous artifact id: previous-artifact if: steps.find_artifacts.outputs.result != '[]' @@ -205,7 +205,7 @@ jobs: echo 'EOFABC' >> $GITHUB_OUTPUT - name: Get doc-coverage for main and pr - id: doccov + id: doccov run: | wget https://${{github.repository_owner}}.github.io/conjure-oxide/coverage/main/doc-coverage.txt echo "main<> $GITHUB_OUTPUT @@ -234,7 +234,7 @@ jobs: repo: context.repo.repo, comment_id: ${{ steps.fc.outputs.comment-id }} }) - + - name: Download the previous lcov.info (historical) file if: steps.find_artifacts.outputs.result != '[]' uses: actions/download-artifact@v4 @@ -294,7 +294,7 @@ jobs: issue-number: ${{ steps.prnum.outputs.num }} body: | ## Code and Documentation Coverage Report - + ### Documentation Coverage
@@ -308,7 +308,7 @@ jobs:
Click to view documentation coverage for main - + ``` ${{ steps.doccov.outputs.main }} ``` @@ -342,7 +342,7 @@ jobs: issue-number: ${{ steps.prnum.outputs.num }} body: | ## Code and Documentation Coverage Report - + ### Documentation Coverage
@@ -356,7 +356,7 @@ jobs:
Click to view documentation coverage for main - + ``` ${{ steps.doccov.outputs.main }} ``` diff --git a/.github/workflows/code-coverage-main.yml b/.github/workflows/code-coverage-main.yml index eb2e78a0b5..0216f07afa 100644 --- a/.github/workflows/code-coverage-main.yml +++ b/.github/workflows/code-coverage-main.yml @@ -26,21 +26,21 @@ jobs: - name: Install rust ${{ env.rust_release }} run: rustup update ${{ env.rust_release }} && rustup default ${{ env.rust_release }} - + - name: Disable rust-lld (to fix linkme) - run: | + run: | echo RUSTFLAGS=${RUSTFLAGS}\ -Zlinker-features=-lld >> $GITHUB_ENV echo RUSTDOCFLAGS=${RUSTDOCFLAGS}\ -Zlinker-features=-lld >> $GITHUB_ENV - uses: ./.github/actions/install-conjure - with: + with: os_arch: linux version: 2.5.1 - name: Generate coverage reports run: | ./tools/coverage.sh - + - name: Generate documentation coverage run: | RUSTDOCFLAGS='-Z unstable-options --show-coverage' cargo +nightly doc --workspace --no-deps > doc-coverage.txt @@ -55,7 +55,7 @@ jobs: cp target/debug/lcov.info deploy/ # used for diffing code coverage in PR comments cp doc-coverage.json deploy/ cp doc-coverage.txt deploy/ - + - name: Copy coverage report to /main. uses: JamesIves/github-pages-deploy-action@v4 with: diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 472dbc56e1..f8276b69b7 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -26,11 +26,11 @@ jobs: - name: Get Sha id: sha run: | - if [[ ${{ github.event_name }} == 'pull_request' ]] + if [[ ${{ github.event_name }} == 'pull_request' ]] then echo -e "sha=${{ github.event.pull_request.head.sha }}" >> "$GITHUB_OUTPUT" echo -e "sha=${{ github.event.pull_request.head.sha }}" - else + else echo -e "sha=${{ github.sha }}" >> "$GITHUB_OUTPUT" echo -e "sha=${{ github.sha }}" fi @@ -42,20 +42,20 @@ jobs: run: rustup update ${{ env.rust_release }} && rustup default ${{ env.rust_release }} - name: Disable rust-lld (to fix linkme) - run: | + run: | echo RUSTFLAGS=${RUSTFLAGS}\ -Zlinker-features=-lld >> $GITHUB_ENV echo RUSTDOCFLAGS=${RUSTDOCFLAGS}\ -Zlinker-features=-lld >> $GITHUB_ENV if: env.rust_release == 'nightly' - + - uses: ./.github/actions/install-conjure - with: + with: os_arch: linux version: 2.5.1 - + - name: Generate coverage reports run: | ./tools/coverage.sh - + - name: Generate documentation coverage run: | RUSTDOCFLAGS='-Z unstable-options --show-coverage' cargo +nightly doc --workspace --no-deps > doc-coverage.txt @@ -77,4 +77,4 @@ jobs: with: name: code-coverage-${{ steps.sha.outputs.sha }} path: deploy/** - + diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ae0963e668..59df7abb0c 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -33,18 +33,18 @@ jobs: - name: Install rust ${{ env.rust_release }} run: rustup update ${{ env.rust_release }} && rustup default ${{ env.rust_release }} - + - name: Generate documentation working-directory: . run: | ./tools/gen_docs.sh - + - name: Move all html to correct folders for deployment run: | mkdir deploy cp -r target/doc/* deploy - - name: Deploy to Github Pages + - name: Deploy to Github Pages uses: JamesIves/github-pages-deploy-action@v4 with: folder: ./deploy diff --git a/.github/workflows/essence-feature-stats.yml b/.github/workflows/essence-feature-stats.yml index 821d031a49..006550ba66 100644 --- a/.github/workflows/essence-feature-stats.yml +++ b/.github/workflows/essence-feature-stats.yml @@ -63,7 +63,7 @@ jobs: - name: Add the .nojekyll file run: touch ./web/static/.nojekyll working-directory: ./tools/essence-feature-usage-stats - + - name: Deploy to GitHub Pages if: github.event_name == 'push' # Run this step only on push events uses: JamesIves/github-pages-deploy-action@v4.4.3 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b18ed20df4..ac09aafe4f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,7 +4,7 @@ on: push: branches: - main # run for pushes to the main branch. other branches need to create a PR if they want testing. - paths: + paths: - conjure_oxide/** - solvers/** - crates/** @@ -12,7 +12,7 @@ on: - .github/workflows/test.yml - .github/workflows/code-coverage-deploy.yml pull_request: - paths: + paths: - conjure_oxide/** - solvers/** - crates/** @@ -55,7 +55,7 @@ jobs: uses: mozilla-actions/sccache-action@v0.0.3 - name: Disable rust-lld (to fix linkme) - run: | + run: | echo RUSTFLAGS=${RUSTFLAGS}\ -Zlinker-features=-lld >> $GITHUB_ENV echo RUSTDOCFLAGS=${RUSTDOCFLAGS}\ -Zlinker-features=-lld >> $GITHUB_ENV if: matrix.rust_release == 'nightly' @@ -65,10 +65,10 @@ jobs: - run: cargo build -vv --workspace - uses: ./.github/actions/install-conjure - with: + with: os_arch: ${{ matrix.release_suffix }} version: ${{ matrix.conjure_version }} - + - run: cargo test --workspace audit: diff --git a/crates/conjure_core/src/ast/expressions.rs b/crates/conjure_core/src/ast/expressions.rs index 56ae0a3e11..5cb7280eb1 100644 --- a/crates/conjure_core/src/ast/expressions.rs +++ b/crates/conjure_core/src/ast/expressions.rs @@ -6,7 +6,6 @@ use serde::{Deserialize, Serialize}; use enum_compatability_macro::document_compatibility; use uniplate::derive::Uniplate; use uniplate::Biplate; -use uniplate::Uniplate; use crate::ast::constants::Constant; use crate::ast::symbol_table::{Name, SymbolTable}; diff --git a/crates/conjure_core/src/bug.rs b/crates/conjure_core/src/bug.rs new file mode 100644 index 0000000000..26d695a37b --- /dev/null +++ b/crates/conjure_core/src/bug.rs @@ -0,0 +1,31 @@ +/// Triggers a panic with a detailed bug report message, while ensuring the panic is ignored in coverage reports. +/// +/// This macro is useful in situations where an unreachable code path is hit or when a bug occurs. +/// +/// # Parameters +/// +/// - `msg`: A string expression describing the cause of the panic or bug. +/// +/// ``` +#[macro_export] +macro_rules! bug { + ($msg:expr $(, $arg:tt)*) => {{ + let formatted_msg = format!($msg, $($arg)*); + let full_message = format!( + r#" +This should never happen, sorry! + +However, it did happen, so it must be a bug. Please report it to us! + +Conjure Oxide is actively developed and maintained. We will get back to you as soon as possible. + +You can help us by providing a minimal failing example. + +Issue tracker: http://github.com/conjure-cp/conjure-oxide/issues + +{} +"#, &formatted_msg); + + panic!("{}", full_message); + }}; +} diff --git a/crates/conjure_core/src/lib.rs b/crates/conjure_core/src/lib.rs index 1d19de56e8..e20fcdbe10 100644 --- a/crates/conjure_core/src/lib.rs +++ b/crates/conjure_core/src/lib.rs @@ -3,6 +3,7 @@ pub extern crate self as conjure_core; pub use model::Model; pub mod ast; +pub mod bug; pub mod context; pub mod error; pub mod metadata; diff --git a/crates/conjure_core/src/parse/parse_model.rs b/crates/conjure_core/src/parse/parse_model.rs index 1ec2889ab7..c361395221 100644 --- a/crates/conjure_core/src/parse/parse_model.rs +++ b/crates/conjure_core/src/parse/parse_model.rs @@ -5,6 +5,7 @@ use serde_json::Value; use serde_json::Value as JsonValue; use crate::ast::{Constant, DecisionVariable, Domain, Expression, Name, Range}; +use crate::bug; use crate::context::Context; use crate::error::{Error, Result}; use crate::metadata::Metadata; @@ -46,9 +47,7 @@ pub fn model_from_json(str: &str, context: Arc>>) -> Res "SuchThat" => { let constraints_arr = match entry.1.as_array() { Some(x) => x, - None => { - return Err(Error::Parse("SuchThat is not a vector".to_owned())); - } + None => bug!("SuchThat is not a vector"), }; let constraints: Vec = @@ -56,7 +55,7 @@ pub fn model_from_json(str: &str, context: Arc>>) -> Res m.add_constraints(constraints); // println!("Nb constraints {}", m.constraints.len()); } - otherwise => panic!("Unhandled Statement {:#?}", otherwise), + otherwise => bug!("Unhandled Statement {:#?}", otherwise), } } @@ -87,7 +86,7 @@ fn parse_variable(v: &JsonValue) -> Result<(Name, DecisionVariable)> { "DomainInt" => Ok(parse_int_domain(domain.1)?), "DomainBool" => Ok(Domain::BoolDomain), _ => Err(Error::Parse( - "FindOrGiven[2] is an unknown object".to_owned(), + "FindOrGiven[2] is an unknown object".to_owned(), // consider covered )), }?; Ok((name, DecisionVariable { domain })) diff --git a/crates/conjure_core/src/rules/minion.rs b/crates/conjure_core/src/rules/minion.rs index 48a5d4d457..83d66d2dae 100644 --- a/crates/conjure_core/src/rules/minion.rs +++ b/crates/conjure_core/src/rules/minion.rs @@ -150,7 +150,7 @@ fn sum_eq_to_sumeq(expr: &Expr, _: &Model) -> ApplicationResult { #[register_rule(("Minion", 4400))] fn sumeq_to_minion(expr: &Expr, _: &Model) -> ApplicationResult { match expr { - Expr::SumEq(metadata, exprs, eq_to) => Ok(Reduction::pure(Expr::And( + Expr::SumEq(_metadata, exprs, eq_to) => Ok(Reduction::pure(Expr::And( Metadata::new(), vec![ Expr::SumGeq(Metadata::new(), exprs.clone(), Box::from(*eq_to.clone())), diff --git a/tools/coverage.sh b/tools/coverage.sh index 8c0d1acd04..29604b40d7 100755 --- a/tools/coverage.sh +++ b/tools/coverage.sh @@ -76,11 +76,11 @@ echo_err "info: running tests" cargo +nightly test --workspace echo_err "info: generating coverage reports" -grcov . -s . --binary-path ./target/debug -t html --ignore-not-existing --ignore "$HOME"'/.cargo/**/*.rs' --ignore 'target/**/*.rs' --ignore '**/main.rs' --ignore '**/build.rs' -o ./target/debug/coverage || { echo_err "fatal: html coverage generation failed" ; exit 1; } +grcov . -s . --binary-path ./target/debug -t html --ignore-not-existing --ignore "$HOME"'/.cargo/**/*.rs' --ignore 'target/**/*.rs' --ignore '**/main.rs' --ignore '**/build.rs' --excl-line 'consider covered|bug!' -o ./target/debug/coverage || { echo_err "fatal: html coverage generation failed" ; exit 1; } echo_err "info: html coverage report generated to target/debug/coverage/index.html" -grcov . -s . --binary-path ./target/debug -t lcov --ignore-not-existing --ignore "$HOME"'/.cargo/**/*.rs' --ignore 'target/**/*.rs' --ignore '**/tests/*.rs' --ignore '.cargo/**/*.rs' --ignore '**/main.rs' --ignore '**/build.rs' -o ./target/debug/lcov.info || { echo_err "fatal: lcov coverage generation failed" ; exit 1; } +grcov . -s . --binary-path ./target/debug -t lcov --ignore-not-existing --ignore "$HOME"'/.cargo/**/*.rs' --ignore 'target/**/*.rs' --ignore '**/tests/*.rs' --ignore '.cargo/**/*.rs' --ignore '**/main.rs' --ignore '**/build.rs' --excl-line 'consider covered|bug!' -o ./target/debug/lcov.info || { echo_err "fatal: lcov coverage generation failed" ; exit 1; } echo_err "info: lcov coverage report generated to target/debug/lcov.info"