├── .gitattributes ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── rust.json └── workflows │ ├── release.yml │ └── rust.yml ├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── README.md ├── examples ├── rustc_basic.rs └── rustc_twice_with_different_flags.rs ├── examples_tests └── rustc_basic │ ├── aux_attr_proc_macro.rs │ ├── aux_derive.fixed │ ├── aux_derive.rs │ ├── aux_derive.stderr │ ├── aux_proc_macro.rs │ ├── aux_proc_macro.stderr │ ├── auxiliary │ ├── derive_proc_macro.rs │ ├── proc_macro_attr.rs │ └── the_proc_macro.rs │ ├── executable.rs │ ├── filtered_out.bar.stderr │ ├── filtered_out.rs │ ├── foomp-rustfix.fixed │ ├── foomp-rustfix.rs │ ├── foomp-rustfix.stderr │ ├── hello.rs │ ├── hello.stderr │ ├── rustfix-multiple.1.fixed │ ├── rustfix-multiple.2.fixed │ ├── rustfix-multiple.rs │ ├── rustfix-multiple.stderr │ ├── stdin.rs │ ├── stdin.run.stderr │ ├── stdin.run.stdin │ ├── subdir │ ├── aux_proc_macro.rs │ └── aux_proc_macro.stderr │ ├── unicode.rs │ ├── windows_paths.rs │ └── windows_paths.stderr ├── rust-toolchain.toml ├── shell.nix ├── src ├── aux_builds.rs ├── build_manager.rs ├── cmd.rs ├── config.rs ├── config │ └── args.rs ├── core.rs ├── custom_flags.rs ├── custom_flags │ ├── edition.rs │ ├── revision_args.rs │ ├── run.rs │ └── rustfix.rs ├── dependencies.rs ├── diagnostics.rs ├── diagnostics │ └── rustc.rs ├── diff.rs ├── error.rs ├── filter.rs ├── github_actions.rs ├── lib.rs ├── mode.rs ├── nextest.rs ├── parser.rs ├── parser │ ├── spanned.rs │ └── tests.rs ├── per_test_config.rs ├── status_emitter.rs ├── status_emitter │ ├── debug.rs │ ├── gha.rs │ ├── json.rs │ └── text.rs ├── test_result.rs └── tests.rs └── tests ├── build_std.rs ├── build_std └── test.rs ├── integration.rs └── integrations ├── basic-bin ├── Cargo.lock ├── Cargo.stdout ├── Cargo.toml ├── src │ └── main.rs └── tests │ ├── actual_tests │ ├── foomp.rs │ └── foomp.stderr │ └── ui_tests.rs ├── basic-fail-mode ├── Cargo.lock ├── Cargo.stdout ├── Cargo.toml ├── src │ └── lib.rs └── tests │ ├── actual_tests │ ├── foomp.rs │ └── foomp.stderr │ ├── run_file.rs │ ├── run_file │ ├── run_file.rs │ └── run_file_with_deps.rs │ └── ui_tests.rs ├── basic-fail ├── Cargo.lock ├── Cargo.stderr ├── Cargo.stdout ├── Cargo.toml ├── src │ └── lib.rs └── tests │ ├── actual_tests │ ├── bad_pattern.rs │ ├── bad_pattern.stderr │ ├── executable.rs │ ├── executable.run.stdout │ ├── executable_compile_err.rs │ ├── exit_code_fail.rs │ ├── filters.rs │ ├── foomp.rs │ ├── foomp.stderr │ ├── foomp2.fixed │ ├── foomp2.rs │ ├── foomp2.stderr │ ├── ice_annotations.rs │ ├── inline_chain.rs │ ├── joined_wrong_order.rs │ ├── lone_joined_pattern.rs │ ├── pattern_too_many_arrow.rs │ ├── pattern_too_many_arrow_above.rs │ ├── rustc_ice.rs │ ├── rustc_ice.stderr │ ├── touching_above_below.rs │ └── touching_above_below_chain.rs │ ├── actual_tests_bless │ ├── abort.rs │ ├── aux_build_not_found.rs │ ├── aux_proc_macro_misuse.rs │ ├── aux_proc_macro_no_main.rs │ ├── aux_proc_macro_no_main.stderr │ ├── auxiliary │ │ ├── foomp.rs │ │ ├── nested.rs │ │ └── the_proc_macro.rs │ ├── compile_flags_quotes.rs │ ├── compiletest-rs-command.rs │ ├── edition_override.rs │ ├── edition_override.stderr │ ├── failing_executable.rs │ ├── failing_executable.run.stderr │ ├── foomp_aux.rs │ ├── foomp_aux.stderr │ ├── nested_aux.rs │ ├── no_main.rs │ ├── no_main_manual.rs │ ├── no_main_manual.stderr │ ├── no_test.rs │ ├── non_top_level_configs.rs │ ├── normalization_override.rs │ ├── normalization_override.stderr │ ├── pass.rs │ ├── pass_with_annotation.rs │ ├── revised_revision.rs │ ├── revisioned_executable.rs │ ├── revisioned_executable_panic.panic.run.stderr │ ├── revisioned_executable_panic.rs │ ├── revisioned_executable_panic.run.run.stderr │ ├── revisions.bar.stderr │ ├── revisions.foo.stderr │ ├── revisions.rs │ ├── revisions_bad.bar.stderr │ ├── revisions_bad.foo.stderr │ ├── revisions_bad.rs │ ├── revisions_filter.rs │ ├── revisions_filter2.bar.stderr │ ├── revisions_filter2.rs │ ├── revisions_multiple_per_annotation.bar.stderr │ ├── revisions_multiple_per_annotation.foo.stderr │ ├── revisions_multiple_per_annotation.rs │ ├── revisions_same_everywhere.bar.stderr │ ├── revisions_same_everywhere.foo.stderr │ ├── revisions_same_everywhere.rs │ ├── run_panic.rs │ ├── run_panic.run.stderr │ ├── rustfix-fail-revisions.a.fixed │ ├── rustfix-fail-revisions.a.stderr │ ├── rustfix-fail-revisions.b.fixed │ ├── rustfix-fail-revisions.b.stderr │ ├── rustfix-fail-revisions.rs │ ├── rustfix-fail.fixed │ ├── rustfix-fail.rs │ ├── rustfix-fail.stderr │ ├── unicode.rs │ ├── unicode.stderr │ ├── unknown_revision.rs │ ├── unknown_revision2.rs │ ├── wrong_diagnostic_code.rs │ └── wrong_diagnostic_code.stderr │ ├── actual_tests_bless_yolo │ ├── revisions_bad.bar.stderr │ ├── revisions_bad.foo.stderr │ ├── revisions_bad.rs │ ├── rustfix-maybe-incorrect.fixed │ ├── rustfix-maybe-incorrect.rs │ ├── rustfix-maybe-incorrect.stderr │ ├── rustfix-multiple-fail.1.fixed │ ├── rustfix-multiple-fail.2.fixed │ ├── rustfix-multiple-fail.3.fixed │ ├── rustfix-multiple-fail.rs │ └── rustfix-multiple-fail.stderr │ ├── json.rs │ ├── ui_tests.rs │ ├── ui_tests_bless.rs │ ├── ui_tests_diff_only.rs │ ├── ui_tests_invalid_program.rs │ └── ui_tests_invalid_program2.rs ├── basic ├── Cargo.lock ├── Cargo.stdout ├── Cargo.toml ├── src │ └── lib.rs └── tests │ ├── actual_tests │ ├── aux_attr_proc_macro.rs │ ├── aux_derive.fixed │ ├── aux_derive.rs │ ├── aux_derive.stderr │ ├── aux_proc_macro.rs │ ├── aux_proc_macro.stderr │ ├── auxiliary │ │ ├── derive_proc_macro.rs │ │ ├── proc_macro_attr.rs │ │ └── the_proc_macro.rs │ ├── dep_derive.rs │ ├── dep_derive.stderr │ ├── error_above.rs │ ├── error_above.stderr │ ├── error_below_multi_rev.edition2015.stderr │ ├── error_below_multi_rev.edition2018.stderr │ ├── error_below_multi_rev.rs │ ├── executable.rs │ ├── executable.run.stdout │ ├── foomp-rustfix.fixed │ ├── foomp-rustfix.rs │ ├── foomp-rustfix.stderr │ ├── foomp.rs │ ├── foomp.stderr │ ├── joined_above.fixed │ ├── joined_above.rs │ ├── joined_above.stderr │ ├── joined_below.fixed │ ├── joined_below.rs │ ├── joined_below.stderr │ ├── joined_mixed.fixed │ ├── joined_mixed.rs │ ├── joined_mixed.stderr │ ├── mac_span.fixed │ ├── mac_span.rs │ ├── mac_span.stderr │ ├── match_diagnostic_code.fixed │ ├── match_diagnostic_code.rs │ ├── match_diagnostic_code.stderr │ ├── no_rustfix.rs │ ├── no_rustfix.stderr │ ├── rustfix-multiple.1.fixed │ ├── rustfix-multiple.2.fixed │ ├── rustfix-multiple.rs │ ├── rustfix-multiple.stderr │ ├── stdin.rs │ ├── stdin.run.stderr │ ├── stdin.run.stdin │ ├── subdir │ │ ├── aux_proc_macro.rs │ │ └── aux_proc_macro.stderr │ ├── unicode.rs │ ├── windows_paths.rs │ └── windows_paths.stderr │ ├── json.rs │ ├── run_file.rs │ ├── run_file │ ├── non_utf8 │ ├── run_file.rs │ └── run_file_with_deps.rs │ └── ui_tests.rs ├── cargo-run ├── Cargo.lock ├── Cargo.stdout ├── Cargo.toml ├── src │ └── main.rs └── tests │ ├── actual_tests │ ├── empty_no_stdin.rs │ ├── empty_no_stdin.stderr │ ├── matching_stdin.rs │ ├── matching_stdin.stderr │ ├── matching_stdin.stdin │ ├── mismatching_stdin.rs │ ├── mismatching_stdin.stderr │ ├── mismatching_stdin.stdin │ ├── no_stdin.rs │ └── no_stdin.stderr │ └── ui_tests.rs ├── dep-fail ├── Cargo.lock ├── Cargo.stderr ├── Cargo.stdout ├── Cargo.toml ├── src │ └── lib.rs ├── tested_crate │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── tests │ ├── ui.rs │ └── ui │ └── basic_test.rs └── ui_test_dep_bug ├── Cargo.lock ├── Cargo.stdout ├── Cargo.toml ├── src └── lib.rs ├── tested_crate ├── Cargo.toml └── src │ └── lib.rs └── tests ├── ui.rs └── ui └── basic_test.rs /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | # TODO (check if already done) 3 | * [ ] Add tests 4 | * [ ] Add CHANGELOG.md entry 5 | * [ ] Bumped minor version and committed lockfiles in case a release is desired 6 | -------------------------------------------------------------------------------- /.github/rust.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "cargo-common", 5 | "pattern": [ 6 | { 7 | "regexp": "^(warning|warn|error)(\\[(\\S*)\\])?: (.*)$", 8 | "severity": 1, 9 | "message": 4, 10 | "code": 3 11 | }, 12 | { 13 | "regexp": "^\\s+-->\\s(\\S+):(\\d+):(\\d+)$", 14 | "file": 1, 15 | "line": 2, 16 | "column": 3 17 | } 18 | ] 19 | }, 20 | { 21 | "owner": "cargo-test", 22 | "pattern": [ 23 | { 24 | "regexp": "^.*panicked\\s+at\\s+'(.*)',\\s+(.*):(\\d+):(\\d+)$", 25 | "message": 1, 26 | "file": 2, 27 | "line": 3, 28 | "column": 4 29 | } 30 | ] 31 | }, 32 | { 33 | "owner": "cargo-fmt", 34 | "pattern": [ 35 | { 36 | "regexp": "^(Diff in (\\S+)) at line (\\d+):", 37 | "message": 1, 38 | "file": 2, 39 | "line": 3 40 | } 41 | ] 42 | } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release new version 2 | 3 | on: 4 | workflow_dispatch: 5 | secrets: 6 | CARGO_REGISTRY_TOKEN: 7 | required: true 8 | 9 | env: 10 | RUST_BACKTRACE: 1 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | create-release: 15 | name: Create release 16 | runs-on: ubuntu-latest 17 | if: github.ref == 'refs/heads/main' 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | with: 22 | persist-credentials: true 23 | 24 | - name: Install rust 25 | uses: actions-rs/toolchain@v1 26 | with: 27 | toolchain: stable 28 | profile: minimal 29 | override: true 30 | 31 | - uses: Swatinem/rust-cache@v2 32 | 33 | # Determine which version we're about to publish, so we can tag it appropriately. 34 | # If the tag already exists, then we've already published this version. 35 | - name: Determine current version 36 | id: version-check 37 | run: | 38 | # Fail on first error, on undefined variables, and on errors in pipes. 39 | set -euo pipefail 40 | export VERSION="$(cargo metadata --format-version 1 | \ 41 | jq --arg crate_name ui_test --exit-status -r \ 42 | '.packages[] | select(.name == $crate_name) | .version')" 43 | echo "version=$VERSION" >> $GITHUB_OUTPUT 44 | if [[ "$(git tag -l "$VERSION")" != '' ]]; then 45 | echo "Aborting: Version $VERSION is already published, we found its tag in the repo." 46 | exit 1 47 | fi 48 | 49 | - name: Install binstall 50 | run: curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash 51 | 52 | # TODO: Replace this with the cargo-semver-checks v2 GitHub Action when it's stabilized: 53 | # https://github.com/obi1kenobi/cargo-semver-checks-action/pull/21 54 | - name: Semver-check 55 | run: | 56 | # Fail on first error, on undefined variables, and on errors in pipes. 57 | set -euo pipefail 58 | cargo binstall --locked cargo-semver-checks 59 | cargo semver-checks check-release 60 | 61 | - name: Publish 62 | run: cargo publish 63 | env: 64 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 65 | 66 | - name: Tag the version 67 | run: | 68 | # Fail on first error, on undefined variables, and on errors in pipes. 69 | set -euo pipefail 70 | git tag "${{ steps.version-check.outputs.version }}" 71 | git push origin "${{ steps.version-check.outputs.version }}" 72 | 73 | - uses: taiki-e/create-gh-release-action@v1 74 | name: Create GitHub release 75 | with: 76 | branch: main 77 | ref: refs/tags/${{ steps.version-check.outputs.version }} 78 | env: 79 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 80 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | fmt: 14 | name: check rustfmt 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: install rustfmt 19 | run: rustup component add rustfmt 20 | - name: Format 21 | run: cargo fmt --check 22 | 23 | clippy: 24 | name: check clippy 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v4 28 | - name: install clippy 29 | run: rustup component add clippy 30 | - name: Clippy 31 | run: cargo clippy -- -D warnings 32 | 33 | tests: 34 | strategy: 35 | matrix: 36 | include: 37 | - os: ubuntu-latest 38 | host_target: x86_64-unknown-linux-gnu 39 | - os: macos-latest 40 | host_target: x86_64-apple-darwin 41 | - os: windows-latest 42 | host_target: i686-pc-windows-msvc 43 | runs-on: ${{ matrix.os }} 44 | # Run tests under a directory with a space in it to double check the windows path heuristic 45 | defaults: 46 | run: 47 | working-directory: "dir with spaces/ui test" 48 | steps: 49 | - uses: actions/checkout@v4 50 | with: 51 | path: "dir with spaces/ui test" 52 | - name: Build 53 | run: cargo build --verbose 54 | - name: Run ui tests 55 | run: cargo test --verbose --test integration -- --check 56 | - name: Run unit tests 57 | run: cargo test --verbose 58 | - name: Test no-rustc mode 59 | run: cargo test --no-default-features 60 | 61 | build-std: 62 | strategy: 63 | matrix: 64 | include: 65 | - os: ubuntu-latest 66 | host_target: x86_64-unknown-linux-gnu 67 | - os: macos-latest 68 | host_target: x86_64-apple-darwin 69 | - os: windows-latest 70 | host_target: i686-pc-windows-msvc 71 | runs-on: ${{ matrix.os }} 72 | # Run tests under a directory with a space in it to double check the windows path heuristic 73 | defaults: 74 | run: 75 | working-directory: "dir with spaces/build std" 76 | steps: 77 | - uses: actions/checkout@v4 78 | with: 79 | path: "dir with spaces/build std" 80 | - uses: dtolnay/rust-toolchain@nightly 81 | with: 82 | components: rust-src 83 | - name: Install rustc-src component 84 | run: rustup toolchain install nightly && rustup +nightly component add rust-src 85 | - name: Run build-std test 86 | run: cargo +nightly test --verbose --test build_std 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .jj 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.linkedProjects": [ 3 | "./tests/integrations/basic/Cargo.toml", 4 | "./tests/integrations/basic-bin/Cargo.toml", 5 | "./tests/integrations/basic-fail/Cargo.toml", 6 | "./tests/integrations/basic-fail-mode/Cargo.toml", 7 | "./tests/integrations/cargo-run/Cargo.toml" 8 | ], 9 | "nixEnvSelector.nixFile": "${workspaceRoot}/shell.nix" 10 | } 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Running the test suite 2 | 3 | Running `cargo test` will automatically update the `.stderr` 4 | and `.stdout` files. 5 | 6 | If you only want to check that the output files match and not 7 | update them, use `cargo test -- -- --check` 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ui_test" 3 | version = "0.30.1" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | description = "A test framework for testing rustc diagnostics output" 7 | repository = "https://github.com/oli-obk/ui_test" 8 | rust-version = "1.78" 9 | 10 | [lib] 11 | test = true # we have unit tests 12 | doctest = false # but no doc tests 13 | 14 | [dependencies] 15 | rustc_version = "0.4" 16 | colored = "2" 17 | serde = { version = "1.0", features = ["derive"] } 18 | serde_json = "1.0" 19 | cargo_metadata = { version = "0.18", optional = true } 20 | crossbeam-channel = "0.5.6" 21 | bstr = "1.0.1" 22 | rustfix = "0.8.1" 23 | cargo-platform = { version = "0.1.2", optional = true } 24 | comma = "1.0.0" 25 | anyhow = "1.0.6" 26 | indicatif = { version = "0.17.6", optional = true } 27 | prettydiff = { version = "0.7", default-features = false } 28 | annotate-snippets = { version = "0.11.2" } 29 | levenshtein = "1.0.5" 30 | spanned = "0.3.0" 31 | 32 | [dev-dependencies] 33 | ctrlc = "3.4.5" 34 | 35 | [dependencies.regex] 36 | version = "1.5.5" 37 | default-features = false 38 | features = ["unicode-gencat"] 39 | 40 | [dependencies.color-eyre] 41 | version = "0.6.1" 42 | default-features = false 43 | features = ["capture-spantrace"] 44 | 45 | [[test]] 46 | name = "integration" 47 | harness = false 48 | required-features = ["rustc"] 49 | 50 | [[test]] 51 | name = "build_std" 52 | test = false 53 | 54 | [features] 55 | default = ["rustc", "indicatif", "gha"] 56 | gha = [] 57 | rustc = ["dep:cargo-platform", "dep:cargo_metadata"] 58 | -------------------------------------------------------------------------------- /examples/rustc_basic.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "rustc")] 2 | use ui_test::{run_tests, Config}; 3 | 4 | #[cfg(feature = "rustc")] 5 | #[cfg_attr(test, test)] 6 | fn main() -> ui_test::color_eyre::Result<()> { 7 | let config = Config::rustc("examples_tests/rustc_basic"); 8 | let abort_check = config.abort_check.clone(); 9 | ctrlc::set_handler(move || abort_check.abort())?; 10 | 11 | // Compile all `.rs` files in the given directory (relative to your 12 | // Cargo.toml) and compare their output against the corresponding 13 | // `.stderr` files. 14 | run_tests(config) 15 | } 16 | 17 | #[cfg(not(feature = "rustc"))] 18 | fn main() -> ui_test::color_eyre::Result<()> { 19 | Ok(()) 20 | } 21 | -------------------------------------------------------------------------------- /examples/rustc_twice_with_different_flags.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "rustc")] 2 | use ui_test::{ 3 | default_file_filter, default_per_file_config, run_tests_generic, status_emitter, Config, 4 | }; 5 | 6 | #[cfg(feature = "rustc")] 7 | #[cfg_attr(test, test)] 8 | fn main() -> ui_test::color_eyre::Result<()> { 9 | let config = Config::rustc("examples_tests/rustc_basic"); 10 | let abort_check = config.abort_check.clone(); 11 | ctrlc::set_handler(move || abort_check.abort())?; 12 | 13 | // Compile all `.rs` files in the given directory (relative to your 14 | // Cargo.toml) and compare their output against the corresponding 15 | // `.stderr` files. 16 | run_tests_generic( 17 | vec![config.clone(), config], 18 | default_file_filter, 19 | default_per_file_config, 20 | status_emitter::Text::verbose(), 21 | ) 22 | } 23 | 24 | #[cfg(not(feature = "rustc"))] 25 | fn main() -> ui_test::color_eyre::Result<()> { 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/aux_attr_proc_macro.rs: -------------------------------------------------------------------------------- 1 | //@aux-build:proc_macro_attr.rs 2 | //@check-pass 3 | 4 | extern crate proc_macro_attr; 5 | 6 | #[proc_macro_attr::passthrough] 7 | pub fn f() {} 8 | 9 | pub fn g() {} 10 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/aux_derive.fixed: -------------------------------------------------------------------------------- 1 | //@aux-build:derive_proc_macro.rs 2 | 3 | #[macro_use] 4 | extern crate derive_proc_macro; 5 | 6 | fn main() { 7 | let mut x = Foo; 8 | x = Foo; 9 | //~^ ERROR: cannot assign twice 10 | } 11 | 12 | #[derive(Something)] 13 | struct Foo; 14 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/aux_derive.rs: -------------------------------------------------------------------------------- 1 | //@aux-build:derive_proc_macro.rs 2 | 3 | #[macro_use] 4 | extern crate derive_proc_macro; 5 | 6 | fn main() { 7 | let x = Foo; 8 | x = Foo; 9 | //~^ ERROR: cannot assign twice 10 | } 11 | 12 | #[derive(Something)] 13 | struct Foo; 14 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/aux_derive.stderr: -------------------------------------------------------------------------------- 1 | warning: variable `x` is assigned to, but never used 2 | --> examples_tests/rustc_basic/aux_derive.rs:7:9 3 | | 4 | 7 | let x = Foo; 5 | | ^ 6 | | 7 | = note: consider using `_x` instead 8 | = note: `#[warn(unused_variables)]` on by default 9 | 10 | warning: value assigned to `x` is never read 11 | --> examples_tests/rustc_basic/aux_derive.rs:8:5 12 | | 13 | 8 | x = Foo; 14 | | ^ 15 | | 16 | = help: maybe it is overwritten before being read? 17 | = note: `#[warn(unused_assignments)]` on by default 18 | 19 | error[E0384]: cannot assign twice to immutable variable `x` 20 | --> examples_tests/rustc_basic/aux_derive.rs:8:5 21 | | 22 | 7 | let x = Foo; 23 | | - 24 | | | 25 | | first assignment to `x` 26 | | help: consider making this binding mutable: `mut x` 27 | 8 | x = Foo; 28 | | ^^^^^^^ cannot assign twice to immutable variable 29 | 30 | error: aborting due to 1 previous error; 2 warnings emitted 31 | 32 | For more information about this error, try `rustc --explain E0384`. 33 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/aux_proc_macro.rs: -------------------------------------------------------------------------------- 1 | //@aux-build:the_proc_macro.rs 2 | 3 | use the_proc_macro::thing; 4 | 5 | fn main() { 6 | thing!(cake); 7 | //~^ ERROR: cannot find value `cake` in this scope 8 | } 9 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/aux_proc_macro.stderr: -------------------------------------------------------------------------------- 1 | error[E0425]: cannot find value `cake` in this scope 2 | --> examples_tests/rustc_basic/aux_proc_macro.rs:6:12 3 | | 4 | 6 | thing!(cake); 5 | | ^^^^ not found in this scope 6 | 7 | error: aborting due to 1 previous error 8 | 9 | For more information about this error, try `rustc --explain E0425`. 10 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/auxiliary/derive_proc_macro.rs: -------------------------------------------------------------------------------- 1 | //@compile-flags: --emit=link -C prefer-dynamic=no 2 | 3 | #![crate_type = "proc-macro"] 4 | 5 | extern crate proc_macro; 6 | 7 | use proc_macro::TokenStream; 8 | 9 | #[proc_macro_derive(Something)] 10 | pub fn noop(_: TokenStream) -> TokenStream { 11 | std::thread::sleep(std::time::Duration::from_secs(5)); 12 | TokenStream::new() 13 | } 14 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/auxiliary/proc_macro_attr.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use proc_macro::TokenStream; 4 | 5 | #[proc_macro_attribute] 6 | pub fn passthrough(_: TokenStream, item: TokenStream) -> TokenStream { 7 | std::thread::sleep(std::time::Duration::from_secs(5)); 8 | item 9 | } 10 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/auxiliary/the_proc_macro.rs: -------------------------------------------------------------------------------- 1 | #![crate_type = "proc-macro"] 2 | 3 | extern crate proc_macro; 4 | 5 | use proc_macro::TokenStream; 6 | 7 | #[proc_macro] 8 | pub fn thing(input: TokenStream) -> TokenStream { 9 | std::thread::sleep(std::time::Duration::from_secs(5)); 10 | input 11 | } 12 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/executable.rs: -------------------------------------------------------------------------------- 1 | //@run 2 | //@revisions: a b c d e f g h i j k l m n 3 | 4 | fn main() { 5 | std::thread::sleep(std::time::Duration::from_secs(5)); 6 | } 7 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/filtered_out.bar.stderr: -------------------------------------------------------------------------------- 1 | error: expected identifier, found `` 2 | --> examples_tests/rustc_basic/filtered_out.rs:5:1 3 | | 4 | 5 | fn 5 | | ^^ expected identifier 6 | 7 | error: aborting due to 1 previous error 8 | 9 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/filtered_out.rs: -------------------------------------------------------------------------------- 1 | //@[foo] only-target: asdfasdf 2 | 3 | //@ revisions: foo bar 4 | 5 | fn//~ ERROR: expected identifier 6 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/foomp-rustfix.fixed: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | 3 | fn main() { 4 | let x = 42; 5 | //~^ ERROR: does not need to be mutable 6 | println!("{x}"); 7 | } 8 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/foomp-rustfix.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | 3 | fn main() { 4 | let mut x = 42; 5 | //~^ ERROR: does not need to be mutable 6 | println!("{x}"); 7 | } 8 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/foomp-rustfix.stderr: -------------------------------------------------------------------------------- 1 | error: variable does not need to be mutable 2 | --> examples_tests/rustc_basic/foomp-rustfix.rs:4:9 3 | | 4 | 4 | let mut x = 42; 5 | | ----^ 6 | | | 7 | | help: remove this `mut` 8 | | 9 | note: the lint level is defined here 10 | --> examples_tests/rustc_basic/foomp-rustfix.rs:1:9 11 | | 12 | 1 | #![deny(warnings)] 13 | | ^^^^^^^^ 14 | = note: `#[deny(unused_mut)]` implied by `#[deny(warnings)]` 15 | 16 | error: aborting due to 1 previous error 17 | 18 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/hello.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println("hello world") 3 | //~^ ERROR: expected function, found macro `println` 4 | } 5 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/hello.stderr: -------------------------------------------------------------------------------- 1 | error[E0423]: expected function, found macro `println` 2 | --> examples_tests/rustc_basic/hello.rs:2:5 3 | | 4 | 2 | println("hello world") 5 | | ^^^^^^^ not a function 6 | | 7 | help: use `!` to invoke the macro 8 | | 9 | 2 | println!("hello world") 10 | | + 11 | 12 | error: aborting due to 1 previous error 13 | 14 | For more information about this error, try `rustc --explain E0423`. 15 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/rustfix-multiple.1.fixed: -------------------------------------------------------------------------------- 1 | pub fn f() -> usize { 2 | 1 3 | } 4 | 5 | #[allow(long_running_const_eval)] 6 | const SLOW: () = { 7 | let mut i = 0; 8 | while i < 5_000_000 { 9 | i += 1; 10 | } 11 | }; 12 | 13 | pub fn g() { 14 | f(); //~ ERROR: mismatched types 15 | } 16 | 17 | fn main() { 18 | SLOW; 19 | } 20 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/rustfix-multiple.2.fixed: -------------------------------------------------------------------------------- 1 | pub fn f() -> usize { 2 | 1 3 | } 4 | 5 | #[allow(long_running_const_eval)] 6 | const SLOW: () = { 7 | let mut i = 0; 8 | while i < 5_000_000 { 9 | i += 1; 10 | } 11 | }; 12 | 13 | pub fn g() -> usize { 14 | f() //~ ERROR: mismatched types 15 | } 16 | 17 | fn main() { 18 | SLOW; 19 | } 20 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/rustfix-multiple.rs: -------------------------------------------------------------------------------- 1 | pub fn f() -> usize { 2 | 1 3 | } 4 | 5 | #[allow(long_running_const_eval)] 6 | const SLOW: () = { 7 | let mut i = 0; 8 | while i < 5_000_000 { 9 | i += 1; 10 | } 11 | }; 12 | 13 | pub fn g() { 14 | f() //~ ERROR: mismatched types 15 | } 16 | 17 | fn main() { 18 | SLOW; 19 | } 20 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/rustfix-multiple.stderr: -------------------------------------------------------------------------------- 1 | warning: constant evaluation is taking a long time 2 | --> examples_tests/rustc_basic/rustfix-multiple.rs:8:5 3 | | 4 | 8 | / while i < 5_000_000 { 5 | 9 | | i += 1; 6 | 10 | | } 7 | | |_____^ the const evaluator is currently interpreting this expression 8 | | 9 | help: the constant being evaluated 10 | --> examples_tests/rustc_basic/rustfix-multiple.rs:6:1 11 | | 12 | 6 | const SLOW: () = { 13 | | ^^^^^^^^^^^^^^ 14 | 15 | error[E0308]: mismatched types 16 | --> examples_tests/rustc_basic/rustfix-multiple.rs:14:5 17 | | 18 | 14 | f() 19 | | ^^^ expected `()`, found `usize` 20 | | 21 | help: consider using a semicolon here 22 | | 23 | 14 | f(); 24 | | + 25 | help: try adding a return type 26 | | 27 | 13 | pub fn g() -> usize { 28 | | ++++++++ 29 | 30 | error: aborting due to 1 previous error; 1 warning emitted 31 | 32 | For more information about this error, try `rustc --explain E0308`. 33 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/stdin.rs: -------------------------------------------------------------------------------- 1 | //@run 2 | 3 | fn main() { 4 | let mut n = 0; 5 | for line in std::io::stdin().lines() { 6 | n += 1; 7 | eprintln!("{}", line.unwrap()); 8 | } 9 | assert_eq!(n, 6); 10 | } 11 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/stdin.run.stderr: -------------------------------------------------------------------------------- 1 | foo 2 | 3 | bar 4 | 5 | baz 6 | 7 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/stdin.run.stdin: -------------------------------------------------------------------------------- 1 | foo 2 | 3 | bar 4 | 5 | baz 6 | 7 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/subdir/aux_proc_macro.rs: -------------------------------------------------------------------------------- 1 | //@aux-build:../auxiliary/the_proc_macro.rs 2 | 3 | use the_proc_macro::thing; 4 | 5 | fn main() { 6 | thing!(cake); 7 | //~^ ERROR: cannot find value `cake` in this scope 8 | } 9 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/subdir/aux_proc_macro.stderr: -------------------------------------------------------------------------------- 1 | error[E0425]: cannot find value `cake` in this scope 2 | --> examples_tests/rustc_basic/subdir/aux_proc_macro.rs:6:12 3 | | 4 | 6 | thing!(cake); 5 | | ^^^^ not found in this scope 6 | 7 | error: aborting due to 1 previous error 8 | 9 | For more information about this error, try `rustc --explain E0425`. 10 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/unicode.rs: -------------------------------------------------------------------------------- 1 | //@check-pass 2 | 3 | #[test] 4 | pub fn issue_7739() { 5 | // Ryū crate: https://github.com/dtolnay/ryu 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/windows_paths.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let () = "escapes stay as backslashes: \t\r\n"; 3 | //~^ ERROR: mismatched types 4 | 5 | let () = r"absolute: C:\foo\file.rs"; 6 | //~^ ERROR: mismatched types 7 | let () = r"absolute, spaces: C:\foo bar\file.rs"; 8 | //~^ ERROR: mismatched types 9 | let () = r"absolute, spaces, dir: C:\foo bar\some dir\"; 10 | //~^ ERROR: mismatched types 11 | let () = r"absolute, spaces, no extension: C:\foo bar\some file"; 12 | //~^ ERROR: mismatched types 13 | 14 | let () = r"relative: foo\file.rs"; 15 | //~^ ERROR: mismatched types 16 | 17 | let () = r"unicode: Ryū\file.rs"; 18 | //~^ ERROR: mismatched types 19 | 20 | let () = r"mixed seperators: C:\foo/../bar\"; 21 | //~^ ERROR: mismatched types 22 | 23 | let () = r"unsupported: foo\bar"; 24 | //~^ ERROR: mismatched types 25 | } 26 | -------------------------------------------------------------------------------- /examples_tests/rustc_basic/windows_paths.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> examples_tests/rustc_basic/windows_paths.rs:2:9 3 | | 4 | 2 | let () = "escapes stay as backslashes: \t\r\n"; 5 | | ^^ ------------------------------------- this expression has type `&str` 6 | | | 7 | | expected `str`, found `()` 8 | 9 | error[E0308]: mismatched types 10 | --> examples_tests/rustc_basic/windows_paths.rs:5:9 11 | | 12 | 5 | let () = r"absolute: C:/foo/file.rs"; 13 | | ^^ --------------------------- this expression has type `&str` 14 | | | 15 | | expected `str`, found `()` 16 | 17 | error[E0308]: mismatched types 18 | --> examples_tests/rustc_basic/windows_paths.rs:7:9 19 | | 20 | 7 | let () = r"absolute, spaces: C:/foo bar/file.rs"; 21 | | ^^ --------------------------------------- this expression has type `&str` 22 | | | 23 | | expected `str`, found `()` 24 | 25 | error[E0308]: mismatched types 26 | --> examples_tests/rustc_basic/windows_paths.rs:9:9 27 | | 28 | 9 | let () = r"absolute, spaces, dir: C:/foo bar/some dir/"; 29 | | ^^ ---------------------------------------------- this expression has type `&str` 30 | | | 31 | | expected `str`, found `()` 32 | 33 | error[E0308]: mismatched types 34 | --> examples_tests/rustc_basic/windows_paths.rs:11:9 35 | | 36 | 11 | let () = r"absolute, spaces, no extension: C:/foo bar/some file"; 37 | | ^^ ------------------------------------------------------- this expression has type `&str` 38 | | | 39 | | expected `str`, found `()` 40 | 41 | error[E0308]: mismatched types 42 | --> examples_tests/rustc_basic/windows_paths.rs:14:9 43 | | 44 | 14 | let () = r"relative: foo/file.rs"; 45 | | ^^ ------------------------ this expression has type `&str` 46 | | | 47 | | expected `str`, found `()` 48 | 49 | error[E0308]: mismatched types 50 | --> examples_tests/rustc_basic/windows_paths.rs:17:9 51 | | 52 | 17 | let () = r"unicode: Ryū/file.rs"; 53 | | ^^ ----------------------- this expression has type `&str` 54 | | | 55 | | expected `str`, found `()` 56 | 57 | error[E0308]: mismatched types 58 | --> examples_tests/rustc_basic/windows_paths.rs:20:9 59 | | 60 | 20 | let () = r"mixed seperators: C:/foo/../bar/"; 61 | | ^^ ----------------------------------- this expression has type `&str` 62 | | | 63 | | expected `str`, found `()` 64 | 65 | error[E0308]: mismatched types 66 | --> examples_tests/rustc_basic/windows_paths.rs:23:9 67 | | 68 | 23 | let () = r"unsupported: foo\bar"; 69 | | ^^ ----------------------- this expression has type `&str` 70 | | | 71 | | expected `str`, found `()` 72 | 73 | error: aborting due to 9 previous errors 74 | 75 | For more information about this error, try `rustc --explain E0308`. 76 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.78" 3 | components = [ "rustfmt", "clippy" ] 4 | profile = "minimal" 5 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | let 2 | pkgs = import {}; 3 | in 4 | pkgs.mkShell rec { 5 | name = "rustc"; 6 | buildInputs = with pkgs; [ 7 | rustup 8 | pkg-config 9 | alsaLib 10 | libGL 11 | xorg.libX11 12 | xorg.libXi 13 | python39 14 | ]; 15 | RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; 16 | LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath buildInputs}"; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/aux_builds.rs: -------------------------------------------------------------------------------- 1 | //! Everything needed to build auxilary files with rustc 2 | // lol we can't name this file `aux.rs` on windows 3 | 4 | use crate::{ 5 | build_manager::{Build, BuildManager}, 6 | custom_flags::Flag, 7 | default_per_file_config, display, 8 | per_test_config::{Comments, TestConfig}, 9 | status_emitter::SilentStatus, 10 | CrateType, Error, Errored, 11 | }; 12 | use bstr::ByteSlice; 13 | use spanned::Spanned; 14 | use std::{ffi::OsString, path::PathBuf, process::Command, sync::Arc}; 15 | 16 | impl Flag for AuxBuilder { 17 | fn must_be_unique(&self) -> bool { 18 | false 19 | } 20 | fn clone_inner(&self) -> Box { 21 | Box::new(self.clone()) 22 | } 23 | fn apply( 24 | &self, 25 | cmd: &mut Command, 26 | config: &TestConfig, 27 | build_manager: &BuildManager, 28 | ) -> Result<(), Errored> { 29 | let aux = &self.aux_file; 30 | let aux_dir = config.aux_dir.clone(); 31 | let aux_file = if aux.starts_with("..") { 32 | aux_dir.parent().unwrap().join(&aux.content) 33 | } else { 34 | aux_dir.join(&aux.content) 35 | }; 36 | let extra_args = build_manager 37 | .build( 38 | AuxBuilder { 39 | aux_file: Spanned::new( 40 | crate::core::strip_path_prefix( 41 | &aux_file.canonicalize().map_err(|err| Errored { 42 | command: format!("canonicalizing path `{}`", display(&aux_file)), 43 | errors: vec![], 44 | stderr: err.to_string().into_bytes(), 45 | stdout: vec![], 46 | })?, 47 | &std::env::current_dir().unwrap(), 48 | ) 49 | .collect(), 50 | aux.span(), 51 | ), 52 | }, 53 | &config.status, 54 | ) 55 | .map_err( 56 | |Errored { 57 | command, 58 | errors, 59 | stderr, 60 | stdout, 61 | }| Errored { 62 | command, 63 | errors: vec![Error::Aux { 64 | path: Spanned::new(aux_file.to_path_buf(), aux.span()), 65 | errors, 66 | }], 67 | stderr, 68 | stdout, 69 | }, 70 | )?; 71 | cmd.args(extra_args); 72 | Ok(()) 73 | } 74 | } 75 | 76 | /// Build an aux-build. 77 | /// Custom `//@aux-build` flag handler. 78 | #[derive(Clone, Debug)] 79 | pub struct AuxBuilder { 80 | /// Full path to the file (including `auxiliary` folder prefix) 81 | pub aux_file: Spanned, 82 | } 83 | 84 | impl Build for AuxBuilder { 85 | fn build(&self, build_manager: &BuildManager) -> Result, Errored> { 86 | let mut config = build_manager.config().clone(); 87 | let file_contents = 88 | Spanned::read_from_file(&self.aux_file.content).map_err(|err| Errored { 89 | command: format!("reading aux file `{}`", display(&self.aux_file)), 90 | errors: vec![], 91 | stderr: err.to_string().into_bytes(), 92 | stdout: vec![], 93 | })?; 94 | let comments = Comments::parse(file_contents.as_ref(), &config) 95 | .map_err(|errors| Errored::new(errors, "parse aux comments"))?; 96 | assert_eq!( 97 | comments.revisions, None, 98 | "aux builds cannot specify revisions" 99 | ); 100 | 101 | default_per_file_config(&mut config, &file_contents); 102 | 103 | match CrateType::from_file_contents(&file_contents) { 104 | // Proc macros must be run on the host 105 | CrateType::ProcMacro => config.target.clone_from(&config.host), 106 | CrateType::Test | CrateType::Bin | CrateType::Lib => {} 107 | } 108 | 109 | let mut config = TestConfig { 110 | config, 111 | comments: Arc::new(comments), 112 | aux_dir: self.aux_file.parent().unwrap().to_owned(), 113 | status: Box::new(SilentStatus { 114 | revision: String::new(), 115 | path: self.aux_file.content.clone(), 116 | }), 117 | }; 118 | 119 | config.patch_out_dir(); 120 | 121 | let mut aux_cmd = config.build_command(build_manager)?; 122 | 123 | aux_cmd.arg("--emit=link"); 124 | let filename = self.aux_file.file_stem().unwrap().to_str().unwrap(); 125 | let output = config.config.run_command(&mut aux_cmd)?; 126 | if !output.status.success() { 127 | let error = Error::Command { 128 | kind: "compilation of aux build failed".to_string(), 129 | status: output.status, 130 | }; 131 | return Err(Errored { 132 | command: format!("{aux_cmd:?}"), 133 | errors: vec![error], 134 | stderr: config.process(&output.stderr).rendered, 135 | stdout: output.stdout, 136 | }); 137 | } 138 | 139 | // Now run the command again to fetch the output filenames 140 | aux_cmd.arg("--print").arg("file-names"); 141 | let output = config.config.run_command(&mut aux_cmd)?; 142 | 143 | assert!(output.status.success()); 144 | 145 | let mut extra_args = vec![]; 146 | for file in output.stdout.lines() { 147 | let file = std::str::from_utf8(file).unwrap(); 148 | let crate_name = filename.replace('-', "_"); 149 | let path = config.config.out_dir.join(file); 150 | extra_args.push("--extern".into()); 151 | let mut cname = OsString::from(&crate_name); 152 | cname.push("="); 153 | cname.push(path); 154 | extra_args.push(cname); 155 | // Help cargo find the crates added with `--extern`. 156 | extra_args.push("-L".into()); 157 | extra_args.push(config.config.out_dir.as_os_str().to_os_string()); 158 | } 159 | Ok(extra_args) 160 | } 161 | 162 | fn description(&self) -> String { 163 | format!("Building aux file {}", display(&self.aux_file)) 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/build_manager.rs: -------------------------------------------------------------------------------- 1 | //! Auxiliary and dependency builder. Extendable to custom builds. 2 | 3 | use crate::{ 4 | per_test_config::TestConfig, 5 | status_emitter::{RevisionStyle, TestStatus}, 6 | test_result::{TestResult, TestRun}, 7 | Config, Errored, 8 | }; 9 | use color_eyre::eyre::Result; 10 | use crossbeam_channel::{bounded, Sender}; 11 | use std::{ 12 | collections::{hash_map::Entry, HashMap}, 13 | ffi::OsString, 14 | sync::{Arc, OnceLock, RwLock}, 15 | }; 16 | 17 | /// A build shared between all tests of the same `BuildManager` 18 | pub trait Build { 19 | /// Runs the build and returns command line args to add to the test so it can find 20 | /// the built things. 21 | fn build(&self, build_manager: &BuildManager) -> Result, Errored>; 22 | /// Must uniquely describe the build, as it is used for checking that a value 23 | /// has already been cached. 24 | fn description(&self) -> String; 25 | } 26 | 27 | /// Deduplicates builds 28 | pub struct BuildManager { 29 | #[allow(clippy::type_complexity)] 30 | cache: RwLock, ()>>>>>, 31 | pub(crate) config: Config, 32 | new_job_submitter: Sender, 33 | } 34 | 35 | /// Type of closure that is used to run individual tests. 36 | pub type NewJob = Box FnOnce(&'a Sender) -> Result<()>>; 37 | 38 | impl BuildManager { 39 | /// Create a new `BuildManager` for a specific `Config`. Each `Config` needs 40 | /// to have its own. 41 | pub fn new(config: Config, new_job_submitter: Sender) -> Self { 42 | Self { 43 | cache: Default::default(), 44 | config, 45 | new_job_submitter, 46 | } 47 | } 48 | 49 | /// Create a new `BuildManager` that cannot create new sub-jobs. 50 | pub fn one_off(config: Config) -> Self { 51 | Self::new(config, bounded(0).0) 52 | } 53 | 54 | /// Lazily add more jobs after a test has finished. These are added to the queue 55 | /// as normally, but nested below the test. 56 | pub fn add_new_job( 57 | &self, 58 | mut config: TestConfig, 59 | job: impl Send + 'static + FnOnce(&mut TestConfig) -> TestResult, 60 | ) { 61 | if self.aborted() { 62 | return; 63 | } 64 | self.new_job_submitter 65 | .send(Box::new(move |sender| { 66 | let result = job(&mut config); 67 | let result = TestRun { 68 | result, 69 | status: config.status, 70 | abort_check: config.config.abort_check, 71 | }; 72 | Ok(sender.send(result)?) 73 | })) 74 | .unwrap() 75 | } 76 | 77 | /// This function will block until the build is done and then return the arguments 78 | /// that need to be passed in order to build the dependencies. 79 | /// The error is only reported once, all follow up invocations of the same build will 80 | /// have a generic error about a previous build failing. 81 | pub fn build( 82 | &self, 83 | what: impl Build, 84 | status: &dyn TestStatus, 85 | ) -> Result, Errored> { 86 | let description = what.description(); 87 | // Fast path without much contention. 88 | if let Some(res) = self 89 | .cache 90 | .read() 91 | .unwrap() 92 | .get(&description) 93 | .and_then(|o| o.get()) 94 | { 95 | return res.clone().map_err(|()| Errored { 96 | command: format!("{description:?}"), 97 | errors: vec![], 98 | stderr: b"previous build failed".to_vec(), 99 | stdout: vec![], 100 | }); 101 | } 102 | let mut lock = self.cache.write().unwrap(); 103 | let once = match lock.entry(description) { 104 | Entry::Occupied(entry) => { 105 | if let Some(res) = entry.get().get() { 106 | return res.clone().map_err(|()| Errored { 107 | command: format!("{:?}", what.description()), 108 | errors: vec![], 109 | stderr: b"previous build failed".to_vec(), 110 | stdout: vec![], 111 | }); 112 | } 113 | entry.get().clone() 114 | } 115 | Entry::Vacant(entry) => { 116 | let once = Arc::new(OnceLock::new()); 117 | entry.insert(once.clone()); 118 | once 119 | } 120 | }; 121 | drop(lock); 122 | 123 | let mut err = None; 124 | once.get_or_init(|| { 125 | let description = what.description(); 126 | let build = status.for_revision(&description, RevisionStyle::Separate); 127 | let res = what.build(self).map_err(|e| err = Some(e)); 128 | build.done( 129 | &res.as_ref() 130 | .map(|_| crate::test_result::TestOk::Ok) 131 | .map_err(|()| Errored { 132 | command: description, 133 | errors: vec![], 134 | stderr: vec![], 135 | stdout: vec![], 136 | }), 137 | self.aborted(), 138 | ); 139 | res 140 | }) 141 | .clone() 142 | .map_err(|()| { 143 | err.unwrap_or_else(|| Errored { 144 | command: what.description(), 145 | errors: vec![], 146 | stderr: b"previous build failed".to_vec(), 147 | stdout: vec![], 148 | }) 149 | }) 150 | } 151 | 152 | /// The `Config` used for all builds. 153 | pub fn config(&self) -> &Config { 154 | &self.config 155 | } 156 | 157 | /// Whether the build was cancelled 158 | pub fn aborted(&self) -> bool { 159 | self.config.abort_check.aborted() 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/cmd.rs: -------------------------------------------------------------------------------- 1 | use crate::display; 2 | use std::{ 3 | ffi::OsString, 4 | path::{Path, PathBuf}, 5 | process::Command, 6 | }; 7 | 8 | #[derive(Debug, Clone)] 9 | /// A command, its args and its environment. Used for 10 | /// the main command, the dependency builder and the cfg-reader. 11 | pub struct CommandBuilder { 12 | /// Path to the binary. 13 | pub program: PathBuf, 14 | /// Arguments to the binary. 15 | pub args: Vec, 16 | /// A flag to prefix before the path to where output files should be written. 17 | pub out_dir_flag: Option, 18 | /// A flag to set as the last flag in the command, so the `build` caller can 19 | /// append the filename themselves. 20 | pub input_file_flag: Option, 21 | /// Environment variables passed to the binary that is executed. 22 | /// The environment variable is removed if the second tuple field is `None` 23 | pub envs: Vec<(OsString, Option)>, 24 | /// A flag to add in order to make the command print the cfgs it supports 25 | pub cfg_flag: Option, 26 | } 27 | 28 | impl CommandBuilder { 29 | /// Uses the `CARGO` env var or just a program named `cargo` and the argument `build`. 30 | pub fn cargo() -> Self { 31 | Self { 32 | program: PathBuf::from(std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into())), 33 | args: vec![ 34 | "build".into(), 35 | "--color=never".into(), 36 | "--quiet".into(), 37 | "--jobs".into(), 38 | "1".into(), 39 | ], 40 | out_dir_flag: Some("--target-dir".into()), 41 | input_file_flag: Some("--manifest-path".into()), 42 | envs: vec![], 43 | cfg_flag: None, 44 | } 45 | } 46 | 47 | /// Uses the `RUSTC` env var or just a program named `rustc` and the argument `--error-format=json`. 48 | /// 49 | /// Take care to only append unless you actually meant to overwrite the defaults. 50 | /// Overwriting the defaults may make `//~ ERROR` style comments stop working. 51 | pub fn rustc() -> Self { 52 | Self { 53 | program: PathBuf::from(std::env::var_os("RUSTC").unwrap_or_else(|| "rustc".into())), 54 | args: vec!["--error-format=json".into()], 55 | out_dir_flag: Some("--out-dir".into()), 56 | input_file_flag: None, 57 | envs: vec![], 58 | cfg_flag: Some("--print=cfg".into()), 59 | } 60 | } 61 | 62 | /// Build a `CommandBuilder` for a command without any argumemnts. 63 | /// You can still add arguments later. 64 | pub fn cmd(cmd: impl Into) -> Self { 65 | Self { 66 | program: cmd.into(), 67 | args: vec![], 68 | out_dir_flag: None, 69 | input_file_flag: None, 70 | envs: vec![], 71 | cfg_flag: None, 72 | } 73 | } 74 | 75 | /// Render the command like you'd use it on a command line. 76 | pub fn display(&self) -> impl std::fmt::Display + '_ { 77 | struct Display<'a>(&'a CommandBuilder); 78 | impl std::fmt::Display for Display<'_> { 79 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 80 | for (var, val) in &self.0.envs { 81 | if let Some(val) = val { 82 | write!(f, "{var:?}={val:?} ")?; 83 | } 84 | } 85 | display(&self.0.program).fmt(f)?; 86 | for arg in &self.0.args { 87 | write!(f, " {arg:?}")?; 88 | } 89 | if let Some(flag) = &self.0.out_dir_flag { 90 | write!(f, " {flag:?} OUT_DIR")?; 91 | } 92 | if let Some(flag) = &self.0.input_file_flag { 93 | write!(f, " {flag:?}")?; 94 | } 95 | Ok(()) 96 | } 97 | } 98 | Display(self) 99 | } 100 | 101 | /// Create a command with the given settings. 102 | pub fn build(&self, out_dir: &Path) -> Command { 103 | let mut cmd = Command::new(&self.program); 104 | cmd.args(self.args.iter()); 105 | if let Some(flag) = &self.out_dir_flag { 106 | cmd.arg(flag).arg(out_dir); 107 | } 108 | if let Some(flag) = &self.input_file_flag { 109 | cmd.arg(flag); 110 | } 111 | self.apply_env(&mut cmd); 112 | cmd 113 | } 114 | 115 | pub(crate) fn apply_env(&self, cmd: &mut Command) { 116 | for (var, val) in self.envs.iter() { 117 | if let Some(val) = val { 118 | cmd.env(var, val); 119 | } else { 120 | cmd.env_remove(var); 121 | } 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/config/args.rs: -------------------------------------------------------------------------------- 1 | //! Default argument processing when `ui_test` is used 2 | //! as a test driver. 3 | 4 | use color_eyre::eyre::{bail, ensure, Result}; 5 | use std::{borrow::Cow, num::NonZeroUsize}; 6 | 7 | /// Plain arguments if `ui_test` is used as a binary. 8 | #[derive(Debug, Default)] 9 | pub struct Args { 10 | /// Filters that will be used to match on individual tests 11 | pub filters: Vec, 12 | 13 | /// Whether to error on mismatches between `.stderr` files and actual 14 | /// output. 15 | pub check: bool, 16 | 17 | /// Whether to overwrite `.stderr` files on mismtach with the actual 18 | /// output. 19 | pub bless: bool, 20 | 21 | /// Only run the test matching the filters exactly. 22 | pub exact: bool, 23 | 24 | /// Whether to only run ignored tests. 25 | pub ignored: bool, 26 | 27 | /// List the tests that can be run. 28 | pub list: bool, 29 | 30 | /// Choose an output format 31 | pub format: Format, 32 | 33 | /// The number of threads to use 34 | pub threads: Option, 35 | 36 | /// Skip tests whose names contain any of these entries. 37 | pub skip: Vec, 38 | } 39 | 40 | /// Possible choices for styling the output. 41 | #[derive(Debug, Copy, Clone, Default)] 42 | pub enum Format { 43 | /// JSON format 44 | JSON, 45 | /// Print one line per test 46 | #[default] 47 | Pretty, 48 | /// Remove test lines once the test finishes and show a progress bar. 49 | Terse, 50 | } 51 | 52 | impl Args { 53 | /// Parse the program arguments. 54 | /// This is meant to be used if `ui_test` is used as a `harness=false` test, called from `cargo test`. 55 | pub fn test() -> Result { 56 | Self::default().parse_args(std::env::args().skip(1)) 57 | } 58 | 59 | /// Parse arguments into an existing `Args` struct. 60 | pub fn parse_args(mut self, mut iter: impl Iterator) -> Result { 61 | while let Some(arg) = iter.next() { 62 | if arg == "--" { 63 | continue; 64 | } 65 | if arg == "--quiet" { 66 | self.format = Format::Terse; 67 | } else if arg == "--check" { 68 | self.check = true; 69 | } else if arg == "--bless" { 70 | self.bless = true; 71 | } else if arg == "--list" { 72 | self.list = true; 73 | } else if arg == "--exact" { 74 | self.exact = true; 75 | } else if arg == "--ignored" { 76 | self.ignored = true; 77 | } else if arg == "--nocapture" { 78 | // We ignore this flag for now. 79 | } else if let Some(format) = parse_value("--format", &arg, &mut iter)? { 80 | self.format = match &*format { 81 | "json" => Format::JSON, 82 | "pretty" => Format::Pretty, 83 | "terse" => Format::Terse, 84 | _ => bail!("unsupported format `{format}`"), 85 | }; 86 | } else if let Some(skip) = parse_value("--skip", &arg, &mut iter)? { 87 | self.skip.push(skip.into_owned()); 88 | } else if arg == "--help" { 89 | bail!("available flags: --quiet, --check, --bless, --test-threads=n, --skip") 90 | } else if let Some(n) = parse_value("--test-threads", &arg, &mut iter)? { 91 | self.threads = Some(n.parse()?); 92 | } else if arg.starts_with("--") { 93 | bail!( 94 | "unknown command line flag `{arg}`: {:?}", 95 | iter.collect::>() 96 | ); 97 | } else { 98 | self.filters.push(arg); 99 | } 100 | } 101 | Ok(self) 102 | } 103 | } 104 | 105 | fn parse_value<'a>( 106 | name: &str, 107 | arg: &'a str, 108 | iter: &mut impl Iterator, 109 | ) -> Result>> { 110 | let with_eq = match arg.strip_prefix(name) { 111 | Some(s) => s, 112 | None => return Ok(None), 113 | }; 114 | if let Some(n) = with_eq.strip_prefix('=') { 115 | Ok(Some(n.into())) 116 | } else { 117 | ensure!(with_eq.is_empty(), "`{name}` can only be followed by `=`"); 118 | 119 | if let Some(next) = iter.next() { 120 | Ok(Some(next.into())) 121 | } else { 122 | bail!("`name` must be followed by a value") 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/core.rs: -------------------------------------------------------------------------------- 1 | //! Basic operations useful for building a testsuite 2 | 3 | use color_eyre::eyre::Result; 4 | use crossbeam_channel::unbounded; 5 | use crossbeam_channel::Receiver; 6 | use crossbeam_channel::Sender; 7 | use regex::bytes::RegexSet; 8 | use std::num::NonZeroUsize; 9 | use std::path::Component; 10 | use std::path::Path; 11 | use std::path::Prefix; 12 | use std::sync::OnceLock; 13 | use std::thread; 14 | 15 | /// Remove the common prefix of this path and the `root_dir`. 16 | pub(crate) fn strip_path_prefix<'a>( 17 | path: &'a Path, 18 | prefix: &Path, 19 | ) -> impl Iterator> { 20 | let mut components = path.components(); 21 | for c in prefix.components() { 22 | // Windows has some funky paths. This is probably wrong, but works well in practice. 23 | let deverbatimize = |c| match c { 24 | Component::Prefix(prefix) => Err(match prefix.kind() { 25 | Prefix::VerbatimUNC(a, b) => Prefix::UNC(a, b), 26 | Prefix::VerbatimDisk(d) => Prefix::Disk(d), 27 | other => other, 28 | }), 29 | c => Ok(c), 30 | }; 31 | let c2 = components.next(); 32 | if Some(deverbatimize(c)) == c2.map(deverbatimize) { 33 | continue; 34 | } 35 | return c2.into_iter().chain(components); 36 | } 37 | None.into_iter().chain(components) 38 | } 39 | 40 | impl CrateType { 41 | /// Heuristic: 42 | /// * [`CrateType::ProcMacro`] if the file contains a [proc macro attribute] 43 | /// * [`CrateType::Test`] if the file contains `#[test]` 44 | /// * [`CrateType::Bin`] if the file contains `fn main()` or `#[start]` 45 | /// * otherwise [`CrateType::Lib`] 46 | /// 47 | /// [proc macro attribute]: https://doc.rust-lang.org/reference/procedural-macros.html 48 | pub fn from_file_contents(file_contents: &[u8]) -> CrateType { 49 | static RE: OnceLock = OnceLock::new(); 50 | let re = RE.get_or_init(|| { 51 | RegexSet::new([ 52 | r"#\[proc_macro(_derive|_attribute)?[\](]", 53 | r"#\[test\]", 54 | r"fn main()|#\[start\]", 55 | ]) 56 | .unwrap() 57 | }); 58 | 59 | match re.matches(file_contents).iter().next() { 60 | Some(0) => CrateType::ProcMacro, 61 | Some(1) => CrateType::Test, 62 | Some(2) => CrateType::Bin, 63 | _ => CrateType::Lib, 64 | } 65 | } 66 | } 67 | 68 | /// The kind of crate we're building here. Corresponds to `--crate-type` flags of rustc 69 | pub enum CrateType { 70 | /// A proc macro 71 | ProcMacro, 72 | /// A file containing unit tests 73 | Test, 74 | /// A binary file containing a main function or start function 75 | Bin, 76 | /// A library crate 77 | Lib, 78 | } 79 | 80 | /// A generic multithreaded runner that has a thread for producing work, 81 | /// a thread for collecting work, and `num_threads` threads for doing the work. 82 | pub fn run_and_collect( 83 | num_threads: NonZeroUsize, 84 | submitter: impl FnOnce([Sender; N]) + Send, 85 | runner: impl Sync + Fn(&[Receiver; N], Sender) -> Result<()>, 86 | collector: impl FnOnce(Receiver) + Send, 87 | ) -> Result<()> { 88 | // A channel for files to process 89 | let (submit, receive): (Vec<_>, Vec<_>) = std::iter::repeat_with(unbounded).take(N).unzip(); 90 | let receive = receive[..].try_into().unwrap(); 91 | let mut submit = submit.into_iter(); 92 | let submit = std::array::from_fn(|_| submit.next().unwrap()); 93 | 94 | thread::scope(|s| { 95 | // Create a thread that is in charge of walking the directory and submitting jobs. 96 | // It closes the channel when it is done. 97 | s.spawn(|| submitter(submit)); 98 | 99 | // A channel for the messages emitted by the individual test threads. 100 | // Used to produce live updates while running the tests. 101 | let (finished_files_sender, finished_files_recv) = unbounded(); 102 | 103 | s.spawn(|| collector(finished_files_recv)); 104 | 105 | let mut threads = vec![]; 106 | 107 | // Create N worker threads that receive files to test. 108 | for _ in 0..num_threads.get() { 109 | let finished_files_sender = finished_files_sender.clone(); 110 | threads.push(s.spawn(|| runner(receive, finished_files_sender))); 111 | } 112 | 113 | for thread in threads { 114 | thread.join().unwrap()?; 115 | } 116 | Ok(()) 117 | }) 118 | } 119 | -------------------------------------------------------------------------------- /src/custom_flags.rs: -------------------------------------------------------------------------------- 1 | //! Define custom test flags not natively supported by ui_test 2 | 3 | use crate::{ 4 | build_manager::BuildManager, parser::Comments, per_test_config::TestConfig, Config, Errored, 5 | }; 6 | use std::{ 7 | panic::{RefUnwindSafe, UnwindSafe}, 8 | process::{Command, Output}, 9 | }; 10 | 11 | #[cfg(feature = "rustc")] 12 | pub mod edition; 13 | #[cfg(feature = "rustc")] 14 | pub mod revision_args; 15 | #[cfg(feature = "rustc")] 16 | pub mod run; 17 | pub mod rustfix; 18 | 19 | /// Tester-specific flag that gets parsed from `//@` comments. 20 | pub trait Flag: Send + Sync + UnwindSafe + RefUnwindSafe + std::fmt::Debug { 21 | /// Clone the boxed value and create a new box. 22 | fn clone_inner(&self) -> Box; 23 | 24 | /// Modify a command to what the flag specifies 25 | fn apply( 26 | &self, 27 | _cmd: &mut Command, 28 | _config: &TestConfig, 29 | _build_manager: &BuildManager, 30 | ) -> Result<(), Errored> { 31 | Ok(()) 32 | } 33 | 34 | /// Whether this flag causes a test to be filtered out 35 | fn test_condition(&self, _config: &Config, _comments: &Comments, _revision: &str) -> bool { 36 | false 37 | } 38 | 39 | /// Run an action after a test is finished. 40 | /// Returns an empty [`Vec`] if no action was taken. 41 | fn post_test_action( 42 | &self, 43 | _config: &TestConfig, 44 | _output: &Output, 45 | _build_manager: &BuildManager, 46 | ) -> Result<(), Errored> { 47 | Ok(()) 48 | } 49 | 50 | /// Whether the flag gets overridden by the same flag in revisions. 51 | fn must_be_unique(&self) -> bool; 52 | } 53 | 54 | /// Use the unit type for when you don't need any behaviour and just need to know if the flag was set or not. 55 | impl Flag for () { 56 | fn clone_inner(&self) -> Box { 57 | Box::new(()) 58 | } 59 | fn must_be_unique(&self) -> bool { 60 | true 61 | } 62 | } 63 | 64 | impl Clone for Box { 65 | fn clone(&self) -> Self { 66 | self.clone_inner() 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/custom_flags/edition.rs: -------------------------------------------------------------------------------- 1 | //! Custom flag for setting the edition for all tests 2 | 3 | use super::Flag; 4 | use crate::{build_manager::BuildManager, per_test_config::TestConfig, Errored}; 5 | 6 | #[derive(Debug)] 7 | /// Set the edition of the tests 8 | pub struct Edition(pub String); 9 | 10 | impl Flag for Edition { 11 | fn must_be_unique(&self) -> bool { 12 | true 13 | } 14 | fn clone_inner(&self) -> Box { 15 | Box::new(Edition(self.0.clone())) 16 | } 17 | 18 | fn apply( 19 | &self, 20 | cmd: &mut std::process::Command, 21 | _config: &TestConfig, 22 | _build_manager: &BuildManager, 23 | ) -> Result<(), Errored> { 24 | cmd.arg("--edition").arg(&self.0); 25 | Ok(()) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/custom_flags/revision_args.rs: -------------------------------------------------------------------------------- 1 | //! Custom flag for setting rustc revision-specific args. 2 | 3 | use super::Flag; 4 | use crate::{build_manager::BuildManager, per_test_config::TestConfig, Errored}; 5 | 6 | /// Set rustc revision-specific args. 7 | #[derive(Clone, Debug)] 8 | pub struct RustcRevisionArgs; 9 | 10 | impl Flag for RustcRevisionArgs { 11 | fn clone_inner(&self) -> Box { 12 | Box::new(self.clone()) 13 | } 14 | 15 | fn must_be_unique(&self) -> bool { 16 | true 17 | } 18 | 19 | fn apply( 20 | &self, 21 | cmd: &mut std::process::Command, 22 | config: &TestConfig, 23 | _build_manager: &BuildManager, 24 | ) -> Result<(), Errored> { 25 | let revision = config.status.revision(); 26 | if !revision.is_empty() { 27 | cmd.arg(format!("--cfg={revision}")); 28 | cmd.arg(format!("-Cextra-filename={revision}")); 29 | } 30 | Ok(()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/custom_flags/run.rs: -------------------------------------------------------------------------------- 1 | //! Types used for running tests after they pass compilation 2 | 3 | use super::Flag; 4 | use crate::{ 5 | build_manager::BuildManager, display, per_test_config::TestConfig, 6 | status_emitter::RevisionStyle, CommandBuilder, Error, Errored, OutputConflictHandling, TestOk, 7 | }; 8 | use bstr::ByteSlice; 9 | use spanned::Spanned; 10 | use std::{path::Path, process::Output}; 11 | 12 | #[derive(Debug, Copy, Clone)] 13 | /// Run a test after successfully compiling it 14 | pub struct Run { 15 | /// The exit code that the test is expected to emit. 16 | pub exit_code: i32, 17 | /// How to handle output conflicts 18 | pub output_conflict_handling: Option, 19 | } 20 | 21 | impl Flag for Run { 22 | fn must_be_unique(&self) -> bool { 23 | true 24 | } 25 | fn clone_inner(&self) -> Box { 26 | Box::new(*self) 27 | } 28 | 29 | fn post_test_action( 30 | &self, 31 | config: &TestConfig, 32 | _output: &Output, 33 | build_manager: &BuildManager, 34 | ) -> Result<(), Errored> { 35 | let mut cmd = config.build_command(build_manager)?; 36 | let exit_code = self.exit_code; 37 | let revision = config.extension("run"); 38 | let mut config = TestConfig { 39 | config: config.config.clone(), 40 | comments: config.comments.clone(), 41 | aux_dir: config.aux_dir.clone(), 42 | status: config.status.for_revision(&revision, RevisionStyle::Show), 43 | }; 44 | if let Some(och) = self.output_conflict_handling { 45 | config.config.output_conflict_handling = och; 46 | } 47 | build_manager.add_new_job(config, move |config| { 48 | cmd.arg("--print").arg("file-names"); 49 | let output = cmd.output().unwrap(); 50 | config.aborted()?; 51 | assert!(output.status.success(), "{cmd:#?}: {output:#?}"); 52 | 53 | let mut files = output.stdout.lines(); 54 | let file = files.next().unwrap(); 55 | assert_eq!(files.next(), None); 56 | let file = std::str::from_utf8(file).unwrap(); 57 | let mut envs = std::mem::take(&mut config.config.program.envs); 58 | config.config.program = CommandBuilder::cmd(config.config.out_dir.join(file)); 59 | envs.extend(config.envs().map(|(k, v)| (k.into(), Some(v.into())))); 60 | config.config.program.envs = envs; 61 | 62 | let mut exe = config.config.program.build(Path::new("")); 63 | let stdin = config 64 | .status 65 | .path() 66 | .with_extension(format!("{revision}.stdin")); 67 | if stdin.exists() { 68 | exe.stdin(std::fs::File::open(stdin).unwrap()); 69 | } 70 | let output = exe.output().unwrap_or_else(|err| { 71 | panic!( 72 | "exe file: {}: {err}", 73 | display(&config.config.program.program) 74 | ) 75 | }); 76 | 77 | config.aborted()?; 78 | 79 | let mut errors = vec![]; 80 | 81 | config.check_test_output(&mut errors, &output.stdout, &output.stderr); 82 | 83 | let status = output.status; 84 | if status.code() != Some(exit_code) { 85 | errors.push(Error::ExitStatus { 86 | status, 87 | expected: exit_code, 88 | reason: match (exit_code, status.code()) { 89 | (_, Some(101)) => get_panic_span(&output.stderr), 90 | (0, _) => { 91 | Spanned::dummy("the test was expected to run successfully".into()) 92 | } 93 | (101, _) => Spanned::dummy("the test was expected to panic".into()), 94 | _ => Spanned::dummy(String::new()), 95 | }, 96 | }) 97 | } 98 | if errors.is_empty() { 99 | Ok(TestOk::Ok) 100 | } else { 101 | Err(Errored { 102 | command: format!("{exe:?}"), 103 | errors, 104 | stderr: output.stderr, 105 | stdout: output.stdout, 106 | }) 107 | } 108 | }); 109 | Ok(()) 110 | } 111 | } 112 | 113 | fn get_panic_span(stderr: &[u8]) -> Spanned { 114 | let mut lines = stderr.lines(); 115 | while let Some(line) = lines.next() { 116 | if let Some((_, location)) = line.split_once_str(b"panicked at ") { 117 | let mut parts = location.split(|&c| c == b':'); 118 | let Some(filename) = parts.next() else { 119 | continue; 120 | }; 121 | let Some(line) = parts.next() else { continue }; 122 | let Some(col) = parts.next() else { continue }; 123 | let message = lines 124 | .next() 125 | .and_then(|msg| msg.to_str().ok()) 126 | .unwrap_or("the test panicked during execution"); 127 | let Ok(line) = line.to_str() else { continue }; 128 | let Ok(col) = col.to_str() else { continue }; 129 | let Ok(filename) = filename.to_str() else { 130 | continue; 131 | }; 132 | let Ok(line) = line.parse::() else { 133 | continue; 134 | }; 135 | let Ok(col) = col.parse::() else { 136 | continue; 137 | }; 138 | let Ok(file) = Spanned::read_from_file(filename) else { 139 | continue; 140 | }; 141 | let Some(line) = line.checked_sub(1) else { 142 | continue; 143 | }; 144 | let Some(line) = file.lines().nth(line) else { 145 | continue; 146 | }; 147 | let Some(col) = col.checked_sub(1) else { 148 | continue; 149 | }; 150 | let span = line.span.inc_col_start(col); 151 | return Spanned::new(message.into(), span); 152 | } 153 | } 154 | Spanned::dummy("".into()) 155 | } 156 | -------------------------------------------------------------------------------- /src/diagnostics.rs: -------------------------------------------------------------------------------- 1 | //! Data structures for handling diagnostic output from tests. 2 | 3 | use std::path::Path; 4 | 5 | #[cfg(feature = "rustc")] 6 | pub mod rustc; 7 | 8 | /// Default diagnostics extractor that does nothing. 9 | pub fn default_diagnostics_extractor(_path: &Path, _stderr: &[u8]) -> Diagnostics { 10 | Diagnostics::default() 11 | } 12 | 13 | /// The different levels of diagnostic messages and their relative ranking. 14 | #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] 15 | pub enum Level { 16 | /// internal compiler errors 17 | Ice = 5, 18 | /// ´error´ level messages 19 | Error = 4, 20 | /// ´warn´ level messages 21 | Warn = 3, 22 | /// ´help´ level messages 23 | Help = 2, 24 | /// ´note´ level messages 25 | Note = 1, 26 | /// Only used for "For more information about this error, try `rustc --explain EXXXX`". 27 | FailureNote = 0, 28 | } 29 | 30 | impl std::str::FromStr for Level { 31 | type Err = String; 32 | fn from_str(s: &str) -> Result { 33 | match s { 34 | "ERROR" | "error" => Ok(Self::Error), 35 | "WARN" | "warning" => Ok(Self::Warn), 36 | "HELP" | "help" => Ok(Self::Help), 37 | "NOTE" | "note" => Ok(Self::Note), 38 | "failure-note" => Ok(Self::FailureNote), 39 | "ICE" | "ice" => Ok(Self::Ice), 40 | _ => Err(format!("unknown level `{s}`")), 41 | } 42 | } 43 | } 44 | 45 | /// A diagnostic message. 46 | #[derive(Debug)] 47 | pub struct Message { 48 | /// The diagnostic level at which this message was emitted 49 | pub level: Level, 50 | /// The main message of the diagnostic (what will be matched for with `//~`) 51 | pub message: String, 52 | /// Information about where in the file the message was emitted 53 | pub line: Option, 54 | /// Exact span information of the message 55 | pub span: Option, 56 | /// Identifier of the message (E0XXX for rustc errors, or lint names) 57 | pub code: Option, 58 | } 59 | 60 | /// All the diagnostics that were emitted in a test. 61 | #[derive(Default, Debug)] 62 | pub struct Diagnostics { 63 | /// Rendered and concatenated version of all diagnostics. 64 | /// This is equivalent to non-json diagnostics. 65 | pub rendered: Vec, 66 | /// Per line, a list of messages for that line. 67 | pub messages: Vec>, 68 | /// Messages not on any line (usually because they are from libstd) 69 | pub messages_from_unknown_file_or_line: Vec, 70 | } 71 | -------------------------------------------------------------------------------- /src/diagnostics/rustc.rs: -------------------------------------------------------------------------------- 1 | //! `rustc` and `cargo` diagnostics extractors. 2 | //! 3 | //! These parse diagnostics from the respective stderr JSON output using the 4 | //! data structures defined in [`cargo_metadata::diagnostic`]. 5 | 6 | use super::{Diagnostics, Level, Message}; 7 | use bstr::ByteSlice; 8 | use cargo_metadata::diagnostic::{Diagnostic, DiagnosticLevel, DiagnosticSpan}; 9 | use regex::Regex; 10 | use std::{ 11 | path::{Path, PathBuf}, 12 | sync::OnceLock, 13 | }; 14 | 15 | fn diag_line(diag: &Diagnostic, file: &Path) -> Option<(spanned::Span, usize)> { 16 | let span = |primary| { 17 | diag.spans 18 | .iter() 19 | .find_map(|span| span_line(span, file, primary)) 20 | }; 21 | span(true).or_else(|| span(false)) 22 | } 23 | 24 | /// Put the message and its children into the line-indexed list. 25 | fn insert_recursive( 26 | diag: Diagnostic, 27 | file: &Path, 28 | messages: &mut Vec>, 29 | messages_from_unknown_file_or_line: &mut Vec, 30 | line: Option<(spanned::Span, usize)>, 31 | ) { 32 | let line = diag_line(&diag, file).or(line); 33 | let msg = Message { 34 | level: diag.level.into(), 35 | message: diag.message, 36 | line: line.as_ref().map(|&(_, l)| l), 37 | span: line.as_ref().map(|(s, _)| s.clone()), 38 | code: diag.code.map(|x| x.code), 39 | }; 40 | if let Some((_, line)) = line.clone() { 41 | if messages.len() <= line { 42 | messages.resize_with(line + 1, Vec::new); 43 | } 44 | messages[line].push(msg); 45 | // All other messages go into the general bin, unless they are specifically of the 46 | // "aborting due to X previous errors" variety, as we never want to match those. They 47 | // only count the number of errors and provide no useful information about the tests. 48 | } else if !(msg.message.starts_with("aborting due to") 49 | && msg.message.contains("previous error")) 50 | { 51 | messages_from_unknown_file_or_line.push(msg); 52 | } 53 | for child in diag.children { 54 | insert_recursive( 55 | child, 56 | file, 57 | messages, 58 | messages_from_unknown_file_or_line, 59 | line.clone(), 60 | ) 61 | } 62 | } 63 | 64 | /// Returns the most expanded line number *in the given file*, if possible. 65 | fn span_line(span: &DiagnosticSpan, file: &Path, primary: bool) -> Option<(spanned::Span, usize)> { 66 | let file_name = PathBuf::from(&span.file_name); 67 | if let Some(exp) = &span.expansion { 68 | if let Some(line) = span_line(&exp.span, file, !primary || span.is_primary) { 69 | return Some(line); 70 | } else if file_name != file { 71 | return if !primary && span.is_primary { 72 | span_line(&exp.span, file, false) 73 | } else { 74 | None 75 | }; 76 | } 77 | } 78 | ((!primary || span.is_primary) && file_name == file).then(|| { 79 | let span = || { 80 | Some(( 81 | spanned::Span { 82 | file: file_name, 83 | bytes: usize::try_from(span.byte_start).unwrap() 84 | ..usize::try_from(span.byte_end).unwrap(), 85 | }, 86 | span.line_start, 87 | )) 88 | }; 89 | span().unwrap_or_default() 90 | }) 91 | } 92 | 93 | fn filter_annotations_from_rendered(rendered: &str) -> std::borrow::Cow<'_, str> { 94 | static ANNOTATIONS_RE: OnceLock = OnceLock::new(); 95 | ANNOTATIONS_RE 96 | .get_or_init(|| Regex::new(r" *//(\[[a-z,]+\])?~.*").unwrap()) 97 | .replace_all(rendered, "") 98 | } 99 | 100 | /// `rustc` diagnostics extractor. 101 | pub fn rustc_diagnostics_extractor(file: &Path, stderr: &[u8]) -> Diagnostics { 102 | let mut rendered = Vec::new(); 103 | let mut messages = vec![]; 104 | let mut messages_from_unknown_file_or_line = vec![]; 105 | for line in stderr.lines_with_terminator() { 106 | if line.starts_with_str(b"{") { 107 | let msg = 108 | serde_json::from_slice::(line).unwrap(); 109 | 110 | rendered.extend( 111 | filter_annotations_from_rendered(msg.rendered.as_ref().unwrap()).as_bytes(), 112 | ); 113 | insert_recursive( 114 | msg, 115 | file, 116 | &mut messages, 117 | &mut messages_from_unknown_file_or_line, 118 | None, 119 | ); 120 | } else { 121 | // FIXME: do we want to throw interpreter stderr into a separate file? 122 | rendered.extend(line); 123 | } 124 | } 125 | Diagnostics { 126 | rendered, 127 | messages, 128 | messages_from_unknown_file_or_line, 129 | } 130 | } 131 | 132 | /// `cargo` diagnostics extractor. 133 | pub fn cargo_diagnostics_extractor(file: &Path, stderr: &[u8]) -> Diagnostics { 134 | let mut rendered = Vec::new(); 135 | let mut messages = vec![]; 136 | let mut messages_from_unknown_file_or_line = vec![]; 137 | for message in cargo_metadata::Message::parse_stream(stderr) { 138 | match message.unwrap() { 139 | cargo_metadata::Message::CompilerMessage(msg) => { 140 | let msg = msg.message; 141 | rendered.extend( 142 | filter_annotations_from_rendered(msg.rendered.as_ref().unwrap()).as_bytes(), 143 | ); 144 | insert_recursive( 145 | msg, 146 | file, 147 | &mut messages, 148 | &mut messages_from_unknown_file_or_line, 149 | None, 150 | ); 151 | } 152 | cargo_metadata::Message::TextLine(line) => { 153 | rendered.extend(line.bytes()); 154 | rendered.push(b'\n') 155 | } 156 | _ => {} 157 | } 158 | } 159 | Diagnostics { 160 | rendered, 161 | messages, 162 | messages_from_unknown_file_or_line, 163 | } 164 | } 165 | 166 | impl From for Level { 167 | fn from(value: DiagnosticLevel) -> Self { 168 | match value { 169 | DiagnosticLevel::Ice => Level::Ice, 170 | DiagnosticLevel::Error => Level::Error, 171 | DiagnosticLevel::Warning => Level::Warn, 172 | DiagnosticLevel::FailureNote => Level::FailureNote, 173 | DiagnosticLevel::Note => Level::Note, 174 | DiagnosticLevel::Help => Level::Help, 175 | other => panic!("rustc got a new kind of diagnostic level: {other:?}"), 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/diff.rs: -------------------------------------------------------------------------------- 1 | use colored::*; 2 | use prettydiff::{basic::DiffOp, basic::DiffOp::*, diff_lines, diff_words}; 3 | 4 | /// How many lines of context are displayed around the actual diffs 5 | const CONTEXT: usize = 2; 6 | 7 | fn skip(skipped_lines: &[&str]) { 8 | // When the amount of skipped lines is exactly `CONTEXT * 2`, we already 9 | // print all the context and don't actually skip anything. 10 | match skipped_lines.len().checked_sub(CONTEXT * 2) { 11 | Some(skipped @ 2..) => { 12 | // Print an initial `CONTEXT` amount of lines. 13 | for line in &skipped_lines[..CONTEXT] { 14 | println!(" {line}"); 15 | } 16 | println!("... {skipped} lines skipped ..."); 17 | // Print `... n lines skipped ...` followed by the last `CONTEXT` lines. 18 | for line in &skipped_lines[skipped + CONTEXT..] { 19 | println!(" {line}"); 20 | } 21 | } 22 | _ => { 23 | // Print all the skipped lines if the amount of context desired is less than the amount of lines 24 | for line in skipped_lines { 25 | println!(" {line}"); 26 | } 27 | } 28 | } 29 | } 30 | 31 | fn row(row: DiffOp<'_, &str>) { 32 | match row { 33 | Remove(l) => { 34 | for l in l { 35 | println!("{}{}", "-".red(), l.red()); 36 | } 37 | } 38 | Equal(l) => { 39 | skip(l); 40 | } 41 | Replace(l, r) => { 42 | if l.len() == r.len() { 43 | for (l, r) in l.iter().zip(r) { 44 | print_line_diff(l, r); 45 | } 46 | } else { 47 | for l in l { 48 | println!("{}{}", "-".red(), l.red()); 49 | } 50 | for r in r { 51 | println!("{}{}", "+".green(), r.green()); 52 | } 53 | } 54 | } 55 | Insert(r) => { 56 | for r in r { 57 | println!("{}{}", "+".green(), r.green()); 58 | } 59 | } 60 | } 61 | } 62 | 63 | fn print_line_diff(l: &str, r: &str) { 64 | let diff = diff_words(l, r); 65 | let diff = diff.diff(); 66 | if has_both_insertions_and_deletions(&diff) 67 | || !colored::control::SHOULD_COLORIZE.should_colorize() 68 | { 69 | // The line both adds and removes chars, print both lines, but highlight their differences instead of 70 | // drawing the entire line in red/green. 71 | print!("{}", "-".red()); 72 | for char in &diff { 73 | match *char { 74 | Replace(l, _) | Remove(l) => { 75 | for l in l { 76 | print!("{}", l.to_string().on_red()) 77 | } 78 | } 79 | Insert(_) => {} 80 | Equal(l) => { 81 | for l in l { 82 | print!("{l}") 83 | } 84 | } 85 | } 86 | } 87 | println!(); 88 | print!("{}", "+".green()); 89 | for char in diff { 90 | match char { 91 | Remove(_) => {} 92 | Replace(_, r) | Insert(r) => { 93 | for r in r { 94 | print!("{}", r.to_string().on_green()) 95 | } 96 | } 97 | Equal(r) => { 98 | for r in r { 99 | print!("{r}") 100 | } 101 | } 102 | } 103 | } 104 | println!(); 105 | } else { 106 | // The line only adds or only removes chars, print a single line highlighting their differences. 107 | print!("{}", "~".yellow()); 108 | for char in diff { 109 | match char { 110 | Remove(l) => { 111 | for l in l { 112 | print!("{}", l.to_string().on_red()) 113 | } 114 | } 115 | Equal(w) => { 116 | for w in w { 117 | print!("{w}") 118 | } 119 | } 120 | Insert(r) => { 121 | for r in r { 122 | print!("{}", r.to_string().on_green()) 123 | } 124 | } 125 | Replace(l, r) => { 126 | for l in l { 127 | print!("{}", l.to_string().on_red()) 128 | } 129 | for r in r { 130 | print!("{}", r.to_string().on_green()) 131 | } 132 | } 133 | } 134 | } 135 | println!(); 136 | } 137 | } 138 | 139 | fn has_both_insertions_and_deletions(diff: &[DiffOp<'_, &str>]) -> bool { 140 | let mut seen_l = false; 141 | let mut seen_r = false; 142 | for char in diff { 143 | let is_whitespace = |s: &[&str]| s.iter().any(|s| s.chars().any(|s| s.is_whitespace())); 144 | match char { 145 | Insert(l) if !is_whitespace(l) => seen_l = true, 146 | Remove(r) if !is_whitespace(r) => seen_r = true, 147 | Replace(l, r) if !is_whitespace(l) && !is_whitespace(r) => return true, 148 | _ => {} 149 | } 150 | } 151 | seen_l && seen_r 152 | } 153 | 154 | pub(crate) fn print_diff(expected: &[u8], actual: &[u8]) { 155 | let expected_str = String::from_utf8_lossy(expected); 156 | let actual_str = String::from_utf8_lossy(actual); 157 | 158 | if expected_str.as_bytes() != expected || actual_str.as_bytes() != actual { 159 | println!( 160 | "{}", 161 | "Non-UTF8 characters in output, diff may be imprecise.".red() 162 | ); 163 | } 164 | 165 | let pat = |c: char| c.is_whitespace() && c != ' ' && c != '\n' && c != '\r'; 166 | let expected_str = expected_str.replace(pat, "░"); 167 | let actual_str = actual_str.replace(pat, "░"); 168 | 169 | for r in diff_lines(&expected_str, &actual_str).diff() { 170 | row(r); 171 | } 172 | println!() 173 | } 174 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | diagnostics::Message, 3 | parser::{Pattern, Span, Spanned}, 4 | }; 5 | use std::{num::NonZeroUsize, path::PathBuf, process::ExitStatus}; 6 | 7 | /// All the ways in which a test can fail. 8 | #[derive(Debug)] 9 | #[must_use] 10 | pub enum Error { 11 | /// Got an invalid exit status. 12 | ExitStatus { 13 | /// The exit status of the command. 14 | status: ExitStatus, 15 | /// The expected exit status as set in the file or derived from the mode. 16 | expected: i32, 17 | /// A reason for why the expected exit status was expected 18 | reason: Spanned, 19 | }, 20 | /// A pattern was declared but had no matching error. 21 | PatternNotFound { 22 | /// The pattern that was not found, and the span of where that pattern was declared. 23 | pattern: Spanned, 24 | /// Can be `None` when it is expected outside the current file 25 | expected_line: Option, 26 | }, 27 | /// A diagnostic code matcher was declared but had no matching error. 28 | CodeNotFound { 29 | /// The code that was not found, and the span of where that code was declared. 30 | code: Spanned, 31 | /// Can be `None` when it is expected outside the current file 32 | expected_line: Option, 33 | }, 34 | /// A ui test checking for failure does not have any failure patterns 35 | NoPatternsFound, 36 | /// A ui test checking for success has failure patterns 37 | PatternFoundInPassTest { 38 | /// Span of a flag changing the mode (if changed from default). 39 | mode: Span, 40 | /// Span of the pattern 41 | span: Span, 42 | }, 43 | /// Stderr/Stdout differed from the `.stderr`/`.stdout` file present. 44 | OutputDiffers { 45 | /// The file containing the expected output that differs from the actual output. 46 | path: PathBuf, 47 | /// The normalized output from the command. 48 | actual: Vec, 49 | /// The unnormalized output from the command. 50 | output: Vec, 51 | /// The contents of the file. 52 | expected: Vec, 53 | /// A command, that when run, causes the output to get blessed instead of erroring. 54 | bless_command: Option, 55 | }, 56 | /// There were errors that don't have a pattern. 57 | ErrorsWithoutPattern { 58 | /// The main message of the error. 59 | msgs: Vec, 60 | /// File and line information of the error. 61 | path: Option<(PathBuf, NonZeroUsize)>, 62 | }, 63 | /// A comment failed to parse. 64 | InvalidComment { 65 | /// The comment 66 | msg: String, 67 | /// The character range in which it was defined. 68 | span: Span, 69 | }, 70 | /// An invalid setting was used. 71 | ConfigError(String), 72 | /// Conflicting comments 73 | MultipleRevisionsWithResults { 74 | /// The comment being looked for 75 | kind: String, 76 | /// The lines where conflicts happened 77 | lines: Vec, 78 | }, 79 | /// A subcommand (e.g. rustfix) of a test failed. 80 | Command { 81 | /// The name of the subcommand (e.g. "rustfix"). 82 | kind: String, 83 | /// The exit status of the command. 84 | status: ExitStatus, 85 | }, 86 | /// This catches crashes of ui tests and reports them along the failed test. 87 | Bug(String), 88 | /// An auxiliary build failed with its own set of errors. 89 | Aux { 90 | /// Path to the aux file. 91 | path: Spanned, 92 | /// The errors that occurred during the build of the aux file. 93 | errors: Vec, 94 | }, 95 | /// An error occured applying [`rustfix`] suggestions 96 | Rustfix(anyhow::Error), 97 | } 98 | 99 | pub(crate) type Errors = Vec; 100 | -------------------------------------------------------------------------------- /src/filter.rs: -------------------------------------------------------------------------------- 1 | //! Datastructures and operations used for normalizing test output. 2 | 3 | use crate::display; 4 | use bstr::ByteSlice; 5 | use regex::bytes::{Captures, Regex}; 6 | use std::borrow::Cow; 7 | use std::path::Path; 8 | use std::sync::OnceLock; 9 | 10 | /// A filter's match rule. 11 | #[derive(Clone, Debug)] 12 | pub enum Match { 13 | /// If the regex matches, the filter applies 14 | Regex(Regex), 15 | /// If the exact byte sequence is found, the filter applies 16 | Exact(Vec), 17 | /// Uses a heuristic to find backslashes in windows style paths 18 | PathBackslash, 19 | } 20 | 21 | impl Match { 22 | pub(crate) fn replace_all<'a>(&self, text: &'a [u8], replacement: &[u8]) -> Cow<'a, [u8]> { 23 | match self { 24 | Match::Regex(regex) => regex.replace_all(text, replacement), 25 | Match::Exact(needle) => text.replace(needle, replacement).into(), 26 | Match::PathBackslash => { 27 | static PATH_RE: OnceLock = OnceLock::new(); 28 | PATH_RE 29 | .get_or_init(|| { 30 | Regex::new( 31 | r"(?x) 32 | (?: 33 | # Match paths to files with extensions that don't include spaces 34 | \\(?:[\pL\pN.\-_']+[/\\])*[\pL\pN.\-_']+\.\pL+ 35 | | 36 | # Allow spaces in absolute paths 37 | [A-Z]:\\(?:[\pL\pN.\-_'\ ]+[/\\])+ 38 | )", 39 | ) 40 | .unwrap() 41 | }) 42 | .replace_all(text, |caps: &Captures<'_>| { 43 | caps[0].replace(r"\", replacement) 44 | }) 45 | } 46 | } 47 | } 48 | } 49 | 50 | impl From<&'_ Path> for Match { 51 | fn from(v: &Path) -> Self { 52 | let mut v = display(v); 53 | // Normalize away windows canonicalized paths. 54 | if v.starts_with(r"//?/") { 55 | v.drain(0..4); 56 | } 57 | Self::Exact(v.into_bytes()) 58 | } 59 | } 60 | 61 | impl From for Match { 62 | fn from(v: Regex) -> Self { 63 | Self::Regex(v) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/github_actions.rs: -------------------------------------------------------------------------------- 1 | //! An interface to github actions workflow commands. 2 | 3 | use std::{ 4 | fmt::{Debug, Write}, 5 | num::NonZeroUsize, 6 | }; 7 | 8 | /// Shows an error message directly in a github diff view on drop. 9 | pub struct Error { 10 | file: String, 11 | line: usize, 12 | title: String, 13 | message: String, 14 | } 15 | impl Error { 16 | /// Set a line for this error. By default the message is shown at the top of the file. 17 | pub fn line(mut self, line: NonZeroUsize) -> Self { 18 | self.line = line.get(); 19 | self 20 | } 21 | } 22 | 23 | /// Create an error to be shown for the given file and with the given title. 24 | pub fn error(file: impl std::fmt::Display, title: impl Into) -> Error { 25 | Error { 26 | file: file.to_string(), 27 | line: 0, 28 | title: title.into(), 29 | message: String::new(), 30 | } 31 | } 32 | 33 | impl Write for Error { 34 | fn write_str(&mut self, s: &str) -> std::fmt::Result { 35 | self.message.write_str(s) 36 | } 37 | } 38 | 39 | impl Drop for Error { 40 | fn drop(&mut self) { 41 | if std::env::var_os("GITHUB_ACTION").is_some() { 42 | let Error { 43 | file, 44 | line, 45 | title, 46 | message, 47 | } = self; 48 | let message = message.trim(); 49 | let message = if message.is_empty() { 50 | "::no message".into() 51 | } else { 52 | format!("::{}", github_action_multiline_escape(message)) 53 | }; 54 | println!("::error file={file},line={line},title={title}{message}"); 55 | } 56 | } 57 | } 58 | 59 | /// Append to the summary file that will be shown for the entire CI run. 60 | pub fn summary() -> Option { 61 | let path = std::env::var_os("GITHUB_STEP_SUMMARY")?; 62 | Some(std::fs::OpenOptions::new().append(true).open(path).unwrap()) 63 | } 64 | 65 | fn github_action_multiline_escape(s: &str) -> String { 66 | s.replace('%', "%25") 67 | .replace('\n', "%0A") 68 | .replace('\r', "%0D") 69 | } 70 | 71 | /// All github actions log messages from this call to the Drop of the return value 72 | /// will be grouped and hidden by default in logs. Note that nesting these does 73 | /// not really work. 74 | pub fn group(name: impl std::fmt::Display) -> Group { 75 | if std::env::var_os("GITHUB_ACTION").is_some() { 76 | println!("::group::{name}"); 77 | } 78 | Group(()) 79 | } 80 | 81 | /// A guard that closes the current github actions log group on drop. 82 | pub struct Group(()); 83 | 84 | impl Debug for Group { 85 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 86 | f.write_str("a handle that will close the github action group on drop") 87 | } 88 | } 89 | 90 | impl Drop for Group { 91 | fn drop(&mut self) { 92 | if std::env::var_os("GITHUB_ACTION").is_some() { 93 | println!("::endgroup::"); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/mode.rs: -------------------------------------------------------------------------------- 1 | use super::Error; 2 | use crate::{per_test_config::TestConfig, Errored}; 3 | use spanned::Spanned; 4 | use std::process::ExitStatus; 5 | 6 | impl TestConfig { 7 | #[allow(clippy::result_large_err)] 8 | pub(crate) fn ok(&self, status: ExitStatus) -> Result, Errored> { 9 | let Some(expected) = self.exit_status()? else { 10 | return Ok(None); 11 | }; 12 | if status.code() == Some(*expected) { 13 | Ok(None) 14 | } else { 15 | let span = expected.span.clone(); 16 | let expected = expected.content; 17 | Ok(Some(Error::ExitStatus { 18 | status, 19 | expected, 20 | reason: Spanned::new( 21 | match (expected, status.code()) { 22 | (_, Some(101)) => "the compiler panicked", 23 | (0, Some(1)) => "compilation failed, but was expected to succeed", 24 | (1, Some(0)) => "compilation succeeded, but was expected to fail", 25 | _ => "", 26 | } 27 | .into(), 28 | span, 29 | ), 30 | })) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/nextest.rs: -------------------------------------------------------------------------------- 1 | //! Helper functions for nextest emulation. 2 | 3 | use crate::Config; 4 | 5 | /// Nexttest emulation: we act as if we are one single test. 6 | /// Returns `true` if we should not run any tests. 7 | /// Patches up the `Config`s to have appropriate filters. 8 | pub fn emulate(configs: &mut Vec) -> bool { 9 | if configs.iter().any(|c| c.list) { 10 | if configs.iter().any(|c| !c.run_only_ignored) { 11 | println!("ui_test: test"); 12 | } 13 | return true; 14 | } 15 | for config in configs { 16 | if config.filter_exact 17 | && config.filter_files.len() == 1 18 | && config.filter_files[0] == "ui_test" 19 | { 20 | config.filter_exact = false; 21 | config.filter_files.clear(); 22 | } 23 | } 24 | false 25 | } 26 | -------------------------------------------------------------------------------- /src/parser/spanned.rs: -------------------------------------------------------------------------------- 1 | pub(crate) use spanned::*; 2 | 3 | /// An optional spanned value. 4 | #[derive(Debug, Clone)] 5 | pub struct OptWithLine(Option>); 6 | 7 | impl std::ops::Deref for OptWithLine { 8 | type Target = Option>; 9 | 10 | fn deref(&self) -> &Self::Target { 11 | &self.0 12 | } 13 | } 14 | 15 | impl From>> for OptWithLine { 16 | fn from(value: Option>) -> Self { 17 | Self(value) 18 | } 19 | } 20 | 21 | impl From> for OptWithLine { 22 | fn from(value: Spanned) -> Self { 23 | Self(Some(value)) 24 | } 25 | } 26 | 27 | impl Default for OptWithLine { 28 | fn default() -> Self { 29 | Self(Default::default()) 30 | } 31 | } 32 | 33 | impl OptWithLine { 34 | /// Creates a new optional spanned value. 35 | pub fn new(data: T, span: Span) -> Self { 36 | Self(Some(Spanned::new(data, span))) 37 | } 38 | 39 | /// Tries to set the value if not already set. Returns newly passed 40 | /// value in case there was already a value there. 41 | #[must_use] 42 | pub fn set(&mut self, data: T, span: Span) -> Option> { 43 | let new = Spanned::new(data, span); 44 | if self.0.is_some() { 45 | Some(new) 46 | } else { 47 | self.0 = Some(new); 48 | None 49 | } 50 | } 51 | 52 | /// Consumes `self` and returns the inner value. 53 | #[must_use] 54 | pub fn into_inner(self) -> Option> { 55 | self.0 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/status_emitter/debug.rs: -------------------------------------------------------------------------------- 1 | //! A debug emitter for when things get stuck. Mostly useful for debugging of ui_test itself 2 | 3 | use crate::Error; 4 | use std::path::{Path, PathBuf}; 5 | 6 | /// Very verbose status emitter 7 | pub struct StatusEmitter; 8 | 9 | impl super::StatusEmitter for StatusEmitter { 10 | fn register_test(&self, path: PathBuf) -> Box { 11 | eprintln!("START {}", path.display()); 12 | Box::new(TestStatus(path, String::new())) 13 | } 14 | 15 | fn finalize( 16 | &self, 17 | failed: usize, 18 | succeeded: usize, 19 | ignored: usize, 20 | filtered: usize, 21 | aborted: bool, 22 | ) -> Box { 23 | eprintln!("{failed}, {succeeded}, {ignored}, {filtered}, {aborted}"); 24 | Box::new(Summary) 25 | } 26 | } 27 | 28 | struct Summary; 29 | 30 | impl super::Summary for Summary { 31 | fn test_failure(&mut self, status: &dyn super::TestStatus, errors: &Vec) { 32 | eprintln!( 33 | "FAILED: {} ({})", 34 | status.path().display(), 35 | status.revision() 36 | ); 37 | eprintln!("{errors:#?}"); 38 | } 39 | } 40 | 41 | struct TestStatus(PathBuf, String); 42 | 43 | impl super::TestStatus for TestStatus { 44 | fn for_revision( 45 | &self, 46 | revision: &str, 47 | _style: super::RevisionStyle, 48 | ) -> Box { 49 | eprintln!( 50 | "REVISION {}: {} (old: {})", 51 | self.0.display(), 52 | revision, 53 | self.1 54 | ); 55 | Box::new(TestStatus(self.0.clone(), revision.to_string())) 56 | } 57 | 58 | fn for_path(&self, path: &Path) -> Box { 59 | eprintln!( 60 | "PATH {} (old: {} ({}))", 61 | path.display(), 62 | self.0.display(), 63 | self.1 64 | ); 65 | Box::new(TestStatus(path.to_owned(), String::new())) 66 | } 67 | 68 | fn failed_test<'a>( 69 | &'a self, 70 | cmd: &'a str, 71 | stderr: &'a [u8], 72 | stdout: &'a [u8], 73 | ) -> Box { 74 | eprintln!("failed {} ({})", self.0.display(), self.1); 75 | eprintln!("{cmd}"); 76 | eprintln!("{}", std::str::from_utf8(stderr).unwrap()); 77 | eprintln!("{}", std::str::from_utf8(stdout).unwrap()); 78 | eprintln!(); 79 | Box::new(()) 80 | } 81 | 82 | fn path(&self) -> &Path { 83 | &self.0 84 | } 85 | 86 | fn revision(&self) -> &str { 87 | &self.1 88 | } 89 | } 90 | 91 | impl Drop for TestStatus { 92 | fn drop(&mut self) { 93 | eprintln!("DONE {} ({})", self.0.display(), self.1); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/test_result.rs: -------------------------------------------------------------------------------- 1 | //! Various data structures used for carrying information about test success or failure 2 | 3 | use crate::{status_emitter::TestStatus, AbortCheck, Error}; 4 | use bstr::ByteSlice; 5 | use color_eyre::eyre::Result; 6 | 7 | /// The possible non-failure results a single test can have. 8 | #[derive(Debug)] 9 | pub enum TestOk { 10 | /// The test passed 11 | Ok, 12 | /// The test was ignored due to a rule (`//@only-*` or `//@ignore-*`) 13 | Ignored, 14 | } 15 | 16 | /// The possible results a single test can have. 17 | pub type TestResult = Result; 18 | 19 | /// Information about a test failure. 20 | pub struct Errored { 21 | /// Command that failed 22 | pub(crate) command: String, 23 | /// The errors that were encountered. 24 | pub(crate) errors: Vec, 25 | /// The full stderr of the test run. 26 | pub(crate) stderr: Vec, 27 | /// The full stdout of the test run. 28 | pub(crate) stdout: Vec, 29 | } 30 | 31 | impl std::fmt::Debug for Errored { 32 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 33 | writeln!(f, "command: {}", self.command)?; 34 | writeln!(f, "errors: {:#?}", self.errors)?; 35 | writeln!(f, "stderr: {}", self.stderr.to_str_lossy())?; 36 | writeln!(f, "stdout: {}", self.stdout.to_str_lossy())?; 37 | Ok(()) 38 | } 39 | } 40 | 41 | impl Errored { 42 | /// If no command was executed for this error, use a message instead. 43 | pub fn new(errors: Vec, message: &str) -> Self { 44 | Self { 45 | errors, 46 | stderr: vec![], 47 | stdout: vec![], 48 | command: message.into(), 49 | } 50 | } 51 | 52 | pub(crate) fn aborted() -> Errored { 53 | Self::new(vec![], "aborted") 54 | } 55 | } 56 | 57 | /// Result of an actual test or sub-test (revision, fixed, run, ...) including its status. 58 | pub struct TestRun { 59 | /// Actual test run output. 60 | pub result: TestResult, 61 | /// Usually created via `for_revsion` or `for_path` 62 | pub status: Box, 63 | /// Whether the run was aborted prematurely 64 | pub abort_check: AbortCheck, 65 | } 66 | -------------------------------------------------------------------------------- /tests/build_std.rs: -------------------------------------------------------------------------------- 1 | use ui_test::dependencies::DependencyBuilder; 2 | use ui_test::spanned::Spanned; 3 | use ui_test::Config; 4 | 5 | #[test] 6 | fn test() { 7 | let mut config = Config::rustc("tests/build_std"); 8 | let revisioned = config.comment_defaults.base(); 9 | 10 | revisioned.exit_status = Spanned::dummy(0).into(); 11 | revisioned.require_annotations = Spanned::dummy(false).into(); 12 | revisioned.set_custom( 13 | "dependencies", 14 | DependencyBuilder { 15 | build_std: Some(String::new()), 16 | ..DependencyBuilder::default() 17 | }, 18 | ); 19 | 20 | ui_test::run_tests(config).unwrap(); 21 | } 22 | -------------------------------------------------------------------------------- /tests/build_std/test.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oli-obk/ui_test/597d4f562fd3749ade663b7cc3f1ff467b55695e/tests/build_std/test.rs -------------------------------------------------------------------------------- /tests/integration.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | use ui_test::{spanned::Spanned, *}; 3 | 4 | fn main() -> Result<()> { 5 | let path = Path::new(file!()).parent().unwrap(); 6 | let root_dir = path.join("integrations"); 7 | let mut config = Config { 8 | bless_command: Some("cargo test -- -- --bless".to_string()), 9 | ..Config::cargo(root_dir.clone()) 10 | }; 11 | 12 | config.comment_defaults.base().require_annotations = Some(Spanned::dummy(false)).into(); 13 | 14 | let args = Args::test()?; 15 | config.with_args(&args); 16 | 17 | config.program.args = vec![ 18 | "test".into(), 19 | "--color".into(), 20 | "never".into(), 21 | "--quiet".into(), 22 | "--jobs".into(), 23 | "1".into(), 24 | "--no-fail-fast".into(), 25 | ]; 26 | config 27 | .program 28 | .envs 29 | .push(("RUST_TEST_THREADS".into(), Some("1".into()))); 30 | 31 | config 32 | .program 33 | .envs 34 | .push(("BLESS".into(), (!args.check).then(|| String::new().into()))); 35 | 36 | config 37 | .program 38 | .envs 39 | .push(("RUST_BACKTRACE".into(), Some("0".into()))); 40 | 41 | config.stdout_filter("in ([0-9]m )?[0-9\\.]+s", ""); 42 | config.stdout_filter(r#""--out-dir"(,)? "[^"]+""#, r#""--out-dir"$1 "$$TMP"#); 43 | config.filter("\\.exe", b""); 44 | config.filter( 45 | "( *process didn't exit successfully: `.*)-[0-9a-f]+`", 46 | "$1-HASH`", 47 | ); 48 | // Windows io::Error uses "exit code". 49 | config.filter("exit code", "exit status"); 50 | config.filter( 51 | "The system cannot find the file specified.", 52 | "No such file or directory", 53 | ); 54 | config.filter(r#"RUSTC_BOOTSTRAP=\\"1\\" "#, ""); 55 | config.filter(r#"RUSTC_ICE=\\"0\\" "#, ""); 56 | config.filter("RUSTC_BOOTSTRAP=\"1\" ", ""); 57 | config.filter("RUSTC_ICE=\"0\" ", ""); 58 | // The order of the `/deps` directory flag is flaky 59 | config.stdout_filter("/deps", ""); 60 | config.stdout_filter("[0-9a-f]+\\.rmeta", "$$HASH.rmeta"); 61 | // Windows backslashes are sometimes escaped. 62 | // Insert the replacement filter at the start to make sure the filter for single backslashes 63 | // runs afterwards. 64 | config 65 | .comment_defaults 66 | .base() 67 | .normalize_stdout 68 | .insert(0, (Match::Exact(b"\\\\".to_vec()), b"\\".to_vec())); 69 | // Do windows path escaping twice because json also escapes paths 70 | config 71 | .comment_defaults 72 | .base() 73 | .normalize_stdout 74 | .insert(0, (Match::Exact(b"\\\\\\\\".to_vec()), b"\\".to_vec())); 75 | config.path_filter(std::path::Path::new(path), "$DIR"); 76 | // Unescape escaped quotes at the end of windows paths in json 77 | config.filter("/\"", "\\\""); 78 | config.stdout_filter(r#"(panic.*)\.rs:[0-9]+:[0-9]+"#, "$1.rs"); 79 | // We don't want to normalize lines starting with `+`, those are diffs of the inner ui_test 80 | // and normalizing these here doesn't make the "actual output differed from expected" go 81 | // away, it just makes it impossible to diagnose. 82 | config.filter(" +[0-9]+: .*\n", ""); 83 | config.filter(" +at \\.?/.*\n", ""); 84 | config.filter(" running on .*", ""); 85 | config.stdout_filter( 86 | "/target/[^/]+/[0-9]+/[^/]+/debug", 87 | "/target/$$TMP/$$TRIPLE/debug", 88 | ); 89 | config.stdout_filter("/target/.tmp[^/ \"]+", "/target/$$TMP"); 90 | // Normalize proc macro filenames on windows to their linux repr 91 | config.stdout_filter("/([^/\\.]+)\\.dll", "/lib$1.so"); 92 | // Normalize proc macro filenames on mac to their linux repr 93 | config.stdout_filter("/([^/\\.]+)\\.dylib", "/$1.so"); 94 | config.stdout_filter("(command: )\"[^ = args.format.into(); 111 | 112 | let mut pass_config = config.clone(); 113 | pass_config.comment_defaults.base().exit_status = Some(Spanned::dummy(0)).into(); 114 | let mut panic_config = config; 115 | panic_config.comment_defaults.base().exit_status = Some(Spanned::dummy(101)).into(); 116 | 117 | run_tests_generic( 118 | vec![pass_config, panic_config], 119 | |path, config| { 120 | let fail = path 121 | .parent() 122 | .unwrap() 123 | .file_name() 124 | .unwrap() 125 | .to_str() 126 | .unwrap() 127 | .ends_with("-fail"); 128 | if !path.ends_with("Cargo.toml") { 129 | return None; 130 | } 131 | Some( 132 | path.parent().unwrap().parent().unwrap() == root_dir 133 | && match config 134 | .comment_defaults 135 | .base_immut() 136 | .exit_status 137 | .as_deref() 138 | .unwrap() 139 | { 140 | 0 => !fail, 141 | // This is weird, but `cargo test` returns 101 instead of 1 when 142 | // multiple [[test]]s exist. If there's only one test, it returns 143 | // 1 on failure. 144 | 101 => fail, 145 | _ => unreachable!(), 146 | } 147 | && default_any_file_filter(path, config), 148 | ) 149 | }, 150 | |_, _| {}, 151 | ( 152 | emitter, 153 | #[cfg(feature = "gha")] 154 | ui_test::status_emitter::Gha { 155 | name: "integration tests".into(), 156 | group: true, 157 | }, 158 | ), 159 | ) 160 | } 161 | -------------------------------------------------------------------------------- /tests/integrations/basic-bin/Cargo.stdout: -------------------------------------------------------------------------------- 1 | 2 | running 0 tests 3 | 4 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished 5 | 6 | Building dependencies ... ok 7 | tests/actual_tests/foomp.rs ... ok 8 | 9 | test result: ok. 1 passed 10 | 11 | -------------------------------------------------------------------------------- /tests/integrations/basic-bin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_bin" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dev-dependencies] 9 | ui_test = { path = "../../.."} 10 | tempfile = "3.3.0" 11 | 12 | [[test]] 13 | name = "ui_tests" 14 | harness = false 15 | -------------------------------------------------------------------------------- /tests/integrations/basic-bin/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | -------------------------------------------------------------------------------- /tests/integrations/basic-bin/tests/actual_tests/foomp.rs: -------------------------------------------------------------------------------- 1 | //@normalize-stderr-test: "(file name should be).*" -> "$1" 2 | 3 | use basic_bin::add; 4 | //~^ ERROR: unresolved import 5 | 6 | fn main() { 7 | add("42", 3); 8 | } 9 | -------------------------------------------------------------------------------- /tests/integrations/basic-bin/tests/actual_tests/foomp.stderr: -------------------------------------------------------------------------------- 1 | error[E0432]: unresolved import `basic_bin` 2 | --> tests/actual_tests/foomp.rs:3:5 3 | | 4 | 3 | use basic_bin::add; 5 | | ^^^^^^^^^ use of undeclared crate or module `basic_bin` 6 | 7 | error: aborting due to 1 previous error 8 | 9 | For more information about this error, try `rustc --explain E0432`. 10 | -------------------------------------------------------------------------------- /tests/integrations/basic-bin/tests/ui_tests.rs: -------------------------------------------------------------------------------- 1 | use ui_test::{dependencies::DependencyBuilder, *}; 2 | 3 | fn main() -> ui_test::color_eyre::Result<()> { 4 | let path = "../../../target"; 5 | let mut config = Config { 6 | output_conflict_handling: if std::env::var_os("BLESS").is_some() { 7 | ui_test::bless_output_files 8 | } else { 9 | ui_test::error_on_output_conflict 10 | }, 11 | bless_command: Some("cargo test".to_string()), 12 | ..Config::rustc("tests/actual_tests") 13 | }; 14 | config 15 | .comment_defaults 16 | .base() 17 | .set_custom("dependencies", DependencyBuilder::default()); 18 | config.stderr_filter("in ([0-9]m )?[0-9\\.]+s", ""); 19 | config.stdout_filter("in ([0-9]m )?[0-9\\.]+s", ""); 20 | config.stderr_filter(r"[^ ]*/\.?cargo/registry/.*/", "$$CARGO_REGISTRY"); 21 | config.stderr_filter(r"\.exe", ""); 22 | config.stderr_filter("/target/[^/]+/[0-9]+/[^/]+/debug", "/target/$$TMP/$$TRIPLE/debug"); 23 | config.path_stderr_filter(&std::path::Path::new(path), "$DIR"); 24 | 25 | // hide binaries generated for successfully passing tests 26 | let tmp_dir = tempfile::tempdir_in(path)?; 27 | let tmp_dir = tmp_dir.path(); 28 | config.out_dir = tmp_dir.into(); 29 | config.path_stderr_filter(tmp_dir, "$$TMP"); 30 | 31 | run_tests_generic( 32 | vec![config], 33 | default_file_filter, 34 | default_per_file_config, 35 | // Avoid github actions, as these would end up showing up in `Cargo.stderr` 36 | status_emitter::Text::verbose(), 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail-mode/Cargo.stdout: -------------------------------------------------------------------------------- 1 | 2 | running 0 tests 3 | 4 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished 5 | 6 | 7 | running 3 tests 8 | ... 9 | test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished 10 | 11 | Building dependencies ... ok 12 | tests/actual_tests/foomp.rs ... ok 13 | 14 | test result: ok. 1 passed 15 | 16 | 17 | running 0 tests 18 | 19 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished 20 | 21 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail-mode/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_fail_mode" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dev-dependencies] 9 | ui_test = { path = "../../.."} 10 | tempfile = "3.3.0" 11 | 12 | [[test]] 13 | name = "ui_tests" 14 | harness = false 15 | 16 | [[test]] 17 | name = "run_file" 18 | harness = true 19 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail-mode/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn add(left: usize, right: usize) -> usize { 2 | left + right 3 | } 4 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail-mode/tests/actual_tests/foomp.rs: -------------------------------------------------------------------------------- 1 | use basic_fail_mode::add; 2 | 3 | fn main() { 4 | add("42", 3); 5 | //~^ ERROR: mismatched types 6 | } 7 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail-mode/tests/actual_tests/foomp.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests/foomp.rs:4:9 3 | | 4 | 4 | add("42", 3); 5 | | --- ^^^^ expected `usize`, found `&str` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | note: function defined here 10 | --> $DIR/tests/integrations/basic-fail-mode/src/lib.rs:1:8 11 | | 12 | 1 | pub fn add(left: usize, right: usize) -> usize { 13 | | ^^^ 14 | 15 | error: aborting due to 1 previous error 16 | 17 | For more information about this error, try `rustc --explain E0308`. 18 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail-mode/tests/run_file.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | use ui_test::color_eyre::{eyre::ensure, Result}; 3 | use ui_test::dependencies::DependencyBuilder; 4 | use ui_test::*; 5 | 6 | #[test] 7 | fn run_file() -> Result<()> { 8 | let mut config = Config::rustc(PathBuf::new()); 9 | 10 | let tmp_dir = tempfile::tempdir_in(env!("CARGO_TARGET_TMPDIR"))?; 11 | let tmp_dir = tmp_dir.path(); 12 | config.out_dir = tmp_dir.into(); 13 | config.path_stderr_filter(tmp_dir, "$TMP"); 14 | 15 | let mut result = ui_test::test_command( 16 | config, 17 | &Path::new(file!()) 18 | .parent() 19 | .unwrap() 20 | .join("run_file/run_file.rs"), 21 | )?; 22 | ensure!(result.output()?.status.success(), ""); 23 | Ok(()) 24 | } 25 | 26 | #[test] 27 | fn fail_run_file() { 28 | let mut config = Config::rustc(PathBuf::new()); 29 | config.host = Some(String::new()); 30 | config.program = CommandBuilder::cmd("invalid_alsdkfjalsdfjalskdfj"); 31 | 32 | let _ = ui_test::test_command( 33 | config, 34 | &Path::new(file!()) 35 | .parent() 36 | .unwrap() 37 | .join("run_file/run_file.rs"), 38 | ) 39 | .unwrap() 40 | .output() 41 | .unwrap_err(); 42 | } 43 | 44 | #[test] 45 | fn run_file_no_deps() -> Result<()> { 46 | let path = "../../../target"; 47 | 48 | let mut config = Config::rustc(PathBuf::new()); 49 | 50 | let tmp_dir = tempfile::tempdir_in(path)?; 51 | let tmp_dir = tmp_dir.path(); 52 | config.out_dir = tmp_dir.into(); 53 | config.path_stderr_filter(tmp_dir, "$TMP"); 54 | 55 | // Don't build a binary, we only provide .rmeta dependencies for now 56 | config.program.args.push("--emit=metadata".into()); 57 | config 58 | .comment_defaults 59 | .base() 60 | .set_custom("dependencies", DependencyBuilder::default()); 61 | 62 | let mut result = ui_test::test_command( 63 | config, 64 | &Path::new(file!()) 65 | .parent() 66 | .unwrap() 67 | .join("run_file/run_file_with_deps.rs"), 68 | )?; 69 | ensure!(result.output()?.status.success(), ""); 70 | Ok(()) 71 | } 72 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail-mode/tests/run_file/run_file.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail-mode/tests/run_file/run_file_with_deps.rs: -------------------------------------------------------------------------------- 1 | use basic_fail_mode::add; 2 | 3 | fn main() { 4 | assert_eq!(add(1, 2), 3); 5 | } 6 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail-mode/tests/ui_tests.rs: -------------------------------------------------------------------------------- 1 | use ui_test::{dependencies::DependencyBuilder, *}; 2 | 3 | fn main() -> ui_test::color_eyre::Result<()> { 4 | let path = "../../../target"; 5 | let mut config = Config { 6 | output_conflict_handling: if std::env::var_os("BLESS").is_some() { 7 | ui_test::bless_output_files 8 | } else { 9 | ui_test::error_on_output_conflict 10 | }, 11 | bless_command: Some("cargo test".to_string()), 12 | ..Config::rustc("tests/actual_tests") 13 | }; 14 | config 15 | .comment_defaults 16 | .base() 17 | .set_custom("dependencies", DependencyBuilder::default()); 18 | config.stderr_filter("in ([0-9]m )?[0-9\\.]+s", ""); 19 | config.stdout_filter("in ([0-9]m )?[0-9\\.]+s", ""); 20 | config.stderr_filter(r"[^ ]*/\.?cargo/registry/.*/", "$$CARGO_REGISTRY"); 21 | config.path_stderr_filter(&std::path::Path::new(path), "$DIR"); 22 | 23 | run_tests_generic( 24 | vec![config], 25 | default_file_filter, 26 | default_per_file_config, 27 | // Avoid github actions, as these would end up showing up in `Cargo.stderr` 28 | status_emitter::Text::verbose(), 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/Cargo.stderr: -------------------------------------------------------------------------------- 1 | Error: tests failed 2 | 3 | Location: 4 | $DIR/src/lib.rs:LL:CC 5 | error: test failed, to rerun pass `--test json` 6 | 7 | Caused by: 8 | process didn't exit successfully: `$DIR/target/ui/1/tests/integrations/basic-fail/debug/deps/json-HASH` (exit status: 1) 9 | Error: tests failed 10 | 11 | Location: 12 | $DIR/src/lib.rs:LL:CC 13 | error: test failed, to rerun pass `--test ui_tests` 14 | 15 | Caused by: 16 | process didn't exit successfully: `$DIR/target/ui/1/tests/integrations/basic-fail/debug/deps/ui_tests-HASH` (exit status: 1) 17 | Error: tests failed 18 | 19 | Location: 20 | $DIR/src/lib.rs:LL:CC 21 | error: test failed, to rerun pass `--test ui_tests_diff_only` 22 | 23 | Caused by: 24 | process didn't exit successfully: `$DIR/target/ui/1/tests/integrations/basic-fail/debug/deps/ui_tests_diff_only-HASH` (exit status: 1) 25 | Error: failed to parse rustc version info: invalid_foobarlaksdfalsdfj 26 | 27 | Caused by: 28 | 29 | Location: 30 | $DIR/src/config.rs:LL:CC 31 | error: test failed, to rerun pass `--test ui_tests_invalid_program` 32 | 33 | Caused by: 34 | process didn't exit successfully: `$DIR/target/ui/1/tests/integrations/basic-fail/debug/deps/ui_tests_invalid_program-HASH` (exit status: 1) 35 | Error: tests failed 36 | 37 | Location: 38 | $DIR/src/lib.rs:LL:CC 39 | error: test failed, to rerun pass `--test ui_tests_invalid_program2` 40 | 41 | Caused by: 42 | process didn't exit successfully: `$DIR/target/ui/1/tests/integrations/basic-fail/debug/deps/ui_tests_invalid_program2-HASH` (exit status: 1) 43 | error: 5 targets failed: 44 | `--test json` 45 | `--test ui_tests` 46 | `--test ui_tests_diff_only` 47 | `--test ui_tests_invalid_program` 48 | `--test ui_tests_invalid_program2` 49 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_fail" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dev-dependencies] 9 | ui_test = { path = "../../.." } 10 | tempfile = "3.3.0" 11 | 12 | [[test]] 13 | name = "ui_tests" 14 | harness = false 15 | 16 | [[test]] 17 | name = "json" 18 | harness = false 19 | 20 | [[test]] 21 | name = "ui_tests_invalid_program" 22 | harness = false 23 | 24 | [[test]] 25 | name = "ui_tests_invalid_program2" 26 | harness = false 27 | 28 | [[test]] 29 | name = "ui_tests_diff_only" 30 | harness = false 31 | 32 | [[test]] 33 | name = "ui_tests_bless" 34 | harness = false 35 | 36 | #@normalize-stdout-test: "(command: canonicalizing path `tests/actual_tests_bless/auxiliary/aasdflkjasdlfjasdlfkjasdf`\n\nfull stderr:\n).*" -> "${1}No such file or directory" 37 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn add(left: usize, right: usize) -> usize { 2 | left + right 3 | } 4 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/bad_pattern.rs: -------------------------------------------------------------------------------- 1 | use basic_fail::add; 2 | 3 | fn main() { 4 | add("42", 3); 5 | //~^ ERROR: miesmätsched types 6 | } 7 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/bad_pattern.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests/bad_pattern.rs:4:9 3 | | 4 | 4 | add("42", 3); 5 | | --- ^^^^ expected `usize`, found `&str` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | note: function defined here 10 | --> $DIR/tests/integrations/basic-fail/src/lib.rs:1:8 11 | | 12 | 1 | pub fn add(left: usize, right: usize) -> usize { 13 | | ^^^ 14 | 15 | error: aborting due to previous error 16 | 17 | For more information about this error, try `rustc --explain E0308`. 18 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/executable.rs: -------------------------------------------------------------------------------- 1 | use basic_fail::add; 2 | 3 | //@run 4 | 5 | fn main() { 6 | println!("{}", add(20, 22)); 7 | } 8 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/executable.run.stdout: -------------------------------------------------------------------------------- 1 | 69 2 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/executable_compile_err.rs: -------------------------------------------------------------------------------- 1 | //@run 2 | 3 | fn main() { 4 | 5 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/exit_code_fail.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/filters.rs: -------------------------------------------------------------------------------- 1 | //@only-x86_64 2 | 3 | fn main() {} 4 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/foomp.rs: -------------------------------------------------------------------------------- 1 | use basic_fail::add; 2 | 3 | fn main() { 4 | add("42", 3); 5 | //~^ ERROR: mismatched types 6 | } 7 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/foomp.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests/foomp.rs:4:9 3 | | 4 | 4 | add("42", 3); 5 | | --- ^^^^ expected `usize`, found `&str` 6 | | 7 | note: function defined here 8 | --> $DIR/tests/integrations/basic/src/lib.rs:1:8 9 | | 10 | 1 | pub fn add(left: usize, right: usize) -> usize { 11 | | ^^^ some expected text that isn't in the actual message 12 | 13 | error: aborting doo to previous error 14 | 15 | For more information about this error, try `rustc --explain E0308`. 16 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/foomp2.fixed: -------------------------------------------------------------------------------- 1 | this is just a test file showing that 2 | stray .fixed files are detected and blessed away 3 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/foomp2.rs: -------------------------------------------------------------------------------- 1 | use basic_fail::add; 2 | 3 | fn main() { 4 | add("42", 3); 5 | //~^ ERROR: mismatched types 6 | } 7 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/foomp2.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests/foomp2.rs:4:9 3 | | 4 | 4 | add("42", 3); 5 | | --- ^^^^ expected `usize`, found `&str` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | note: function defined here 10 | --> $DIR/tests/integrations/basic-fail/src/lib.rs:1:8 11 | | 12 | 1 | pub fn add(left: usize, right: usize) -> usize { 13 | | ^^^ 14 | 15 | error: aborting due to previous error 16 | 17 | For more information about this error, try `rustc --explain E0308`. 18 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/ice_annotations.rs: -------------------------------------------------------------------------------- 1 | //@compile-flags: -Ztreat-err-as-bug 2 | //@rustc-env: RUSTC_BOOTSTRAP=1 3 | //@rustc-env: RUSTC_ICE=0 4 | //@normalize-stderr-test: "(?s)(thread 'rustc' panicked).*end of query stack" -> "$1" 5 | use basic_fail::add; 6 | 7 | fn main() { 8 | add("42", 3); 9 | //~^ ICE: mismatched types 10 | } 11 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/inline_chain.rs: -------------------------------------------------------------------------------- 1 | #![deny(unused_mut, unused_variables)] 2 | 3 | fn main() { 4 | let mut x = 0u32; //~ unused_mut 5 | //~| unused_variables 6 | } 7 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/joined_wrong_order.rs: -------------------------------------------------------------------------------- 1 | #![deny(unused_mut, unused_variables)] 2 | 3 | fn main() { 4 | //~vv unused_variables 5 | //~| unused_mut 6 | let mut x = 0u32; 7 | } 8 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/lone_joined_pattern.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | //~| ERROR: mismatched types 3 | use_unit(1_u32); 4 | //~| ERROR: mismatched types 5 | } 6 | 7 | fn use_unit(_: ()) {} 8 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/pattern_too_many_arrow.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use_unit(1_u32); 3 | //~^^^^^^^ ERROR: mismatched types 4 | } 5 | 6 | fn use_unit(_: ()) {} 7 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/pattern_too_many_arrow_above.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | //~vvvvvv ERROR: mismatched types 3 | use_unit(1_u32); 4 | } 5 | 6 | fn use_unit(_: ()) {} 7 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/rustc_ice.rs: -------------------------------------------------------------------------------- 1 | //@compile-flags: -Ztreat-err-as-bug 2 | //@rustc-env: RUSTC_BOOTSTRAP=1 3 | //@rustc-env: RUSTC_ICE=0 4 | //@normalize-stderr-test: "(?s)(thread 'rustc' panicked).*end of query stack" -> "$1" 5 | use basic_fail::add; 6 | 7 | fn main() { 8 | add("42", 3); 9 | //~^ ERROR: mismatched types 10 | } 11 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/rustc_ice.stderr: -------------------------------------------------------------------------------- 1 | error: internal compiler error[E0308]: mismatched types 2 | --> tests/actual_tests/rustc_ice.rs:8:9 3 | | 4 | 8 | add("42", 3); 5 | | --- ^^^^ expected `usize`, found `&str` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | note: function defined here 10 | --> $DIR/tests/integrations/basic-fail/src/lib.rs:1:8 11 | | 12 | 1 | pub fn add(left: usize, right: usize) -> usize { 13 | | ^^^ 14 | 15 | thread 'rustc' panicked 16 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/touching_above_below.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use_unit(1_u32); 3 | //~^ ERROR: mismatched types 4 | //~v ERROR: mismatched types 5 | use_unit(1_u32); 6 | } 7 | 8 | fn use_unit(_: ()) {} 9 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests/touching_above_below_chain.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use_unit(1_u32); 3 | //~^ ERROR: mismatched types 4 | //~| ERROR: variable does not need to be mutable 5 | //~v ERROR: unused variable 6 | let mut x = 0u32; 7 | } 8 | 9 | fn use_unit(_: ()) {} 10 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/abort.rs: -------------------------------------------------------------------------------- 1 | //@run 2 | 3 | fn main() { 4 | std::process::abort(); 5 | } 6 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/aux_build_not_found.rs: -------------------------------------------------------------------------------- 1 | //@aux-build:aasdflkjasdlfjasdlfkjasdf 2 | 3 | fn main() {} 4 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/aux_proc_macro_misuse.rs: -------------------------------------------------------------------------------- 1 | //@aux-build:the_proc_macro.rs:proc-macro 2 | 3 | use the_proc_macro::thing; 4 | 5 | fn main() { 6 | thing!(cake); 7 | //~^ ERROR: cannot find value `cake` in this scope 8 | } 9 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/aux_proc_macro_no_main.rs: -------------------------------------------------------------------------------- 1 | //@aux-build:the_proc_macro.rs 2 | //! Test that our automatic --crate-type detection (changing this crate to `lib`) 3 | //! does not change the aux build to `lib` 4 | 5 | use the_proc_macro::thing; 6 | 7 | thing!(cake); 8 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/aux_proc_macro_no_main.stderr: -------------------------------------------------------------------------------- 1 | error: expected one of `!` or `::`, found `` 2 | --> tests/actual_tests_bless/aux_proc_macro_no_main.rs:7:8 3 | | 4 | 7 | thing!(cake); 5 | | ^^^^ expected one of `!` or `::` 6 | 7 | error: aborting due to 1 previous error 8 | 9 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/auxiliary/foomp.rs: -------------------------------------------------------------------------------- 1 | pub fn add(_: u8, _: u8) {} 2 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/auxiliary/nested.rs: -------------------------------------------------------------------------------- 1 | //@aux-build:foomp.rs 2 | 3 | pub fn bar() { 4 | foomp::add(1, 2); 5 | } 6 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/auxiliary/the_proc_macro.rs: -------------------------------------------------------------------------------- 1 | #![crate_type = "proc-macro"] 2 | 3 | extern crate proc_macro; 4 | 5 | use proc_macro::TokenStream; 6 | 7 | #[proc_macro] 8 | pub fn thing(input: TokenStream) -> TokenStream { 9 | input 10 | } 11 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/compile_flags_quotes.rs: -------------------------------------------------------------------------------- 1 | //@compile-flag: cake 2 | //@compile-flags: -Z "cake is a lie" 3 | //@compile-flags: -Z "cheese is good 4 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/compiletest-rs-command.rs: -------------------------------------------------------------------------------- 1 | //aux-build:foomp.rs 2 | // aux-build:asf.rs 3 | // aux-build:asasdflk.rs 4 | // @aux-build:asldkfjasldfj.rs 5 | // @aux-build:asldkjf.rs 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/edition_override.rs: -------------------------------------------------------------------------------- 1 | //@edition: 2018 2 | //@check-pass 3 | 4 | fn main() { 5 | match 42 { 6 | 0...1 => {} 7 | _ => {} 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/edition_override.stderr: -------------------------------------------------------------------------------- 1 | warning: `...` range patterns are deprecated 2 | --> tests/actual_tests_bless/edition_override.rs:6:10 3 | | 4 | 6 | 0...1 => {} 5 | | ^^^ help: use `..=` for an inclusive range 6 | | 7 | = warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2021! 8 | = note: for more information, see 9 | = note: `#[warn(ellipsis_inclusive_range_patterns)]` on by default 10 | 11 | warning: 1 warning emitted 12 | 13 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/failing_executable.rs: -------------------------------------------------------------------------------- 1 | //@run 2 | 3 | fn main() { 4 | assert_eq!(5, 6); 5 | } 6 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/failing_executable.run.stderr: -------------------------------------------------------------------------------- 1 | thread 'main' panicked at tests/actual_tests_bless/failing_executable.rs:4:5: 2 | assertion `left == right` failed 3 | left: 5 4 | right: 6 5 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 6 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/foomp_aux.rs: -------------------------------------------------------------------------------- 1 | //@aux-build:foomp.rs 2 | 3 | use foomp::add; 4 | 5 | fn main() { 6 | add("42", 3); 7 | //~^ ERROR: mismatched types 8 | } 9 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/foomp_aux.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests_bless/foomp_aux.rs:6:9 3 | | 4 | 6 | add("42", 3); 5 | | --- ^^^^ expected `u8`, found `&str` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | note: function defined here 10 | --> $DIR/tests/integrations/basic-fail/tests/actual_tests_bless/auxiliary/foomp.rs:1:8 11 | | 12 | 1 | pub fn add(_: u8, _: u8) {} 13 | | ^^^ 14 | 15 | error: aborting due to 1 previous error 16 | 17 | For more information about this error, try `rustc --explain E0308`. 18 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/nested_aux.rs: -------------------------------------------------------------------------------- 1 | //@aux-build:nested.rs 2 | //@check-pass 3 | 4 | use nested::bar; 5 | 6 | fn main() { 7 | bar(); 8 | } 9 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/no_main.rs: -------------------------------------------------------------------------------- 1 | pub fn foo() {} 2 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/no_main_manual.rs: -------------------------------------------------------------------------------- 1 | //@compile-flags: --crate-type=bin 2 | 3 | pub fn foo() {} 4 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/no_main_manual.stderr: -------------------------------------------------------------------------------- 1 | error: cannot mix `bin` crate type with others 2 | 3 | error[E0601]: `main` function not found in crate `no_main_manual` 4 | --> tests/actual_tests_bless/no_main_manual.rs:3:16 5 | | 6 | 3 | pub fn foo() {} 7 | | ^ consider adding a `main` function to `tests/actual_tests_bless/no_main_manual.rs` 8 | 9 | error: aborting due to 2 previous errors 10 | 11 | For more information about this error, try `rustc --explain E0601`. 12 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/no_test.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn foo() {} 3 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/non_top_level_configs.rs: -------------------------------------------------------------------------------- 1 | ////@check-pass 2 | 3 | fn main() { 4 | //@ignore-target-cheesecake 5 | } 6 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/normalization_override.rs: -------------------------------------------------------------------------------- 1 | //@normalize-stderr-test: "NORMALIZATION_OVERRIDE" -> "SUCCESS" 2 | 3 | // Test that the above normalization will be run after the default normalization set in ui_tests_bless.rs 4 | 5 | fn main() { 6 | asdf //~ ERROR: cannot find 7 | } 8 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/normalization_override.stderr: -------------------------------------------------------------------------------- 1 | error[E0425]: cannot find value `asdf` in this scope 2 | --> tests/actual_tests_bless/SUCCESS.rs:6:5 3 | | 4 | 6 | asdf 5 | | ^^^^ not found in this scope 6 | 7 | error: aborting due to 1 previous error 8 | 9 | For more information about this error, try `rustc --explain E0425`. 10 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/pass.rs: -------------------------------------------------------------------------------- 1 | //@check-pass 2 | 3 | fn main() {} 4 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/pass_with_annotation.rs: -------------------------------------------------------------------------------- 1 | //@check-pass 2 | 3 | //~ ERROR: the cake is a lie 4 | fn main() {} 5 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revised_revision.rs: -------------------------------------------------------------------------------- 1 | use basic_fail::add; 2 | //@[foo] revisions: foo bar 3 | 4 | #[cfg(foo)] 5 | fn main() { 6 | add("42", 3); //~[foo] ERROR: mismatched types 7 | } 8 | 9 | #[cfg(bar)] 10 | fn main() { 11 | add("42", 3); 12 | //~[bar]^ ERROR: mismatched types 13 | } 14 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisioned_executable.rs: -------------------------------------------------------------------------------- 1 | //@revisions: run panic 2 | //@[run]run 3 | //@[panic]run:101 4 | 5 | fn main() {} 6 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisioned_executable_panic.panic.run.stderr: -------------------------------------------------------------------------------- 1 | thread 'main' panicked at tests/actual_tests_bless/revisioned_executable_panic.rs:6:5: 2 | explicit panic 3 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 4 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisioned_executable_panic.rs: -------------------------------------------------------------------------------- 1 | //@revisions: run panic 2 | //@[run]run 3 | //@[panic]run:101 4 | 5 | fn main() { 6 | panic!() 7 | } 8 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisioned_executable_panic.run.run.stderr: -------------------------------------------------------------------------------- 1 | thread 'main' panicked at tests/actual_tests_bless/revisioned_executable_panic.rs:6:5: 2 | explicit panic 3 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 4 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisions.bar.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests_bless/revisions.rs:11:9 3 | | 4 | 11 | add("42", 3); 5 | | --- ^^^^ expected `usize`, found `&str` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | note: function defined here 10 | --> $DIR/tests/integrations/basic-fail/src/lib.rs:1:8 11 | | 12 | 1 | pub fn add(left: usize, right: usize) -> usize { 13 | | ^^^ 14 | 15 | error: aborting due to 1 previous error 16 | 17 | For more information about this error, try `rustc --explain E0308`. 18 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisions.foo.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests_bless/revisions.rs:6:9 3 | | 4 | 6 | add("42", 3); 5 | | --- ^^^^ expected `usize`, found `&str` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | note: function defined here 10 | --> $DIR/tests/integrations/basic-fail/src/lib.rs:1:8 11 | | 12 | 1 | pub fn add(left: usize, right: usize) -> usize { 13 | | ^^^ 14 | 15 | error: aborting due to 1 previous error 16 | 17 | For more information about this error, try `rustc --explain E0308`. 18 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisions.rs: -------------------------------------------------------------------------------- 1 | use basic_fail::add; 2 | //@ revisions: foo bar 3 | 4 | #[cfg(foo)] 5 | fn main() { 6 | add("42", 3); //~[foo] ERROR: mismatched types 7 | } 8 | 9 | #[cfg(bar)] 10 | fn main() { 11 | add("42", 3); 12 | //~[bar]^ ERROR: mismatched types 13 | } 14 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisions_bad.bar.stderr: -------------------------------------------------------------------------------- 1 | error[E0601]: `main` function not found in crate `revisions_bad` 2 | --> tests/actual_tests_bless/revisions_bad.rs:10:2 3 | | 4 | 10 | } 5 | | ^ consider adding a `main` function to `tests/actual_tests_bless/revisions_bad.rs` 6 | 7 | error: aborting due to 1 previous error 8 | 9 | For more information about this error, try `rustc --explain E0601`. 10 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisions_bad.foo.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests_bless/revisions_bad.rs:8:9 3 | | 4 | 8 | add("42", 3); 5 | | --- ^^^^ expected `usize`, found `&str` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | note: function defined here 10 | --> $DIR/tests/integrations/basic-fail/src/lib.rs:1:8 11 | | 12 | 1 | pub fn add(left: usize, right: usize) -> usize { 13 | | ^^^ 14 | 15 | error: aborting due to 1 previous error 16 | 17 | For more information about this error, try `rustc --explain E0308`. 18 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisions_bad.rs: -------------------------------------------------------------------------------- 1 | #[cfg(foo)] 2 | use basic_fail::add; 3 | //@ revisions: foo bar 4 | //@[bar] error-in-other-file: `main` function not found in crate `revisions_bad` 5 | 6 | #[cfg(foo)] 7 | fn main() { 8 | add("42", 3); 9 | //~[foo]^ ERROR: mismatched types 10 | } 11 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisions_filter.rs: -------------------------------------------------------------------------------- 1 | use basic_fail::add; 2 | //@ignore-on-host 3 | //@ revisions: foo bar 4 | 5 | #[cfg(foo)] 6 | fn main() { 7 | add("42", 3); 8 | } 9 | 10 | #[cfg(bar)] 11 | fn main() { 12 | add("42", 3); 13 | //~[bar]^ ERROR: mismatched types 14 | } 15 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisions_filter2.bar.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests_bless/revisions_filter2.rs:12:9 3 | | 4 | 12 | add("42", 3); 5 | | --- ^^^^ expected `usize`, found `&str` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | note: function defined here 10 | --> $DIR/tests/integrations/basic-fail/src/lib.rs:1:8 11 | | 12 | 1 | pub fn add(left: usize, right: usize) -> usize { 13 | | ^^^ 14 | 15 | error: aborting due to 1 previous error 16 | 17 | For more information about this error, try `rustc --explain E0308`. 18 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisions_filter2.rs: -------------------------------------------------------------------------------- 1 | use basic_fail::add; 2 | //@[foo] ignore-on-host 3 | //@ revisions: foo bar 4 | 5 | #[cfg(foo)] 6 | fn main() { 7 | add("42", 3); 8 | } 9 | 10 | #[cfg(bar)] 11 | fn main() { 12 | add("42", 3); 13 | //~[bar]^ ERROR: mismatched types 14 | } 15 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisions_multiple_per_annotation.bar.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests_bless/revisions_multiple_per_annotation.rs:5:9 3 | | 4 | 5 | add("42", 3); 5 | | --- ^^^^ expected `usize`, found `&str` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | note: function defined here 10 | --> $DIR/tests/integrations/basic-fail/src/lib.rs:1:8 11 | | 12 | 1 | pub fn add(left: usize, right: usize) -> usize { 13 | | ^^^ 14 | 15 | error: aborting due to 1 previous error 16 | 17 | For more information about this error, try `rustc --explain E0308`. 18 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisions_multiple_per_annotation.foo.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests_bless/revisions_multiple_per_annotation.rs:5:9 3 | | 4 | 5 | add("42", 3); 5 | | --- ^^^^ expected `usize`, found `&str` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | note: function defined here 10 | --> $DIR/tests/integrations/basic-fail/src/lib.rs:1:8 11 | | 12 | 1 | pub fn add(left: usize, right: usize) -> usize { 13 | | ^^^ 14 | 15 | error: aborting due to 1 previous error 16 | 17 | For more information about this error, try `rustc --explain E0308`. 18 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisions_multiple_per_annotation.rs: -------------------------------------------------------------------------------- 1 | use basic_fail::add; 2 | //@ revisions: foo bar 3 | 4 | fn main() { 5 | add("42", 3); 6 | //~[bar, foo]^ ERROR: mismatched types 7 | } 8 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisions_same_everywhere.bar.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests_bless/revisions_same_everywhere.rs:5:9 3 | | 4 | 5 | add("42", 3); 5 | | --- ^^^^ expected `usize`, found `&str` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | note: function defined here 10 | --> $DIR/tests/integrations/basic-fail/src/lib.rs:1:8 11 | | 12 | 1 | pub fn add(left: usize, right: usize) -> usize { 13 | | ^^^ 14 | 15 | error: aborting due to 1 previous error 16 | 17 | For more information about this error, try `rustc --explain E0308`. 18 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisions_same_everywhere.foo.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests_bless/revisions_same_everywhere.rs:5:9 3 | | 4 | 5 | add("42", 3); 5 | | --- ^^^^ expected `usize`, found `&str` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | note: function defined here 10 | --> $DIR/tests/integrations/basic-fail/src/lib.rs:1:8 11 | | 12 | 1 | pub fn add(left: usize, right: usize) -> usize { 13 | | ^^^ 14 | 15 | error: aborting due to 1 previous error 16 | 17 | For more information about this error, try `rustc --explain E0308`. 18 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/revisions_same_everywhere.rs: -------------------------------------------------------------------------------- 1 | use basic_fail::add; 2 | //@ revisions: foo bar 3 | 4 | fn main() { 5 | add("42", 3); 6 | //~^ ERROR: mismatched types 7 | } 8 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/run_panic.rs: -------------------------------------------------------------------------------- 1 | //@run: 101 2 | 3 | fn main() { 4 | panic!(); 5 | } 6 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/run_panic.run.stderr: -------------------------------------------------------------------------------- 1 | thread 'main' panicked at tests/actual_tests_bless/run_panic.rs:4:5: 2 | explicit panic 3 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 4 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/rustfix-fail-revisions.a.fixed: -------------------------------------------------------------------------------- 1 | //@revisions: a b 2 | #![deny(warnings)] 3 | 4 | fn main() { 5 | let x = match 0 { 6 | 0 => String::new(), 7 | //~^ ERROR: expected `,` 8 | _ => return, 9 | }; 10 | 11 | x; 12 | } 13 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/rustfix-fail-revisions.a.stderr: -------------------------------------------------------------------------------- 1 | error: expected `,` following `match` arm 2 | --> tests/actual_tests_bless/rustfix-fail-revisions.rs:6:27 3 | | 4 | 6 | 0 => String::new() 5 | | ^ help: missing a comma here to end this `match` arm: `,` 6 | 7 | error: aborting due to 1 previous error 8 | 9 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/rustfix-fail-revisions.b.fixed: -------------------------------------------------------------------------------- 1 | //@revisions: a b 2 | #![deny(warnings)] 3 | 4 | fn main() { 5 | let x = match 0 { 6 | 0 => String::new(), 7 | //~^ ERROR: expected `,` 8 | _ => return, 9 | }; 10 | 11 | x; 12 | } 13 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/rustfix-fail-revisions.b.stderr: -------------------------------------------------------------------------------- 1 | error: expected `,` following `match` arm 2 | --> tests/actual_tests_bless/rustfix-fail-revisions.rs:6:27 3 | | 4 | 6 | 0 => String::new() 5 | | ^ help: missing a comma here to end this `match` arm: `,` 6 | 7 | error: aborting due to 1 previous error 8 | 9 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/rustfix-fail-revisions.rs: -------------------------------------------------------------------------------- 1 | //@revisions: a b 2 | #![deny(warnings)] 3 | 4 | fn main() { 5 | let x = match 0 { 6 | 0 => String::new() 7 | //~^ ERROR: expected `,` 8 | _ => return, 9 | }; 10 | 11 | x; 12 | } 13 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/rustfix-fail.fixed: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | 3 | fn main() { 4 | let x = match 0 { 5 | 0 => String::new(), 6 | //~^ ERROR: expected `,` 7 | _ => return, 8 | }; 9 | 10 | x; 11 | } 12 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/rustfix-fail.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | 3 | fn main() { 4 | let x = match 0 { 5 | 0 => String::new() 6 | //~^ ERROR: expected `,` 7 | _ => return, 8 | }; 9 | 10 | x; 11 | } 12 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/rustfix-fail.stderr: -------------------------------------------------------------------------------- 1 | error: expected `,` following `match` arm 2 | --> tests/actual_tests_bless/rustfix-fail.rs:5:27 3 | | 4 | 5 | 0 => String::new() 5 | | ^ help: missing a comma here to end this `match` arm: `,` 6 | 7 | error: aborting due to 1 previous error 8 | 9 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/unicode.rs: -------------------------------------------------------------------------------- 1 | #![deny(confusable_idents)] 2 | 3 | // Test that a missing annotation causing a 4 | // ui_test diagnostic will not ICE due to byte vs char offsets. 5 | 6 | fn main() { 7 | let Ě电脑 = 1; 8 | } 9 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/unicode.stderr: -------------------------------------------------------------------------------- 1 | warning: unused variable: `Ě电脑` 2 | --> tests/actual_tests_bless/unicode.rs:7:9 3 | | 4 | 7 | let Ě电脑 = 1; 5 | | ^^^^^ help: if this is intentional, prefix it with an underscore: `_Ě电脑` 6 | | 7 | = note: `#[warn(unused_variables)]` on by default 8 | 9 | warning: variable `Ě电脑` should have a snake case name 10 | --> tests/actual_tests_bless/unicode.rs:7:9 11 | | 12 | 7 | let Ě电脑 = 1; 13 | | ^^^^^ help: convert the identifier to snake case: `ě电脑` 14 | | 15 | = note: `#[warn(non_snake_case)]` on by default 16 | 17 | warning: 2 warnings emitted 18 | 19 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/unknown_revision.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | assert_eq!(5, 6); 3 | //~[cake, lie] ERROR: kawoom 4 | } 5 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/unknown_revision2.rs: -------------------------------------------------------------------------------- 1 | //@ revisions: cheese lie 2 | 3 | fn main() { 4 | assert_eq!(5, 6); 5 | //~[cake, lie] ERROR: kawoom 6 | } 7 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/wrong_diagnostic_code.rs: -------------------------------------------------------------------------------- 1 | #![deny(dead_code, unreachable_code)] 2 | 3 | fn foo() -> i32 { 4 | //~^ should_be_dead_code 5 | panic!(); 6 | 0 //~ unreachable_code 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless/wrong_diagnostic_code.stderr: -------------------------------------------------------------------------------- 1 | error: unreachable expression 2 | --> tests/actual_tests_bless/wrong_diagnostic_code.rs:6:5 3 | | 4 | 5 | panic!(); 5 | | -------- any code following this expression is unreachable 6 | 6 | 0 7 | | ^ unreachable expression 8 | | 9 | note: the lint level is defined here 10 | --> tests/actual_tests_bless/wrong_diagnostic_code.rs:1:20 11 | | 12 | 1 | #![deny(dead_code, unreachable_code)] 13 | | ^^^^^^^^^^^^^^^^ 14 | 15 | error: function `foo` is never used 16 | --> tests/actual_tests_bless/wrong_diagnostic_code.rs:3:4 17 | | 18 | 3 | fn foo() -> i32 { 19 | | ^^^ 20 | | 21 | note: the lint level is defined here 22 | --> tests/actual_tests_bless/wrong_diagnostic_code.rs:1:9 23 | | 24 | 1 | #![deny(dead_code, unreachable_code)] 25 | | ^^^^^^^^^ 26 | 27 | error: aborting due to 2 previous errors 28 | 29 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless_yolo/revisions_bad.bar.stderr: -------------------------------------------------------------------------------- 1 | error[E0601]: `main` function not found in crate `revisions_bad` 2 | --> tests/actual_tests_bless_yolo/revisions_bad.rs:9:2 3 | | 4 | 9 | } 5 | | ^ consider adding a `main` function to `tests/actual_tests_bless_yolo/revisions_bad.rs` 6 | 7 | error: aborting due to 1 previous error 8 | 9 | For more information about this error, try `rustc --explain E0601`. 10 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless_yolo/revisions_bad.foo.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests_bless_yolo/revisions_bad.rs:7:9 3 | | 4 | 7 | add("42", 3); 5 | | --- ^^^^ expected `usize`, found `&str` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | note: function defined here 10 | --> $DIR/tests/integrations/basic-fail/src/lib.rs:1:8 11 | | 12 | 1 | pub fn add(left: usize, right: usize) -> usize { 13 | | ^^^ 14 | 15 | error: aborting due to 1 previous error 16 | 17 | For more information about this error, try `rustc --explain E0308`. 18 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless_yolo/revisions_bad.rs: -------------------------------------------------------------------------------- 1 | #[cfg(foo)] 2 | use basic_fail::add; 3 | //@ revisions: foo bar 4 | 5 | #[cfg(foo)] 6 | fn main() { 7 | add("42", 3); 8 | //~[foo]^ ERROR: mismatched types 9 | } 10 | //~[bar]^ ERROR: `main` function not found in crate `revisions_bad` 11 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless_yolo/rustfix-maybe-incorrect.fixed: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | 3 | fn main() { 4 | let _x: String = 42.to_string(); 5 | //~^ ERROR: mismatched types 6 | } 7 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless_yolo/rustfix-maybe-incorrect.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | 3 | fn main() { 4 | let _x: String = 42; 5 | //~^ ERROR: mismatched types 6 | } 7 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless_yolo/rustfix-maybe-incorrect.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests_bless_yolo/rustfix-maybe-incorrect.rs:4:22 3 | | 4 | 4 | let _x: String = 42; 5 | | ------ ^^- help: try using a conversion method: `.to_string()` 6 | | | | 7 | | | expected `String`, found integer 8 | | expected due to this 9 | 10 | error: aborting due to 1 previous error 11 | 12 | For more information about this error, try `rustc --explain E0308`. 13 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless_yolo/rustfix-multiple-fail.1.fixed: -------------------------------------------------------------------------------- 1 | pub fn f(_: i32) -> &'static i32 { 2 | //~^ ERROR: missing lifetime 3 | unimplemented!() 4 | } 5 | 6 | fn main() { 7 | f(1); 8 | } 9 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless_yolo/rustfix-multiple-fail.2.fixed: -------------------------------------------------------------------------------- 1 | pub fn f(_: &i32) -> &i32 { 2 | //~^ ERROR: missing lifetime 3 | unimplemented!() 4 | } 5 | 6 | fn main() { 7 | f(1); 8 | } 9 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless_yolo/rustfix-multiple-fail.3.fixed: -------------------------------------------------------------------------------- 1 | pub fn f(_: i32) -> i32 { 2 | //~^ ERROR: missing lifetime 3 | unimplemented!() 4 | } 5 | 6 | fn main() { 7 | f(1); 8 | } 9 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless_yolo/rustfix-multiple-fail.rs: -------------------------------------------------------------------------------- 1 | pub fn f(_: i32) -> &i32 { 2 | //~^ ERROR: missing lifetime 3 | unimplemented!() 4 | } 5 | 6 | fn main() { 7 | f(1); 8 | } 9 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/actual_tests_bless_yolo/rustfix-multiple-fail.stderr: -------------------------------------------------------------------------------- 1 | error[E0106]: missing lifetime specifier 2 | --> tests/actual_tests_bless_yolo/rustfix-multiple-fail.rs:1:21 3 | | 4 | 1 | pub fn f(_: i32) -> &i32 { 5 | | ^ expected named lifetime parameter 6 | | 7 | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from 8 | help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static` 9 | | 10 | 1 | pub fn f(_: i32) -> &'static i32 { 11 | | +++++++ 12 | help: instead, you are more likely to want to change the argument to be borrowed... 13 | | 14 | 1 | pub fn f(_: &i32) -> &i32 { 15 | | + 16 | help: ...or alternatively, you might want to return an owned value 17 | | 18 | 1 - pub fn f(_: i32) -> &i32 { 19 | 1 + pub fn f(_: i32) -> i32 { 20 | | 21 | 22 | error: aborting due to 1 previous error 23 | 24 | For more information about this error, try `rustc --explain E0106`. 25 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/json.rs: -------------------------------------------------------------------------------- 1 | use ui_test::{dependencies::DependencyBuilder, *}; 2 | 3 | fn main() -> ui_test::color_eyre::Result<()> { 4 | let path = "../../../target"; 5 | let mut config = Config { 6 | // Never bless integrations-fail tests, we want to see stderr mismatches 7 | output_conflict_handling: ui_test::error_on_output_conflict, 8 | bless_command: Some("DO NOT BLESS. These are meant to fail".to_string()), 9 | ..Config::rustc("tests/actual_tests") 10 | }; 11 | 12 | config 13 | .comment_defaults 14 | .base() 15 | .set_custom("dependencies", DependencyBuilder::default()); 16 | 17 | // hide binaries generated for successfully passing tests 18 | let tmp_dir = tempfile::tempdir_in(path)?; 19 | let tmp_dir = tmp_dir.path(); 20 | config.out_dir = tmp_dir.into(); 21 | config.path_stderr_filter(tmp_dir, "$TMP"); 22 | 23 | config.stderr_filter("in ([0-9]m )?[0-9\\.]+s", ""); 24 | config.stdout_filter("in ([0-9]m )?[0-9\\.]+s", ""); 25 | config.stderr_filter(r"[^ ]*/\.?cargo/registry/.*/", "$$CARGO_REGISTRY"); 26 | config.path_stderr_filter(&std::path::Path::new(path), "$DIR"); 27 | 28 | run_tests_generic( 29 | vec![config], 30 | default_file_filter, 31 | default_per_file_config, 32 | status_emitter::JSON::new(), 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/ui_tests.rs: -------------------------------------------------------------------------------- 1 | use ui_test::{dependencies::DependencyBuilder, *}; 2 | 3 | fn main() -> ui_test::color_eyre::Result<()> { 4 | let path = "../../../target"; 5 | let mut config = Config { 6 | // Never bless integrations-fail tests, we want to see stderr mismatches 7 | output_conflict_handling: ui_test::error_on_output_conflict, 8 | bless_command: Some("DO NOT BLESS. These are meant to fail".to_string()), 9 | ..Config::rustc("tests/actual_tests") 10 | }; 11 | 12 | config 13 | .comment_defaults 14 | .base() 15 | .set_custom("dependencies", DependencyBuilder::default()); 16 | 17 | // hide binaries generated for successfully passing tests 18 | let tmp_dir = tempfile::tempdir_in(path)?; 19 | let tmp_dir = tmp_dir.path(); 20 | config.out_dir = tmp_dir.into(); 21 | config.path_stderr_filter(tmp_dir, "$TMP"); 22 | 23 | config.stderr_filter("in ([0-9]m )?[0-9\\.]+s", ""); 24 | config.stdout_filter("in ([0-9]m )?[0-9\\.]+s", ""); 25 | config.stderr_filter(r"[^ ]*/\.?cargo/registry/.*/", "$$CARGO_REGISTRY"); 26 | config.path_stderr_filter(&std::path::Path::new(path), "$DIR"); 27 | 28 | run_tests_generic( 29 | vec![config], 30 | default_file_filter, 31 | default_per_file_config, 32 | // Avoid github actions, as these would end up showing up in `Cargo.stderr` 33 | status_emitter::Text::verbose(), 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/ui_tests_bless.rs: -------------------------------------------------------------------------------- 1 | use ui_test::{ 2 | custom_flags::rustfix::RustfixMode, dependencies::DependencyBuilder, spanned::Spanned, *, 3 | }; 4 | 5 | fn main() -> ui_test::color_eyre::Result<()> { 6 | for rustfix in [RustfixMode::MachineApplicable, RustfixMode::Everything] { 7 | let path = "../../../target"; 8 | 9 | let root_dir = match rustfix { 10 | RustfixMode::Everything => "tests/actual_tests_bless_yolo", 11 | RustfixMode::MachineApplicable { .. } => "tests/actual_tests_bless", 12 | _ => unreachable!(), 13 | }; 14 | 15 | let mut config = Config { 16 | output_conflict_handling: if std::env::var_os("BLESS").is_some() { 17 | ui_test::bless_output_files 18 | } else { 19 | ui_test::error_on_output_conflict 20 | }, 21 | bless_command: Some("cargo test".to_string()), 22 | ..Config::rustc(root_dir) 23 | }; 24 | config.comment_defaults.base().exit_status = match rustfix { 25 | RustfixMode::Everything => None.into(), 26 | RustfixMode::MachineApplicable { .. } => Spanned::dummy(1).into(), 27 | _ => unreachable!(), 28 | }; 29 | config 30 | .comment_defaults 31 | .base() 32 | .set_custom("rustfix", rustfix); 33 | 34 | config 35 | .comment_defaults 36 | .base() 37 | .set_custom("dependencies", DependencyBuilder::default()); 38 | 39 | // hide binaries generated for successfully passing tests 40 | let tmp_dir = tempfile::tempdir_in(path)?; 41 | let tmp_dir = tmp_dir.path(); 42 | config.out_dir = tmp_dir.into(); 43 | config.path_stderr_filter(tmp_dir, "$TMP"); 44 | 45 | config.stderr_filter("in ([0-9]m )?[0-9\\.]+s", ""); 46 | config.stdout_filter("in ([0-9]m )?[0-9\\.]+s", ""); 47 | config.stderr_filter(r"[^ ]*/\.?cargo/registry/.*/", "$$CARGO_REGISTRY"); 48 | config.path_stderr_filter(&std::path::Path::new(path), "$DIR"); 49 | 50 | // This is part of a test: we want to make sure the base filters are run first 51 | config.stderr_filter("normalization_override", "NORMALIZATION_OVERRIDE"); 52 | 53 | let _ = run_tests_generic( 54 | vec![config], 55 | default_file_filter, 56 | default_per_file_config, 57 | // Avoid github actions, as these would end up showing up in `Cargo.stderr` 58 | status_emitter::Text::verbose(), 59 | ); 60 | } 61 | Ok(()) 62 | } 63 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/ui_tests_diff_only.rs: -------------------------------------------------------------------------------- 1 | use ui_test::*; 2 | 3 | fn main() -> ui_test::color_eyre::Result<()> { 4 | let config = Config { 5 | // Never bless integrations-fail tests, we want to see stderr mismatches 6 | output_conflict_handling: ui_test::error_on_output_conflict, 7 | bless_command: Some("DO NOT BLESS. These are meant to fail".to_string()), 8 | ..Config::rustc("tests/actual_tests") 9 | }; 10 | 11 | run_tests_generic( 12 | vec![config], 13 | default_file_filter, 14 | default_per_file_config, 15 | // Avoid github actions, as these would end up showing up in `Cargo.stderr` 16 | status_emitter::Text::diff(), 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/ui_tests_invalid_program.rs: -------------------------------------------------------------------------------- 1 | use ui_test::*; 2 | 3 | fn main() -> ui_test::color_eyre::Result<()> { 4 | let config = Config { 5 | program: CommandBuilder::cmd("invalid_foobarlaksdfalsdfj"), 6 | // Never bless integrations-fail tests, we want to see stderr mismatches 7 | output_conflict_handling: ui_test::error_on_output_conflict, 8 | bless_command: Some("DO NOT BLESS. These are meant to fail".to_string()), 9 | ..Config::rustc("tests/actual_tests") 10 | }; 11 | 12 | run_tests_generic( 13 | vec![config], 14 | default_file_filter, 15 | default_per_file_config, 16 | // Avoid github actions, as these would end up showing up in `Cargo.stderr` 17 | status_emitter::Text::verbose(), 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /tests/integrations/basic-fail/tests/ui_tests_invalid_program2.rs: -------------------------------------------------------------------------------- 1 | use ui_test::*; 2 | 3 | fn main() -> ui_test::color_eyre::Result<()> { 4 | let config = Config { 5 | program: CommandBuilder::cmd("invalid_foobarlaksdfalsdfj"), 6 | // Never bless integrations-fail tests, we want to see stderr mismatches 7 | output_conflict_handling: ui_test::error_on_output_conflict, 8 | bless_command: Some("DO NOT BLESS. These are meant to fail".to_string()), 9 | host: Some("foo".into()), 10 | ..Config::rustc("tests/actual_tests") 11 | }; 12 | 13 | run_tests_generic( 14 | vec![config], 15 | default_file_filter, 16 | default_per_file_config, 17 | // Avoid github actions, as these would end up showing up in `Cargo.stderr` 18 | status_emitter::Text::verbose(), 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /tests/integrations/basic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | serde_derive = "1.0" 10 | 11 | [dev-dependencies] 12 | ui_test = { path = "../../.."} 13 | tempfile = "3.3.0" 14 | 15 | [[test]] 16 | name = "ui_tests" 17 | harness = false 18 | 19 | [[test]] 20 | name = "json" 21 | harness = false 22 | 23 | [[test]] 24 | name = "run_file" 25 | harness = true 26 | 27 | # Regression check for #225 28 | [lib] 29 | crate-type = ["staticlib", "lib"] 30 | -------------------------------------------------------------------------------- /tests/integrations/basic/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub fn add(left: usize, right: usize) -> usize { 2 | left + right 3 | } 4 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/aux_attr_proc_macro.rs: -------------------------------------------------------------------------------- 1 | //@aux-build:proc_macro_attr.rs 2 | //@check-pass 3 | 4 | extern crate proc_macro_attr; 5 | 6 | #[proc_macro_attr::passthrough] 7 | pub fn f() {} 8 | 9 | pub fn g() {} 10 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/aux_derive.fixed: -------------------------------------------------------------------------------- 1 | //@aux-build:derive_proc_macro.rs 2 | 3 | #[macro_use] 4 | extern crate derive_proc_macro; 5 | 6 | fn main() { 7 | let mut x = Foo; 8 | x = Foo; 9 | //~^ ERROR: cannot assign twice 10 | } 11 | 12 | #[derive(Something)] 13 | struct Foo; 14 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/aux_derive.rs: -------------------------------------------------------------------------------- 1 | //@aux-build:derive_proc_macro.rs 2 | 3 | #[macro_use] 4 | extern crate derive_proc_macro; 5 | 6 | fn main() { 7 | let x = Foo; 8 | x = Foo; 9 | //~^ ERROR: cannot assign twice 10 | } 11 | 12 | #[derive(Something)] 13 | struct Foo; 14 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/aux_derive.stderr: -------------------------------------------------------------------------------- 1 | warning: variable `x` is assigned to, but never used 2 | --> tests/actual_tests/aux_derive.rs:7:9 3 | | 4 | 7 | let x = Foo; 5 | | ^ 6 | | 7 | = note: consider using `_x` instead 8 | = note: `#[warn(unused_variables)]` on by default 9 | 10 | warning: value assigned to `x` is never read 11 | --> tests/actual_tests/aux_derive.rs:8:5 12 | | 13 | 8 | x = Foo; 14 | | ^ 15 | | 16 | = help: maybe it is overwritten before being read? 17 | = note: `#[warn(unused_assignments)]` on by default 18 | 19 | error[E0384]: cannot assign twice to immutable variable `x` 20 | --> tests/actual_tests/aux_derive.rs:8:5 21 | | 22 | 7 | let x = Foo; 23 | | - 24 | | | 25 | | first assignment to `x` 26 | | help: consider making this binding mutable: `mut x` 27 | 8 | x = Foo; 28 | | ^^^^^^^ cannot assign twice to immutable variable 29 | 30 | error: aborting due to 1 previous error; 2 warnings emitted 31 | 32 | For more information about this error, try `rustc --explain E0384`. 33 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/aux_proc_macro.rs: -------------------------------------------------------------------------------- 1 | //@aux-build:the_proc_macro.rs 2 | 3 | use the_proc_macro::thing; 4 | 5 | fn main() { 6 | thing!(cake); 7 | //~^ ERROR: cannot find value `cake` in this scope 8 | } 9 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/aux_proc_macro.stderr: -------------------------------------------------------------------------------- 1 | error[E0425]: cannot find value `cake` in this scope 2 | --> tests/actual_tests/aux_proc_macro.rs:6:12 3 | | 4 | 6 | thing!(cake); 5 | | ^^^^ not found in this scope 6 | 7 | error: aborting due to 1 previous error 8 | 9 | For more information about this error, try `rustc --explain E0425`. 10 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/auxiliary/derive_proc_macro.rs: -------------------------------------------------------------------------------- 1 | //@compile-flags: --emit=link -C prefer-dynamic=no 2 | 3 | #![crate_type = "proc-macro"] 4 | 5 | extern crate proc_macro; 6 | 7 | use proc_macro::TokenStream; 8 | 9 | #[proc_macro_derive(Something)] 10 | pub fn noop(_: TokenStream) -> TokenStream { 11 | TokenStream::new() 12 | } 13 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/auxiliary/proc_macro_attr.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use proc_macro::TokenStream; 4 | 5 | #[proc_macro_attribute] 6 | pub fn passthrough(_: TokenStream, item: TokenStream) -> TokenStream { 7 | item 8 | } 9 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/auxiliary/the_proc_macro.rs: -------------------------------------------------------------------------------- 1 | #![crate_type = "proc-macro"] 2 | 3 | extern crate proc_macro; 4 | 5 | use proc_macro::TokenStream; 6 | 7 | #[proc_macro] 8 | pub fn thing(input: TokenStream) -> TokenStream { 9 | input 10 | } 11 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/dep_derive.rs: -------------------------------------------------------------------------------- 1 | //@run 2 | 3 | #[macro_use] 4 | extern crate serde_derive; 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/dep_derive.stderr: -------------------------------------------------------------------------------- 1 | warning: unused `#[macro_use]` import 2 | --> tests/actual_tests/dep_derive.rs:3:1 3 | | 4 | 3 | #[macro_use] 5 | | ^^^^^^^^^^^^ 6 | | 7 | = note: `#[warn(unused_imports)]` on by default 8 | 9 | warning: 1 warning emitted 10 | 11 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/error_above.rs: -------------------------------------------------------------------------------- 1 | use basic::add; 2 | 3 | fn main() { 4 | //~v ERROR: mismatched types 5 | add("42", 3); 6 | } 7 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/error_above.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests/error_above.rs:5:9 3 | | 4 | 5 | add("42", 3); 5 | | --- ^^^^ expected `usize`, found `&str` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | note: function defined here 10 | --> $DIR/tests/integrations/basic/src/lib.rs:1:8 11 | | 12 | 1 | pub fn add(left: usize, right: usize) -> usize { 13 | | ^^^ 14 | 15 | error: aborting due to 1 previous error 16 | 17 | For more information about this error, try `rustc --explain E0308`. 18 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/error_below_multi_rev.edition2015.stderr: -------------------------------------------------------------------------------- 1 | error: unused import: `::std::vec` 2 | --> tests/actual_tests/error_below_multi_rev.rs:9:5 3 | | 4 | 9 | use ::std::vec; 5 | | ^^^^^^^^^^ 6 | | 7 | note: the lint level is defined here 8 | --> tests/actual_tests/error_below_multi_rev.rs:6:20 9 | | 10 | 6 | #![deny(dead_code, unused_imports)] 11 | | ^^^^^^^^^^^^^^ 12 | 13 | error: unused import: `::m::foo` 14 | --> tests/actual_tests/error_below_multi_rev.rs:18:5 15 | | 16 | 18 | use ::m::foo; 17 | | ^^^^^^^^ 18 | 19 | error: function `foo` is never used 20 | --> tests/actual_tests/error_below_multi_rev.rs:13:10 21 | | 22 | 13 | pub fn foo() {} 23 | | ^^^ 24 | | 25 | note: the lint level is defined here 26 | --> tests/actual_tests/error_below_multi_rev.rs:6:9 27 | | 28 | 6 | #![deny(dead_code, unused_imports)] 29 | | ^^^^^^^^^ 30 | 31 | error: aborting due to 3 previous errors 32 | 33 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/error_below_multi_rev.edition2018.stderr: -------------------------------------------------------------------------------- 1 | error[E0432]: unresolved import `m` 2 | --> tests/actual_tests/error_below_multi_rev.rs:18:7 3 | | 4 | 18 | use ::m::foo; 5 | | ^ help: a similar path exists: `self::m` 6 | 7 | error: unused import: `::std::vec` 8 | --> tests/actual_tests/error_below_multi_rev.rs:9:5 9 | | 10 | 9 | use ::std::vec; 11 | | ^^^^^^^^^^ 12 | | 13 | note: the lint level is defined here 14 | --> tests/actual_tests/error_below_multi_rev.rs:6:20 15 | | 16 | 6 | #![deny(dead_code, unused_imports)] 17 | | ^^^^^^^^^^^^^^ 18 | 19 | error: aborting due to 2 previous errors 20 | 21 | For more information about this error, try `rustc --explain E0432`. 22 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/error_below_multi_rev.rs: -------------------------------------------------------------------------------- 1 | //@no-rustfix 2 | //@revisions: edition2015 edition2018 3 | //@[edition2015] edition:2015 4 | //@[edition2018] edition:2018 5 | 6 | #![deny(dead_code, unused_imports)] 7 | 8 | //~v unused_imports 9 | use ::std::vec; 10 | 11 | mod m { 12 | //~[edition2015]v dead_code 13 | pub fn foo() {} 14 | } 15 | 16 | //~[edition2015]| unused_imports 17 | //~[edition2018]v E0432 18 | use ::m::foo; 19 | 20 | fn main() {} 21 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/executable.rs: -------------------------------------------------------------------------------- 1 | use basic::add; 2 | 3 | //@run 4 | 5 | fn main() { 6 | println!("{}", add(20, 22)); 7 | } 8 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/executable.run.stdout: -------------------------------------------------------------------------------- 1 | 42 2 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/foomp-rustfix.fixed: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | 3 | fn main() { 4 | let x = 42; 5 | //~^ ERROR: does not need to be mutable 6 | println!("{x}"); 7 | } 8 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/foomp-rustfix.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | 3 | fn main() { 4 | let mut x = 42; 5 | //~^ ERROR: does not need to be mutable 6 | println!("{x}"); 7 | } 8 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/foomp-rustfix.stderr: -------------------------------------------------------------------------------- 1 | error: variable does not need to be mutable 2 | --> tests/actual_tests/foomp-rustfix.rs:4:9 3 | | 4 | 4 | let mut x = 42; 5 | | ----^ 6 | | | 7 | | help: remove this `mut` 8 | | 9 | note: the lint level is defined here 10 | --> tests/actual_tests/foomp-rustfix.rs:1:9 11 | | 12 | 1 | #![deny(warnings)] 13 | | ^^^^^^^^ 14 | = note: `#[deny(unused_mut)]` implied by `#[deny(warnings)]` 15 | 16 | error: aborting due to 1 previous error 17 | 18 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/foomp.rs: -------------------------------------------------------------------------------- 1 | use basic::add; 2 | 3 | fn main() { 4 | // 5 | add("42", 3); 6 | //~^ ERROR: mismatched types 7 | } 8 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/foomp.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests/foomp.rs:5:9 3 | | 4 | 5 | add("42", 3); 5 | | --- ^^^^ expected `usize`, found `&str` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | note: function defined here 10 | --> $DIR/tests/integrations/basic/src/lib.rs:1:8 11 | | 12 | 1 | pub fn add(left: usize, right: usize) -> usize { 13 | | ^^^ 14 | 15 | error: aborting due to 1 previous error 16 | 17 | For more information about this error, try `rustc --explain E0308`. 18 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/joined_above.fixed: -------------------------------------------------------------------------------- 1 | #![deny(unused_mut, unused_variables)] 2 | 3 | fn main() { 4 | //~| unused_mut 5 | //~v unused_variables 6 | let _x = 0u32; 7 | } 8 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/joined_above.rs: -------------------------------------------------------------------------------- 1 | #![deny(unused_mut, unused_variables)] 2 | 3 | fn main() { 4 | //~| unused_mut 5 | //~v unused_variables 6 | let mut x = 0u32; 7 | } 8 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/joined_above.stderr: -------------------------------------------------------------------------------- 1 | error: unused variable: `x` 2 | --> tests/actual_tests/joined_above.rs:6:13 3 | | 4 | 6 | let mut x = 0u32; 5 | | ^ help: if this is intentional, prefix it with an underscore: `_x` 6 | | 7 | note: the lint level is defined here 8 | --> tests/actual_tests/joined_above.rs:1:21 9 | | 10 | 1 | #![deny(unused_mut, unused_variables)] 11 | | ^^^^^^^^^^^^^^^^ 12 | 13 | error: variable does not need to be mutable 14 | --> tests/actual_tests/joined_above.rs:6:9 15 | | 16 | 6 | let mut x = 0u32; 17 | | ----^ 18 | | | 19 | | help: remove this `mut` 20 | | 21 | note: the lint level is defined here 22 | --> tests/actual_tests/joined_above.rs:1:9 23 | | 24 | 1 | #![deny(unused_mut, unused_variables)] 25 | | ^^^^^^^^^^ 26 | 27 | error: aborting due to 2 previous errors 28 | 29 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/joined_below.fixed: -------------------------------------------------------------------------------- 1 | #![deny(unused_mut, unused_variables)] 2 | 3 | fn main() { 4 | let _x = 0u32; 5 | //~^ unused_variables 6 | //~| unused_mut 7 | } 8 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/joined_below.rs: -------------------------------------------------------------------------------- 1 | #![deny(unused_mut, unused_variables)] 2 | 3 | fn main() { 4 | let mut x = 0u32; 5 | //~^ unused_variables 6 | //~| unused_mut 7 | } 8 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/joined_below.stderr: -------------------------------------------------------------------------------- 1 | error: unused variable: `x` 2 | --> tests/actual_tests/joined_below.rs:4:13 3 | | 4 | 4 | let mut x = 0u32; 5 | | ^ help: if this is intentional, prefix it with an underscore: `_x` 6 | | 7 | note: the lint level is defined here 8 | --> tests/actual_tests/joined_below.rs:1:21 9 | | 10 | 1 | #![deny(unused_mut, unused_variables)] 11 | | ^^^^^^^^^^^^^^^^ 12 | 13 | error: variable does not need to be mutable 14 | --> tests/actual_tests/joined_below.rs:4:9 15 | | 16 | 4 | let mut x = 0u32; 17 | | ----^ 18 | | | 19 | | help: remove this `mut` 20 | | 21 | note: the lint level is defined here 22 | --> tests/actual_tests/joined_below.rs:1:9 23 | | 24 | 1 | #![deny(unused_mut, unused_variables)] 25 | | ^^^^^^^^^^ 26 | 27 | error: aborting due to 2 previous errors 28 | 29 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/joined_mixed.fixed: -------------------------------------------------------------------------------- 1 | #![deny(unused_mut, unused_variables)] 2 | 3 | fn main() { 4 | let _x = 0u32; 5 | //~^ unused_variables 6 | //~| unused_mut 7 | 8 | //~| unused_mut 9 | //~v unused_variables 10 | let _y = 0u32; 11 | } 12 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/joined_mixed.rs: -------------------------------------------------------------------------------- 1 | #![deny(unused_mut, unused_variables)] 2 | 3 | fn main() { 4 | let mut x = 0u32; 5 | //~^ unused_variables 6 | //~| unused_mut 7 | 8 | //~| unused_mut 9 | //~v unused_variables 10 | let mut y = 0u32; 11 | } 12 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/joined_mixed.stderr: -------------------------------------------------------------------------------- 1 | error: unused variable: `x` 2 | --> tests/actual_tests/joined_mixed.rs:4:13 3 | | 4 | 4 | let mut x = 0u32; 5 | | ^ help: if this is intentional, prefix it with an underscore: `_x` 6 | | 7 | note: the lint level is defined here 8 | --> tests/actual_tests/joined_mixed.rs:1:21 9 | | 10 | 1 | #![deny(unused_mut, unused_variables)] 11 | | ^^^^^^^^^^^^^^^^ 12 | 13 | error: unused variable: `y` 14 | --> tests/actual_tests/joined_mixed.rs:10:13 15 | | 16 | 10 | let mut y = 0u32; 17 | | ^ help: if this is intentional, prefix it with an underscore: `_y` 18 | 19 | error: variable does not need to be mutable 20 | --> tests/actual_tests/joined_mixed.rs:4:9 21 | | 22 | 4 | let mut x = 0u32; 23 | | ----^ 24 | | | 25 | | help: remove this `mut` 26 | | 27 | note: the lint level is defined here 28 | --> tests/actual_tests/joined_mixed.rs:1:9 29 | | 30 | 1 | #![deny(unused_mut, unused_variables)] 31 | | ^^^^^^^^^^ 32 | 33 | error: variable does not need to be mutable 34 | --> tests/actual_tests/joined_mixed.rs:10:9 35 | | 36 | 10 | let mut y = 0u32; 37 | | ----^ 38 | | | 39 | | help: remove this `mut` 40 | 41 | error: aborting due to 4 previous errors 42 | 43 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/mac_span.fixed: -------------------------------------------------------------------------------- 1 | #![deny(unused_variables)] 2 | 3 | macro_rules! m { 4 | () => {{ 5 | let _x = 0; //~ unused_variables 6 | }}; 7 | } 8 | 9 | fn main() { 10 | m!(); 11 | } 12 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/mac_span.rs: -------------------------------------------------------------------------------- 1 | #![deny(unused_variables)] 2 | 3 | macro_rules! m { 4 | () => {{ 5 | let x = 0; //~ unused_variables 6 | }}; 7 | } 8 | 9 | fn main() { 10 | m!(); 11 | } 12 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/mac_span.stderr: -------------------------------------------------------------------------------- 1 | error: unused variable: `x` 2 | --> tests/actual_tests/mac_span.rs:5:13 3 | | 4 | 5 | let x = 0; 5 | | ^ help: if this is intentional, prefix it with an underscore: `_x` 6 | ... 7 | 10 | m!(); 8 | | ---- in this macro invocation 9 | | 10 | note: the lint level is defined here 11 | --> tests/actual_tests/mac_span.rs:1:9 12 | | 13 | 1 | #![deny(unused_variables)] 14 | | ^^^^^^^^^^^^^^^^ 15 | = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) 16 | 17 | error: aborting due to 1 previous error 18 | 19 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/match_diagnostic_code.fixed: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let _x: i32 = 0i32; //~ E0308 3 | } 4 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/match_diagnostic_code.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let _x: i32 = 0u32; //~ E0308 3 | } 4 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/match_diagnostic_code.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests/match_diagnostic_code.rs:2:19 3 | | 4 | 2 | let _x: i32 = 0u32; 5 | | --- ^^^^ expected `i32`, found `u32` 6 | | | 7 | | expected due to this 8 | | 9 | help: change the type of the numeric literal from `u32` to `i32` 10 | | 11 | 2 | let _x: i32 = 0i32; 12 | | ~~~ 13 | 14 | error: aborting due to 1 previous error 15 | 16 | For more information about this error, try `rustc --explain E0308`. 17 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/no_rustfix.rs: -------------------------------------------------------------------------------- 1 | //@no-rustfix 2 | 3 | #![deny(warnings)] 4 | 5 | fn main() { 6 | let mut x = 42; 7 | //~^ ERROR: does not need to be mutable 8 | println!("{x}"); 9 | } 10 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/no_rustfix.stderr: -------------------------------------------------------------------------------- 1 | error: variable does not need to be mutable 2 | --> tests/actual_tests/no_rustfix.rs:6:9 3 | | 4 | 6 | let mut x = 42; 5 | | ----^ 6 | | | 7 | | help: remove this `mut` 8 | | 9 | note: the lint level is defined here 10 | --> tests/actual_tests/no_rustfix.rs:3:9 11 | | 12 | 3 | #![deny(warnings)] 13 | | ^^^^^^^^ 14 | = note: `#[deny(unused_mut)]` implied by `#[deny(warnings)]` 15 | 16 | error: aborting due to 1 previous error 17 | 18 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/rustfix-multiple.1.fixed: -------------------------------------------------------------------------------- 1 | pub fn f() -> usize { 2 | 1 3 | } 4 | 5 | pub fn g() { 6 | f(); //~ ERROR: mismatched types 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/rustfix-multiple.2.fixed: -------------------------------------------------------------------------------- 1 | pub fn f() -> usize { 2 | 1 3 | } 4 | 5 | pub fn g() -> usize { 6 | f() //~ ERROR: mismatched types 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/rustfix-multiple.rs: -------------------------------------------------------------------------------- 1 | pub fn f() -> usize { 2 | 1 3 | } 4 | 5 | pub fn g() { 6 | f() //~ ERROR: mismatched types 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/rustfix-multiple.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests/rustfix-multiple.rs:6:5 3 | | 4 | 6 | f() 5 | | ^^^ expected `()`, found `usize` 6 | | 7 | help: consider using a semicolon here 8 | | 9 | 6 | f(); 10 | | + 11 | help: try adding a return type 12 | | 13 | 5 | pub fn g() -> usize { 14 | | ++++++++ 15 | 16 | error: aborting due to 1 previous error 17 | 18 | For more information about this error, try `rustc --explain E0308`. 19 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/stdin.rs: -------------------------------------------------------------------------------- 1 | //@run 2 | 3 | fn main() { 4 | let mut n = 0; 5 | for line in std::io::stdin().lines() { 6 | n += 1; 7 | eprintln!("{}", line.unwrap()); 8 | } 9 | assert_eq!(n, 6); 10 | } 11 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/stdin.run.stderr: -------------------------------------------------------------------------------- 1 | foo 2 | 3 | bar 4 | 5 | baz 6 | 7 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/stdin.run.stdin: -------------------------------------------------------------------------------- 1 | foo 2 | 3 | bar 4 | 5 | baz 6 | 7 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/subdir/aux_proc_macro.rs: -------------------------------------------------------------------------------- 1 | //@aux-build:../auxiliary/the_proc_macro.rs 2 | 3 | use the_proc_macro::thing; 4 | 5 | fn main() { 6 | thing!(cake); 7 | //~^ ERROR: cannot find value `cake` in this scope 8 | } 9 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/subdir/aux_proc_macro.stderr: -------------------------------------------------------------------------------- 1 | error[E0425]: cannot find value `cake` in this scope 2 | --> tests/actual_tests/subdir/aux_proc_macro.rs:6:12 3 | | 4 | 6 | thing!(cake); 5 | | ^^^^ not found in this scope 6 | 7 | error: aborting due to 1 previous error 8 | 9 | For more information about this error, try `rustc --explain E0425`. 10 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/unicode.rs: -------------------------------------------------------------------------------- 1 | //@check-pass 2 | 3 | #[test] 4 | pub fn issue_7739() { 5 | // Ryū crate: https://github.com/dtolnay/ryu 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/windows_paths.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let () = "escapes stay as backslashes: \t\r\n"; 3 | //~^ ERROR: mismatched types 4 | 5 | let () = r"absolute: C:\foo\file.rs"; 6 | //~^ ERROR: mismatched types 7 | let () = r"absolute, spaces: C:\foo bar\file.rs"; 8 | //~^ ERROR: mismatched types 9 | let () = r"absolute, spaces, dir: C:\foo bar\some dir\"; 10 | //~^ ERROR: mismatched types 11 | let () = r"absolute, spaces, no extension: C:\foo bar\some file"; 12 | //~^ ERROR: mismatched types 13 | 14 | let () = r"relative: foo\file.rs"; 15 | //~^ ERROR: mismatched types 16 | 17 | let () = r"unicode: Ryū\file.rs"; 18 | //~^ ERROR: mismatched types 19 | 20 | let () = r"mixed seperators: C:\foo/../bar\"; 21 | //~^ ERROR: mismatched types 22 | 23 | let () = r"unsupported: foo\bar"; 24 | //~^ ERROR: mismatched types 25 | } 26 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/actual_tests/windows_paths.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/actual_tests/windows_paths.rs:2:9 3 | | 4 | 2 | let () = "escapes stay as backslashes: \t\r\n"; 5 | | ^^ ------------------------------------- this expression has type `&str` 6 | | | 7 | | expected `str`, found `()` 8 | 9 | error[E0308]: mismatched types 10 | --> tests/actual_tests/windows_paths.rs:5:9 11 | | 12 | 5 | let () = r"absolute: C:/foo/file.rs"; 13 | | ^^ --------------------------- this expression has type `&str` 14 | | | 15 | | expected `str`, found `()` 16 | 17 | error[E0308]: mismatched types 18 | --> tests/actual_tests/windows_paths.rs:7:9 19 | | 20 | 7 | let () = r"absolute, spaces: C:/foo bar/file.rs"; 21 | | ^^ --------------------------------------- this expression has type `&str` 22 | | | 23 | | expected `str`, found `()` 24 | 25 | error[E0308]: mismatched types 26 | --> tests/actual_tests/windows_paths.rs:9:9 27 | | 28 | 9 | let () = r"absolute, spaces, dir: C:/foo bar/some dir/"; 29 | | ^^ ---------------------------------------------- this expression has type `&str` 30 | | | 31 | | expected `str`, found `()` 32 | 33 | error[E0308]: mismatched types 34 | --> tests/actual_tests/windows_paths.rs:11:9 35 | | 36 | 11 | let () = r"absolute, spaces, no extension: C:/foo bar/some file"; 37 | | ^^ ------------------------------------------------------- this expression has type `&str` 38 | | | 39 | | expected `str`, found `()` 40 | 41 | error[E0308]: mismatched types 42 | --> tests/actual_tests/windows_paths.rs:14:9 43 | | 44 | 14 | let () = r"relative: foo/file.rs"; 45 | | ^^ ------------------------ this expression has type `&str` 46 | | | 47 | | expected `str`, found `()` 48 | 49 | error[E0308]: mismatched types 50 | --> tests/actual_tests/windows_paths.rs:17:9 51 | | 52 | 17 | let () = r"unicode: Ryū/file.rs"; 53 | | ^^ ----------------------- this expression has type `&str` 54 | | | 55 | | expected `str`, found `()` 56 | 57 | error[E0308]: mismatched types 58 | --> tests/actual_tests/windows_paths.rs:20:9 59 | | 60 | 20 | let () = r"mixed seperators: C:/foo/../bar/"; 61 | | ^^ ----------------------------------- this expression has type `&str` 62 | | | 63 | | expected `str`, found `()` 64 | 65 | error[E0308]: mismatched types 66 | --> tests/actual_tests/windows_paths.rs:23:9 67 | | 68 | 23 | let () = r"unsupported: foo\bar"; 69 | | ^^ ----------------------- this expression has type `&str` 70 | | | 71 | | expected `str`, found `()` 72 | 73 | error: aborting due to 9 previous errors 74 | 75 | For more information about this error, try `rustc --explain E0308`. 76 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/json.rs: -------------------------------------------------------------------------------- 1 | use ui_test::{custom_flags::rustfix::RustfixMode, dependencies::DependencyBuilder, *}; 2 | 3 | fn main() -> ui_test::color_eyre::Result<()> { 4 | let path = "../../../target"; 5 | let mut config = Config { 6 | output_conflict_handling: if std::env::var_os("BLESS").is_some() { 7 | ui_test::bless_output_files 8 | } else { 9 | ui_test::error_on_output_conflict 10 | }, 11 | bless_command: Some("cargo test".to_string()), 12 | ..Config::rustc("tests/actual_tests") 13 | }; 14 | config.stderr_filter("in ([0-9]m )?[0-9\\.]+s", ""); 15 | config.stdout_filter("in ([0-9]m )?[0-9\\.]+s", ""); 16 | config.stderr_filter(r"[^ ]*/\.?cargo/registry/.*/", "$$CARGO_REGISTRY"); 17 | config.path_stderr_filter(&std::path::Path::new(path), "$DIR"); 18 | config 19 | .comment_defaults 20 | .base() 21 | .set_custom("dependencies", DependencyBuilder::default()); 22 | config 23 | .comment_defaults 24 | .base() 25 | .set_custom("rustfix", RustfixMode::Everything); 26 | 27 | if let Ok(target) = std::env::var("UITEST_TEST_TARGET") { 28 | config.target = Some(target); 29 | config.output_conflict_handling = ui_test::ignore_output_conflict; 30 | } 31 | 32 | // hide binaries generated for successfully passing tests 33 | let tmp_dir = tempfile::tempdir_in(path)?; 34 | let tmp_dir = tmp_dir.path(); 35 | config.out_dir = tmp_dir.into(); 36 | config.path_stderr_filter(tmp_dir, "$TMP"); 37 | 38 | run_tests_generic( 39 | vec![config], 40 | default_file_filter, 41 | default_per_file_config, 42 | status_emitter::JSON::new(), 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/run_file.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | use ui_test::color_eyre::{eyre::ensure, Result}; 3 | use ui_test::dependencies::DependencyBuilder; 4 | use ui_test::*; 5 | 6 | #[test] 7 | fn run_file() -> Result<()> { 8 | let mut config = Config::rustc(PathBuf::new()); 9 | 10 | let tmp_dir = tempfile::tempdir_in(env!("CARGO_TARGET_TMPDIR"))?; 11 | let tmp_dir = tmp_dir.path(); 12 | config.out_dir = tmp_dir.into(); 13 | config.path_stderr_filter(tmp_dir, "$TMP"); 14 | 15 | let mut result = ui_test::test_command( 16 | config, 17 | &Path::new(file!()) 18 | .parent() 19 | .unwrap() 20 | .join("run_file/run_file.rs"), 21 | )?; 22 | ensure!(result.output()?.status.success(), ""); 23 | Ok(()) 24 | } 25 | 26 | #[test] 27 | fn run_file_with_deps() -> Result<()> { 28 | let path = "../../../target"; 29 | 30 | let mut config = Config::rustc(PathBuf::new()); 31 | 32 | let tmp_dir = tempfile::tempdir_in(path)?; 33 | let tmp_dir = tmp_dir.path(); 34 | config.out_dir = tmp_dir.into(); 35 | config.path_stderr_filter(tmp_dir, "$TMP"); 36 | 37 | // Don't build a binary, we only provide .rmeta dependencies for now 38 | config.program.args.push("--emit=metadata".into()); 39 | config 40 | .comment_defaults 41 | .base() 42 | .set_custom("dependencies", DependencyBuilder::default()); 43 | 44 | let mut result = ui_test::test_command( 45 | config, 46 | &Path::new(file!()) 47 | .parent() 48 | .unwrap() 49 | .join("run_file/run_file_with_deps.rs"), 50 | )?; 51 | ensure!(result.output()?.status.success(), ""); 52 | Ok(()) 53 | } 54 | 55 | #[test] 56 | fn non_utf8() -> Result<()> { 57 | let path = Path::new(file!()) 58 | .parent() 59 | .unwrap() 60 | .join("run_file/non_utf8"); 61 | if cfg!(windows) { 62 | return Ok(()); 63 | } 64 | let mut config = Config::rustc(PathBuf::new()); 65 | config.program = CommandBuilder::cmd("cat"); 66 | config.comment_defaults.base().custom.clear(); 67 | config.host = Some(String::new()); 68 | 69 | let mut result = ui_test::test_command(config, &path)?; 70 | ensure!(result.output()?.status.success(), ""); 71 | Ok(()) 72 | } 73 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/run_file/non_utf8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oli-obk/ui_test/597d4f562fd3749ade663b7cc3f1ff467b55695e/tests/integrations/basic/tests/run_file/non_utf8 -------------------------------------------------------------------------------- /tests/integrations/basic/tests/run_file/run_file.rs: -------------------------------------------------------------------------------- 1 | fn main() {} 2 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/run_file/run_file_with_deps.rs: -------------------------------------------------------------------------------- 1 | use basic::add; 2 | 3 | fn main() { 4 | assert_eq!(add(1, 2), 3); 5 | } 6 | -------------------------------------------------------------------------------- /tests/integrations/basic/tests/ui_tests.rs: -------------------------------------------------------------------------------- 1 | use ui_test::{custom_flags::rustfix::RustfixMode, dependencies::DependencyBuilder, *}; 2 | 3 | fn main() -> ui_test::color_eyre::Result<()> { 4 | let path = "../../../target"; 5 | let mut config = Config { 6 | output_conflict_handling: if std::env::var_os("BLESS").is_some() { 7 | ui_test::bless_output_files 8 | } else { 9 | ui_test::error_on_output_conflict 10 | }, 11 | bless_command: Some("cargo test".to_string()), 12 | ..Config::rustc("tests/actual_tests") 13 | }; 14 | config.stderr_filter("in ([0-9]m )?[0-9\\.]+s", ""); 15 | config.stdout_filter("in ([0-9]m )?[0-9\\.]+s", ""); 16 | config.stderr_filter(r"[^ ]*/\.?cargo/registry/.*/", "$$CARGO_REGISTRY"); 17 | config.path_stderr_filter(&std::path::Path::new(path), "$DIR"); 18 | config 19 | .comment_defaults 20 | .base() 21 | .set_custom("dependencies", DependencyBuilder::default()); 22 | config 23 | .comment_defaults 24 | .base() 25 | .set_custom("rustfix", RustfixMode::Everything); 26 | 27 | if let Ok(target) = std::env::var("UITEST_TEST_TARGET") { 28 | config.target = Some(target); 29 | config.output_conflict_handling = ui_test::ignore_output_conflict; 30 | } 31 | 32 | // hide binaries generated for successfully passing tests 33 | let tmp_dir = tempfile::tempdir_in(path)?; 34 | let tmp_dir = tmp_dir.path(); 35 | config.out_dir = tmp_dir.into(); 36 | config.path_stderr_filter(tmp_dir, "$TMP"); 37 | 38 | run_tests_generic( 39 | vec![config], 40 | default_file_filter, 41 | default_per_file_config, 42 | // Avoid github actions, as these would end up showing up in `Cargo.stderr` 43 | status_emitter::Text::verbose(), 44 | ) 45 | } 46 | -------------------------------------------------------------------------------- /tests/integrations/cargo-run/Cargo.stdout: -------------------------------------------------------------------------------- 1 | 2 | running 0 tests 3 | 4 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished 5 | 6 | tests/actual_tests/empty_no_stdin.rs ... ok 7 | tests/actual_tests/matching_stdin.rs ... ok 8 | tests/actual_tests/mismatching_stdin.rs ... ok 9 | tests/actual_tests/no_stdin.rs ... ok 10 | 11 | test result: ok. 4 passed 12 | 13 | -------------------------------------------------------------------------------- /tests/integrations/cargo-run/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_bin" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dev-dependencies] 9 | ui_test = { path = "../../.."} 10 | tempfile = "3.3.0" 11 | 12 | [[test]] 13 | name = "ui_tests" 14 | harness = false 15 | -------------------------------------------------------------------------------- /tests/integrations/cargo-run/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let arg = std::env::args().nth(1).unwrap(); 3 | let input = std::fs::read_to_string(arg).unwrap(); 4 | let mut b = std::io::stdin().lines(); 5 | for (line, a ) in input.lines().enumerate() { 6 | let b = b.next().expect(&format!(".stdin file is missing line {}", line + 1)).unwrap(); 7 | assert_eq!(a, b); 8 | } 9 | for b in b { 10 | panic!("{}", b.unwrap()); 11 | } 12 | panic!("done"); 13 | } 14 | -------------------------------------------------------------------------------- /tests/integrations/cargo-run/tests/actual_tests/empty_no_stdin.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oli-obk/ui_test/597d4f562fd3749ade663b7cc3f1ff467b55695e/tests/integrations/cargo-run/tests/actual_tests/empty_no_stdin.rs -------------------------------------------------------------------------------- /tests/integrations/cargo-run/tests/actual_tests/empty_no_stdin.stderr: -------------------------------------------------------------------------------- 1 | thread 'main' panicked at src/main.rs:12:5: 2 | done 3 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 4 | -------------------------------------------------------------------------------- /tests/integrations/cargo-run/tests/actual_tests/matching_stdin.rs: -------------------------------------------------------------------------------- 1 | abc 2 | def 3 | -------------------------------------------------------------------------------- /tests/integrations/cargo-run/tests/actual_tests/matching_stdin.stderr: -------------------------------------------------------------------------------- 1 | thread 'main' panicked at src/main.rs:12:5: 2 | done 3 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 4 | -------------------------------------------------------------------------------- /tests/integrations/cargo-run/tests/actual_tests/matching_stdin.stdin: -------------------------------------------------------------------------------- 1 | abc 2 | def 3 | -------------------------------------------------------------------------------- /tests/integrations/cargo-run/tests/actual_tests/mismatching_stdin.rs: -------------------------------------------------------------------------------- 1 | cake 2 | lie 3 | -------------------------------------------------------------------------------- /tests/integrations/cargo-run/tests/actual_tests/mismatching_stdin.stderr: -------------------------------------------------------------------------------- 1 | thread 'main' panicked at src/main.rs:7:9: 2 | assertion `left == right` failed 3 | left: "cake" 4 | right: "asdfasdf" 5 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 6 | -------------------------------------------------------------------------------- /tests/integrations/cargo-run/tests/actual_tests/mismatching_stdin.stdin: -------------------------------------------------------------------------------- 1 | asdfasdf 2 | -------------------------------------------------------------------------------- /tests/integrations/cargo-run/tests/actual_tests/no_stdin.rs: -------------------------------------------------------------------------------- 1 | asdf 2 | uiop 3 | -------------------------------------------------------------------------------- /tests/integrations/cargo-run/tests/actual_tests/no_stdin.stderr: -------------------------------------------------------------------------------- 1 | thread 'main' panicked at src/main.rs:6:26: 2 | .stdin file is missing line 1 3 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 4 | -------------------------------------------------------------------------------- /tests/integrations/cargo-run/tests/ui_tests.rs: -------------------------------------------------------------------------------- 1 | use ui_test::{spanned::Spanned, *}; 2 | 3 | fn main() -> ui_test::color_eyre::Result<()> { 4 | let mut config = Config { 5 | output_conflict_handling: if std::env::var_os("BLESS").is_some() { 6 | ui_test::bless_output_files 7 | } else { 8 | ui_test::error_on_output_conflict 9 | }, 10 | bless_command: Some("cargo test".to_string()), 11 | ..Config::cargo("tests/actual_tests") 12 | }; 13 | config.comment_defaults.base().exit_status = Spanned::dummy(101).into(); 14 | config.comment_defaults.base().require_annotations = Spanned::dummy(false).into(); 15 | 16 | config.program.args = vec!["run".into(), "--quiet".into()]; 17 | config.program.input_file_flag = Some("--".into()); 18 | 19 | run_tests_generic( 20 | vec![config], 21 | default_file_filter, 22 | |_config, _content| {}, 23 | // Avoid github actions, as these would end up showing up in `Cargo.stderr` 24 | status_emitter::Text::verbose(), 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /tests/integrations/dep-fail/Cargo.stderr: -------------------------------------------------------------------------------- 1 | Error: tests failed 2 | 3 | Location: 4 | $DIR/src/lib.rs:LL:CC 5 | error: test failed, to rerun pass `--test ui` 6 | 7 | Caused by: 8 | process didn't exit successfully: `$DIR/target/ui/1/tests/integrations/dep-fail/debug/deps/ui-HASH` (exit status: 1) 9 | error: 1 target failed: 10 | `--test ui` 11 | -------------------------------------------------------------------------------- /tests/integrations/dep-fail/Cargo.stdout: -------------------------------------------------------------------------------- 1 | 2 | running 0 tests 3 | 4 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished 5 | 6 | Building dependencies ... FAILED 7 | tests/ui/basic_test.rs ... FAILED 8 | 9 | FAILED TEST: tests/ui/basic_test.rs 10 | command: "$CMD" "build" "--color=never" "--quiet" "--jobs" "1" "--target-dir" "$DIR/tests/integrations/dep-fail/target/ui/0" "--manifest-path" "tested_crate/Cargo.toml" "--message-format=json" 11 | 12 | full stderr: 13 | error: could not compile `tested_crate` (lib) due to 2 previous errors 14 | 15 | full stdout: 16 | $DIR/tests/integrations/dep-fail/tested_crate/src/lib.rs 17 | error: this file contains an unclosed delimiter 18 | --> src/lib.rs:LL:CC 19 | | 20 | 1 | pub trait A { 21 | | - ^ 22 | | | 23 | | unclosed delimiter 24 | 25 | $DIR/tests/integrations/dep-fail/tested_crate/src/lib.rs 26 | error: aborting due to 1 previous error 27 | 28 | BuildFinished { success: false } 29 | 30 | 31 | FAILURES: 32 | tests/ui/basic_test.rs 33 | 34 | test result: FAIL. 1 failed 35 | 36 | 37 | running 0 tests 38 | 39 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished 40 | 41 | -------------------------------------------------------------------------------- /tests/integrations/dep-fail/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "compile_test" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dev-dependencies] 7 | ui_test = { path = "../../.." } 8 | 9 | [[test]] 10 | name = "ui" 11 | harness = false 12 | -------------------------------------------------------------------------------- /tests/integrations/dep-fail/src/lib.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oli-obk/ui_test/597d4f562fd3749ade663b7cc3f1ff467b55695e/tests/integrations/dep-fail/src/lib.rs -------------------------------------------------------------------------------- /tests/integrations/dep-fail/tested_crate/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "tested_crate" 7 | version = "0.1.0-dev" 8 | -------------------------------------------------------------------------------- /tests/integrations/dep-fail/tested_crate/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tested_crate" 3 | version = "0.1.0-dev" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] -------------------------------------------------------------------------------- /tests/integrations/dep-fail/tested_crate/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub trait A { 2 | -------------------------------------------------------------------------------- /tests/integrations/dep-fail/tests/ui.rs: -------------------------------------------------------------------------------- 1 | use ui_test::{ 2 | default_file_filter, default_per_file_config, dependencies::DependencyBuilder, 3 | run_tests_generic, status_emitter::Text, Args, Config, 4 | }; 5 | 6 | fn main() -> ui_test::Result<()> { 7 | let mut config = Config { 8 | output_conflict_handling: ui_test::ignore_output_conflict, 9 | ..Config::rustc("tests/ui") 10 | }; 11 | config.comment_defaults.base().set_custom( 12 | "dependencies", 13 | DependencyBuilder { 14 | crate_manifest_path: "tested_crate/Cargo.toml".into(), 15 | ..DependencyBuilder::default() 16 | }, 17 | ); 18 | 19 | let args = Args::test()?; 20 | 21 | config.with_args(&args); 22 | 23 | run_tests_generic( 24 | [config].into(), 25 | default_file_filter, 26 | default_per_file_config, 27 | Text::verbose(), 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /tests/integrations/dep-fail/tests/ui/basic_test.rs: -------------------------------------------------------------------------------- 1 | use tested_crate::A; 2 | -------------------------------------------------------------------------------- /tests/integrations/ui_test_dep_bug/Cargo.stdout: -------------------------------------------------------------------------------- 1 | 2 | running 0 tests 3 | 4 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished 5 | 6 | Building dependencies ... ok 7 | tests/ui/basic_test.rs ... ok 8 | 9 | test result: ok. 1 passed 10 | 11 | 12 | running 0 tests 13 | 14 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished 15 | 16 | -------------------------------------------------------------------------------- /tests/integrations/ui_test_dep_bug/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "compile_test" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | # This fails 10 | tested_crate = { path = "tested_crate" } 11 | 12 | # This works 13 | #tested_crate = { path = "../tested_crate", version = "0.1.0-dev" } 14 | 15 | [dev-dependencies] 16 | ui_test = { path = "../../.." } 17 | 18 | [[test]] 19 | name = "ui" 20 | harness = false 21 | -------------------------------------------------------------------------------- /tests/integrations/ui_test_dep_bug/src/lib.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oli-obk/ui_test/597d4f562fd3749ade663b7cc3f1ff467b55695e/tests/integrations/ui_test_dep_bug/src/lib.rs -------------------------------------------------------------------------------- /tests/integrations/ui_test_dep_bug/tested_crate/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tested_crate" 3 | version = "0.1.0-dev" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] -------------------------------------------------------------------------------- /tests/integrations/ui_test_dep_bug/tested_crate/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub trait A {} 2 | pub trait B: A {} 3 | -------------------------------------------------------------------------------- /tests/integrations/ui_test_dep_bug/tests/ui.rs: -------------------------------------------------------------------------------- 1 | use ui_test::{ 2 | default_file_filter, default_per_file_config, dependencies::DependencyBuilder, 3 | run_tests_generic, status_emitter::Text, Args, Config, 4 | }; 5 | 6 | fn main() -> ui_test::Result<()> { 7 | let mut config = Config { 8 | output_conflict_handling: ui_test::ignore_output_conflict, 9 | ..Config::rustc("tests/ui") 10 | }; 11 | config 12 | .comment_defaults 13 | .base() 14 | .set_custom("dependencies", DependencyBuilder::default()); 15 | 16 | let args = Args::test()?; 17 | 18 | config.with_args(&args); 19 | 20 | run_tests_generic( 21 | [config].into(), 22 | default_file_filter, 23 | default_per_file_config, 24 | Text::verbose(), 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /tests/integrations/ui_test_dep_bug/tests/ui/basic_test.rs: -------------------------------------------------------------------------------- 1 | use tested_crate::B; 2 | 3 | struct DoesntImplementA; 4 | 5 | impl B for DoesntImplementA {} 6 | //~^ E0277 --------------------------------------------------------------------------------