cargo-mutants 27.0.0

Inject bugs and see if your tests catch them
name: Tests

permissions:
  contents: read

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

on:
  push:
    # branches:
    #   - main
    paths:
      - ".cargo/*.toml"
      - ".github/workflows/tests.yml"
      - "Cargo.*"
      - "mutants_attrs/**"
      - "src/**"
      - "testdata/**"
      - "tests/**"
  # pull_request:
  #   paths:
  #     - ".cargo/*.toml"
  #     - ".github/workflows/tests.yml"
  #     - "Cargo.*"
  #     - "mutants_attrs/**"
  #     - "src/**"
  #     - "testdata/**"
  #     - "tests/**"

# see https://matklad.github.io/2021/09/04/fast-rust-builds.html
env:
  CARGO_TERM_COLOR: always
  CARGO_INCREMENTAL: 0
  CARGO_NET_RETRY: 10
  CI: 1
  RUST_BACKTRACE: short
  RUSTFLAGS: "-W rust-2021-compatibility"
  RUSTUP_MAX_RETRIES: 10
  CARGO_MUTANTS_MINIMUM_TEST_TIMEOUT: 60

jobs:
  # Quick sanity check before anything else.
  quick-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: dtolnay/rust-toolchain@master
        with:
          toolchain: beta
          components: rustfmt
      - uses: Swatinem/rust-cache@v2
      - run: cargo check --all-targets --all-features
      - name: Check rustfmt
        run: cargo fmt --all --check
      - name: Check typos
        uses: crate-ci/typos@master

  binary:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: dtolnay/rust-toolchain@master
        with:
          toolchain: beta
      - uses: Swatinem/rust-cache@v2
      - run: cargo build --release
      - name: Upload binary artifact
        uses: actions/upload-artifact@v4
        with:
          name: cargo-mutants-linux
          path: |
            target/release/cargo-mutants

  test:
    needs: [quick-check]
    strategy:
      matrix:
        os: [macOS-latest, ubuntu-latest, windows-latest]
        version: [stable, nightly, "1.88"]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: dtolnay/rust-toolchain@master
        with:
          toolchain: ${{ matrix.version }}
          components: rustfmt
      - name: Show Cargo and rustc version
        run: |
          cargo --version
          rustc --version
      - uses: Swatinem/rust-cache@v2
      - name: Check rustfmt
        run: cargo fmt --all --check
      - uses: taiki-e/install-action@v2
        name: Install nextest using install-action
        with:
          tool: nextest
      - name: Test
        run: cargo test --workspace
      - name: Install locked
        run: cargo install --path . --locked
      - name: Install unlocked
        if: contains(fromJSON('["nightly", "beta", "stable"]'), matrix.version) # unlocked dependencies might change MSRV
        run: cargo install --path .

  update-dependencies:
    needs: [test]
    strategy:
      matrix:
        os: [macOS-latest, ubuntu-latest, windows-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: Swatinem/rust-cache@v2
      - uses: dtolnay/rust-toolchain@master
        with:
          toolchain: nightly
      - uses: taiki-e/install-action@v2
        name: Install nextest using install-action
        with:
          tool: nextest
      - run: cargo update
      - name: Test after cargo update
        run: cargo test --workspace

  cross-build:
    strategy:
      matrix:
        target:
          - x86_64-unknown-linux-musl
    runs-on: ubuntu-latest
    needs: [quick-check]
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: Swatinem/rust-cache@v2
        with:
          key: ${{ matrix.target }}
      - uses: dtolnay/rust-toolchain@master
        with:
          toolchain: beta
          target: ${{ matrix.target }}
      - run: cargo build --all-targets --all-features --target ${{ matrix.target }}

  # Testing direct-minimal-versions seems to have unclear value for a binary
  # crate?
  #
  # minimal-versions:
  #   needs: [test]
  #   strategy:
  #     matrix:
  #       os: [macos-latest, ubuntu-latest, windows-latest]
  #   runs-on: ${{ matrix.os }}
  #   steps:
  #     - uses: actions/checkout@v4
  #     - uses: swatinem/rust-cache@v2
  #     - uses: dtolnay/rust-toolchain@master
  #       with:
  #         toolchain: nightly
  #     - uses: taiki-e/install-action@v2
  #       name: install nextest using install-action
  #       with:
  #         tool: nextest
  #     - name: downgrade to minimal versions
  #       run: cargo +nightly -zdirect-minimal-versions update
  #     - name: test on minimal versions
  #       run: cargo test

  tests-from-tarball:
    needs: [quick-check]
    strategy:
      matrix:
        os: [ubuntu-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: Swatinem/rust-cache@v2
        with:
          save-if: false
      - uses: dtolnay/rust-toolchain@nightly
      - run: cargo package --no-verify
      - name: Upload package artifact
        uses: actions/upload-artifact@v4
        with:
          name: cargo-mutants-package
          path: |
            target/package
      - name: Unpack package
        run: |
          cd target/package
          ls -l
          tar xvf cargo-mutants*.crate
      - name: Install nextest using install-action
        uses: taiki-e/install-action@v2
        with:
          tool: nextest
      - name: Run tests from package
        # A clunky way to cd into the likely unpacked name, excluding the directory,
        # still matching e.g. `1.2.3-pre2`.
        run: |
          cd "$( find target/package -type d -name 'cargo-mutants-*' -print -quit )"
          cargo test

  branch-mutants:
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref != 'refs/heads/main'
    needs: [quick-check, binary]
    strategy:
      matrix:
        test_tool: [cargo] # nextest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Relative diff
        run: |
          git branch -av
          git diff origin/main.. | tee git.diff
      - uses: dtolnay/rust-toolchain@master
        with:
          toolchain: beta
      - uses: Swatinem/rust-cache@v2
        with:
          save-if: false
      - uses: taiki-e/install-action@v2
        name: Install nextest using install-action
        with:
          tool: nextest
      - name: Download cargo-mutants binary
        uses: actions/download-artifact@v4
        with:
          name: cargo-mutants-linux
      - name: Install cargo-mutants binary
        run: |
          install cargo-mutants ~/.cargo/bin/
      - name: Mutants in-diff
        run: >
          cargo mutants -vV --in-diff=git.diff --in-place
          --test-tool=${{matrix.test_tool}} --timeout=500 --build-timeout=500
          --exclude=windows.rs --exclude=console.rs
      - name: Archive mutants.out
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: mutants-incremental-${{ matrix.test_tool}}.out
          path: mutants.out

  pr-mutants:
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    needs: [quick-check, binary]
    strategy:
      matrix:
        test_tool: [cargo] # nextest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Relative diff
        run: |
          git branch -av
          git diff origin/${{ github.base_ref }}.. | tee git.diff
      - uses: dtolnay/rust-toolchain@master
        with:
          toolchain: beta
      - uses: Swatinem/rust-cache@v2
        with:
          save-if: false
      - uses: taiki-e/install-action@v2
        name: Install nextest using install-action
        with:
          tool: nextest
      - name: Download cargo-mutants binary
        uses: actions/download-artifact@v4
        with:
          name: cargo-mutants-linux
      - name: Install cargo-mutants binary
        run: |
          install cargo-mutants ~/.cargo/bin/
      - name: Mutants in-diff
        # Normally this would have --in-place, but for the sake of exercising more cases, it does not.
        run: >
          cargo mutants -vV --in-diff git.diff
          --test-tool=${{matrix.test_tool}} --timeout=500 --build-timeout=500
          --exclude=windows.rs --exclude=console.rs
      - name: Archive mutants.out
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: mutants-incremental-${{ matrix.test_tool}}.out
          path: mutants.out

  cargo-mutants:
    runs-on: ubuntu-latest
    needs: [test, binary]
    strategy:
      fail-fast: false # We want to get all the mutant failures
      matrix:
        shard: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        test_tool: [cargo]
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: dtolnay/rust-toolchain@master
        with:
          toolchain: nightly
      - uses: Swatinem/rust-cache@v2
        with:
          save-if: false
      - uses: taiki-e/install-action@v2
        with:
          tool: nextest, wild-linker
      - name: Configure Wild
        run: |
          mkdir -p ~/.cargo && cat <<EOF >~/.cargo/config.toml
          [target.x86_64-unknown-linux-gnu]
          linker = "clang"
          rustflags = ["-C", "link-arg=--ld-path=wild"]
          EOF
      - name: Download cargo-mutants binary
        uses: actions/download-artifact@v4
        with:
          name: cargo-mutants-linux
      - name: Install cargo-mutants binary
        run: |
          install cargo-mutants ~/.cargo/bin/
      - name: Mutants
        # Skip baselines because this job only runs after the baseline has been separately run.
        run: >
          cargo mutants -vV --shard ${{ matrix.shard }}/10
          --test-tool ${{ matrix.test_tool }} --baseline=skip --timeout=500
          --build-timeout=500 --in-place --exclude=windows.rs
          --exclude=console.rs
          -- -- -Zunstable-options --fail-fast
      - name: Archive mutants.out
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: mutants-${{matrix.test_tool}}-shard${{matrix.shard}}.out
          path: mutants.out