diff --git a/.clippy.toml b/.clippy.toml index f631664..027eef4 100644 --- a/.clippy.toml +++ b/.clippy.toml @@ -1,5 +1,5 @@ -msrv = "1.73" # MSRV warn-on-all-wildcard-imports = true +allow-print-in-tests = true allow-expect-in-tests = true allow-unwrap-in-tests = true allow-dbg-in-tests = true diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 1725e3e..6cd7ded 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -3,6 +3,7 @@ 'before 5am on the first day of the month', ], semanticCommits: 'enabled', + commitMessageLowerCase: 'never', configMigration: true, dependencyDashboard: true, customManagers: [ @@ -20,7 +21,25 @@ 'MSRV.*?(?\\d+\\.\\d+(\\.\\d+)?)', '(?\\d+\\.\\d+(\\.\\d+)?).*?MSRV', ], - depNameTemplate: 'rust', + depNameTemplate: 'MSRV', + packageNameTemplate: 'rust-lang/rust', + datasourceTemplate: 'github-releases', + }, + { + customType: 'regex', + fileMatch: [ + '^rust-toolchain\\.toml$', + 'Cargo.toml$', + 'clippy.toml$', + '\\.clippy.toml$', + '^\\.github/workflows/ci.yml$', + '^\\.github/workflows/rust-next.yml$', + ], + matchStrings: [ + 'STABLE.*?(?\\d+\\.\\d+(\\.\\d+)?)', + '(?\\d+\\.\\d+(\\.\\d+)?).*?STABLE', + ], + depNameTemplate: 'STABLE', packageNameTemplate: 'rust-lang/rust', datasourceTemplate: 'github-releases', }, @@ -32,7 +51,7 @@ 'custom.regex', ], matchPackageNames: [ - 'rust', + 'MSRV', ], minimumReleaseAge: '126 days', // 3 releases * 6 weeks per release * 7 days per week internalChecksFilter: 'strict', @@ -41,6 +60,19 @@ '* * * * *', ], }, + { + commitMessageTopic: 'Rust Stable', + matchManagers: [ + 'custom.regex', + ], + matchPackageNames: [ + 'STABLE', + ], + extractVersion: '^(?\\d+\\.\\d+)', // Drop the patch version + schedule: [ + '* * * * *', + ], + }, // Goals: // - Keep version reqs low, ignoring compatible normal/build dependencies // - Take advantage of latest dev-dependencies @@ -72,6 +104,7 @@ matchCurrentVersion: '>=1.0.0', matchUpdateTypes: [ 'minor', + 'patch', ], enabled: false, }, @@ -99,6 +132,7 @@ matchCurrentVersion: '>=1.0.0', matchUpdateTypes: [ 'minor', + 'patch', ], automerge: true, groupName: 'compatible (dev)', diff --git a/.github/settings.yml b/.github/settings.yml index bab6281..49b182e 100644 --- a/.github/settings.yml +++ b/.github/settings.yml @@ -42,14 +42,18 @@ labels: color: '#c2e0c6' description: "Help wanted!" -branches: - - name: master - protection: - required_pull_request_reviews: null - required_conversation_resolution: true - required_status_checks: - # Required. Require branches to be up to date before merging. - strict: false - contexts: ["CI", "Lint Commits", "Spell Check with Typos"] - enforce_admins: false - restrictions: null +# This serves more as documentation. +# Branch protection API was replaced by rulesets but settings isn't updated. +# See https://github.com/repository-settings/app/issues/825 +# +# branches: +# - name: master +# protection: +# required_pull_request_reviews: null +# required_conversation_resolution: true +# required_status_checks: +# # Required. Require branches to be up to date before merging. +# strict: false +# contexts: ["CI", "Spell Check with Typos"] +# enforce_admins: false +# restrictions: null diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 442e637..35b3da8 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -17,6 +17,10 @@ env: CARGO_TERM_COLOR: always CLICOLOR: 1 +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + jobs: security_audit: permissions: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e962915..504d777 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,21 +14,27 @@ env: CARGO_TERM_COLOR: always CLICOLOR: 1 +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + jobs: ci: permissions: contents: none name: CI - needs: [test, msrv, docs, rustfmt, clippy] + needs: [test, msrv, lockfile, docs, rustfmt, clippy] runs-on: ubuntu-latest + if: "always()" steps: - - name: Done - run: exit 0 + - name: Failed + run: exit 1 + if: "contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped')" test: name: Test strategy: matrix: - os: ["ubuntu-latest", "windows-latest", "macos-latest"] + os: ["ubuntu-latest", "windows-latest", "macos-14"] rust: ["stable"] continue-on-error: ${{ matrix.rust != 'stable' }} runs-on: ${{ matrix.os }} @@ -40,16 +46,13 @@ jobs: with: toolchain: ${{ matrix.rust }} - uses: Swatinem/rust-cache@v2 + - uses: taiki-e/install-action@cargo-hack - name: Build - run: cargo test --no-run --workspace --all-features - - name: Default features - run: cargo test --workspace - - name: All features - run: cargo test --workspace --all-features - - name: No-default features - run: cargo test --workspace --no-default-features + run: cargo test --workspace --no-run + - name: Test + run: cargo hack test --feature-powerset --workspace msrv: - name: "Check MSRV: 1.73" + name: "Check MSRV" runs-on: ubuntu-latest steps: - name: Checkout repository @@ -57,14 +60,11 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: "1.73" # MSRV + toolchain: stable - uses: Swatinem/rust-cache@v2 + - uses: taiki-e/install-action@cargo-hack - name: Default features - run: cargo check --workspace --all-targets - - name: All features - run: cargo check --workspace --all-targets --all-features - - name: No-default features - run: cargo check --workspace --all-targets --no-default-features + run: cargo hack check --feature-powerset --locked --rust-version --ignore-private --workspace --all-targets lockfile: runs-on: ubuntu-latest steps: @@ -76,7 +76,7 @@ jobs: toolchain: stable - uses: Swatinem/rust-cache@v2 - name: "Is lockfile updated?" - run: cargo fetch --locked + run: cargo update --workspace --locked docs: name: Docs runs-on: ubuntu-latest @@ -86,7 +86,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: stable + toolchain: "1.76" # STABLE - uses: Swatinem/rust-cache@v2 - name: Check documentation env: @@ -101,9 +101,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - # Not MSRV because its harder to jump between versions and people are - # more likely to have stable - toolchain: stable + toolchain: "1.76" # STABLE components: rustfmt - uses: Swatinem/rust-cache@v2 - name: Check formatting @@ -119,13 +117,13 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - toolchain: "1.73" # MSRV + toolchain: "1.76" # STABLE components: clippy - uses: Swatinem/rust-cache@v2 - name: Install SARIF tools - run: cargo install clippy-sarif --version 0.3.4 --locked # Held back due to msrv + run: cargo install clippy-sarif --locked - name: Install SARIF tools - run: cargo install sarif-fmt --version 0.3.4 --locked # Held back due to msrv + run: cargo install sarif-fmt --locked - name: Check run: > cargo clippy --workspace --all-features --all-targets --message-format=json -- -D warnings --allow deprecated @@ -140,3 +138,22 @@ jobs: wait-for-processing: true - name: Report status run: cargo clippy --workspace --all-features --all-targets -- -D warnings --allow deprecated + coverage: + name: Coverage + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + - uses: Swatinem/rust-cache@v2 + - name: Install cargo-tarpaulin + run: cargo install cargo-tarpaulin + - name: Gather coverage + run: cargo tarpaulin --output-dir coverage --out lcov + - name: Publish to Coveralls + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/committed.yml b/.github/workflows/committed.yml index 0462558..e7a50fb 100644 --- a/.github/workflows/committed.yml +++ b/.github/workflows/committed.yml @@ -11,6 +11,10 @@ env: CARGO_TERM_COLOR: always CLICOLOR: 1 +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + jobs: committed: name: Lint Commits diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index e5b3bd4..3d04055 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -12,6 +12,10 @@ env: CARGO_TERM_COLOR: always CLICOLOR: 1 +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + jobs: pre-commit: permissions: diff --git a/.github/workflows/rust-next.yml b/.github/workflows/rust-next.yml index a881f90..f4dc8fd 100644 --- a/.github/workflows/rust-next.yml +++ b/.github/workflows/rust-next.yml @@ -12,12 +12,16 @@ env: CARGO_TERM_COLOR: always CLICOLOR: 1 +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + jobs: test: name: Test strategy: matrix: - os: ["ubuntu-latest", "windows-latest", "macos-latest"] + os: ["ubuntu-latest", "windows-latest", "macos-latest", "macos-14"] rust: ["stable", "beta"] include: - os: ubuntu-latest @@ -32,12 +36,11 @@ jobs: with: toolchain: ${{ matrix.rust }} - uses: Swatinem/rust-cache@v2 - - name: Default features - run: cargo test --workspace - - name: All features - run: cargo test --workspace --all-features - - name: No-default features - run: cargo test --workspace --no-default-features + - uses: taiki-e/install-action@cargo-hack + - name: Build + run: cargo test --workspace --no-run + - name: Test + run: cargo hack test --feature-powerset --workspace latest: name: "Check latest dependencies" runs-on: ubuntu-latest @@ -49,11 +52,10 @@ jobs: with: toolchain: stable - uses: Swatinem/rust-cache@v2 + - uses: taiki-e/install-action@cargo-hack - name: Update dependencues run: cargo update - - name: Default features - run: cargo test --workspace --all-targets - - name: All features - run: cargo test --workspace --all-targets --all-features - - name: No-default features - run: cargo test --workspace --all-targets --no-default-features + - name: Build + run: cargo test --workspace --no-run + - name: Test + run: cargo hack test --feature-powerset --workspace diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml index 12f7585..8e58d9e 100644 --- a/.github/workflows/spelling.yml +++ b/.github/workflows/spelling.yml @@ -10,6 +10,10 @@ env: CARGO_TERM_COLOR: always CLICOLOR: 1 +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + jobs: spelling: name: Spell Check with Typos diff --git a/Cargo.lock b/Cargo.lock index e647152..1c3f8ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,6 +56,7 @@ version = "2.0.14" dependencies = [ "anstream", "anstyle", + "automod", "bstr", "doc-comment", "escargot", @@ -65,6 +66,17 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "automod" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edf3ee19dbc0a46d740f6f0926bde8c50f02bdbc7b536842da28f6ac56513a8b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + [[package]] name = "bstr" version = "1.0.1" @@ -191,18 +203,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -236,7 +248,7 @@ checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.103", ] [[package]] @@ -261,6 +273,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "termtree" version = "0.2.4" diff --git a/Cargo.toml b/Cargo.toml index 64bde63..7d86fcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,80 @@ include = [ "examples/**/*" ] +[workspace.lints.rust] +rust_2018_idioms = "warn" +unreachable_pub = "warn" +unsafe_op_in_unsafe_fn = "warn" +unused_lifetimes = "warn" +unused_macro_rules = "warn" +unused_qualifications = "warn" + +[workspace.lints.clippy] +bool_assert_comparison = "allow" +branches_sharing_code = "allow" +checked_conversions = "warn" +collapsible_else_if = "allow" +create_dir = "warn" +dbg_macro = "warn" +debug_assert_with_mut_call = "warn" +doc_markdown = "warn" +empty_enum = "warn" +enum_glob_use = "warn" +exhaustive_enums = "warn" +exhaustive_structs = "warn" +exit = "warn" +expl_impl_clone_on_copy = "warn" +explicit_deref_methods = "warn" +explicit_into_iter_loop = "warn" +fallible_impl_from = "warn" +filter_map_next = "warn" +flat_map_option = "warn" +float_cmp_const = "warn" +fn_params_excessive_bools = "warn" +from_iter_instead_of_collect = "warn" +if_same_then_else = "allow" +implicit_clone = "warn" +imprecise_flops = "warn" +inconsistent_struct_constructor = "warn" +inefficient_to_string = "warn" +infinite_loop = "warn" +invalid_upcast_comparisons = "warn" +items_after_statements = "warn" +large_digit_groups = "warn" +large_stack_arrays = "warn" +large_types_passed_by_value = "warn" +let_and_return = "allow" # sometimes good to name what you are returning +linkedlist = "warn" +lossy_float_literal = "warn" +macro_use_imports = "warn" +match_wildcard_for_single_variants = "warn" +mem_forget = "warn" +mutex_integer = "warn" +needless_continue = "warn" +needless_for_each = "warn" +negative_feature_names = "warn" +path_buf_push_overwrite = "warn" +ptr_as_ptr = "warn" +rc_mutex = "warn" +redundant_feature_names = "warn" +ref_option_ref = "warn" +rest_pat_in_fully_bound_structs = "warn" +same_functions_in_if_condition = "warn" +self_named_module_files = "warn" +semicolon_if_nothing_returned = "warn" +single_match_else = "warn" +str_to_string = "warn" +string_add = "warn" +string_add_assign = "warn" +string_lit_as_bytes = "warn" +string_to_string = "warn" +todo = "warn" +trait_duplication_in_bounds = "warn" +unwrap_used = "warn" +verbose_file_reads = "warn" +wildcard_imports = "warn" +zero_sized_map_values = "warn" + [package] name = "assert_cmd" version = "2.0.14" @@ -64,3 +138,7 @@ anstyle = "1.0.0" [dev-dependencies] escargot = "0.5" +automod = "1.0.14" + +[lints] +workspace = true diff --git a/build.rs b/build.rs index 8b7b65a..5ac652a 100644 --- a/build.rs +++ b/build.rs @@ -11,6 +11,7 @@ fn main() { let out = path::PathBuf::from(env::var_os("OUT_DIR").expect("run within cargo")) .join("current_target.txt"); let default_target = env::var("TARGET").expect("run as cargo build script"); - let mut file = fs::File::create(out).unwrap(); - file.write_all(default_target.as_bytes()).unwrap(); + let mut file = fs::File::create(out).expect("can write to OUT_DIR"); + file.write_all(default_target.as_bytes()) + .expect("can write to OUT_DIR"); } diff --git a/examples/example_fixture.rs b/examples/example_fixture.rs index f47540f..0b22817 100644 --- a/examples/example_fixture.rs +++ b/examples/example_fixture.rs @@ -1,3 +1,5 @@ +#![allow(clippy::exit)] + use std::env; use std::error::Error; use std::io; diff --git a/examples/failure.rs b/examples/failure.rs index d6678a5..7c7393f 100644 --- a/examples/failure.rs +++ b/examples/failure.rs @@ -1,3 +1,4 @@ +#[allow(clippy::wildcard_imports)] // false positive use assert_cmd::prelude::*; use std::process::Command; diff --git a/src/assert.rs b/src/assert.rs index 48370b0..6c2e9c4 100644 --- a/src/assert.rs +++ b/src/assert.rs @@ -547,7 +547,7 @@ where } /// Keep `predicates` concrete Predicates out of our public API. -/// [predicates_core::Predicate] used by [`IntoCodePredicate`] for code. +/// [`predicates_core::Predicate`] used by [`IntoCodePredicate`] for code. /// /// # Example /// @@ -616,7 +616,7 @@ impl IntoCodePredicate for i32 { } /// Keep `predicates` concrete Predicates out of our public API. -/// [predicates_core::Predicate] used by [`IntoCodePredicate`] for iterables of codes. +/// [`predicates_core::Predicate`] used by [`IntoCodePredicate`] for iterables of codes. /// /// # Example /// @@ -741,7 +741,7 @@ where } /// Keep `predicates` concrete Predicates out of our public API. -/// [predicates_core::Predicate] used by [`IntoOutputPredicate`] for bytes. +/// [`predicates_core::Predicate`] used by [`IntoOutputPredicate`] for bytes. /// /// # Example /// @@ -781,7 +781,7 @@ impl predicates_core::Predicate<[u8]> for BytesContentOutputPredicate { &self, expected: bool, variable: &[u8], - ) -> Option { + ) -> Option> { let actual = self.eval(variable); if expected == actual { Some(predicates_core::reflection::Case::new(Some(self), actual)) @@ -814,7 +814,7 @@ impl IntoOutputPredicate for &'static [u8] { } /// Keep `predicates` concrete Predicates out of our public API. -/// [predicates_core::Predicate] used by [`IntoOutputPredicate`] for [`str`]. +/// [`predicates_core::Predicate`] used by [`IntoOutputPredicate`] for [`str`]. /// /// # Example /// @@ -901,7 +901,7 @@ impl IntoOutputPredicate for &'static str { } // Keep `predicates` concrete Predicates out of our public API. -/// [predicates_core::Predicate] used by [`IntoOutputPredicate`] for +/// [`predicates_core::Predicate`] used by [`IntoOutputPredicate`] for /// [`Predicate`][predicates_core::Predicate]. /// /// # Example @@ -1123,7 +1123,7 @@ mod test { fn convert_code(pred: I) -> P where I: IntoCodePredicate

, - P: predicates_core::Predicate, + P: Predicate, { pred.into_code() } @@ -1157,7 +1157,7 @@ mod test { fn convert_output(pred: I) -> P where I: IntoOutputPredicate

, - P: predicates_core::Predicate<[u8]>, + P: Predicate<[u8]>, { pred.into_output() } diff --git a/src/bin/bin_fixture.rs b/src/bin/bin_fixture.rs index d18b23b..882819d 100644 --- a/src/bin/bin_fixture.rs +++ b/src/bin/bin_fixture.rs @@ -1,3 +1,5 @@ +#![allow(clippy::exit)] + use std::env; use std::error::Error; use std::io; diff --git a/src/cargo.rs b/src/cargo.rs index 6200b35..b69d80c 100644 --- a/src/cargo.rs +++ b/src/cargo.rs @@ -213,7 +213,7 @@ fn target_dir() -> path::PathBuf { } path }) - .unwrap() + .expect("this should only be used where a `current_exe` can be set") } /// Look up the path to a cargo-built binary within an integration test. diff --git a/src/cmd.rs b/src/cmd.rs index ebe107c..30e67ae 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -444,12 +444,8 @@ impl Command { input: Option>, timeout: Option, ) -> io::Result { - let stdin = input.and_then(|i| { - child - .stdin - .take() - .map(|mut stdin| std::thread::spawn(move || stdin.write_all(&i))) - }); + #![allow(clippy::unwrap_used)] // changes behavior in some tests + fn read(mut input: R) -> std::thread::JoinHandle>> where R: Read + Send + 'static, @@ -459,6 +455,13 @@ impl Command { input.read_to_end(&mut ret).map(|_| ret) }) } + + let stdin = input.and_then(|i| { + child + .stdin + .take() + .map(|mut stdin| std::thread::spawn(move || stdin.write_all(&i))) + }); let stdout = child.stdout.take().map(read); let stderr = child.stderr.take().map(read); diff --git a/src/lib.rs b/src/lib.rs index 7c61ff7..6f59717 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -100,8 +100,10 @@ //! [`OutputOkExt`]: output::OutputOkExt //! [`OutputAssertExt`]: assert::OutputAssertExt -#![warn(missing_docs)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![warn(missing_docs)] +#![warn(clippy::print_stderr)] +#![warn(clippy::print_stdout)] /// Allows you to pull the name from your Cargo.toml at compile time. /// diff --git a/src/output.rs b/src/output.rs index 810a4c1..b511c75 100644 --- a/src/output.rs +++ b/src/output.rs @@ -350,12 +350,13 @@ fn format_bytes(data: &[u8], f: &mut impl fmt::Write) -> fmt::Result { const LINES_MAX_START: usize = 20; const LINES_MAX_END: usize = 40; const LINES_MAX_PRINTED: usize = LINES_MAX_START + LINES_MAX_END; - assert!(LINES_MAX_PRINTED < LINES_MIN_OVERFLOW); const BYTES_MIN_OVERFLOW: usize = 8192; const BYTES_MAX_START: usize = 2048; const BYTES_MAX_END: usize = 2048; const BYTES_MAX_PRINTED: usize = BYTES_MAX_START + BYTES_MAX_END; + + assert!(LINES_MAX_PRINTED < LINES_MIN_OVERFLOW); assert!(BYTES_MAX_PRINTED < BYTES_MIN_OVERFLOW); let lines_total = data.as_bstr().lines_with_terminator().count(); diff --git a/tests/testsuite/delete_me.rs b/tests/testsuite/delete_me.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs new file mode 100644 index 0000000..4441374 --- /dev/null +++ b/tests/testsuite/main.rs @@ -0,0 +1 @@ +automod::dir!("tests/testsuite");