datatest-stable-0.3.3/.cargo/config.toml000064400000000000000000000001261046102023000162220ustar 00000000000000[alias] xfmt = "fmt -- --config imports_granularity=Crate --config group_imports=One" datatest-stable-0.3.3/.cargo_vcs_info.json0000644000000001360000000000100141200ustar { "git": { "sha1": "9234df379529ba2c25271973494a2e9b1f082d0d" }, "path_in_vcs": "" }datatest-stable-0.3.3/.github/codecov.yml000064400000000000000000000001711046102023000164140ustar 00000000000000coverage: status: project: default: informational: true patch: default: target: 0% datatest-stable-0.3.3/.github/renovate.json000064400000000000000000000000621046102023000167640ustar 00000000000000{ "extends": ["github>nextest-rs/renovate"] } datatest-stable-0.3.3/.github/workflows/ci.yml000064400000000000000000000060521046102023000174260ustar 00000000000000on: push: branches: - main pull_request: branches: - main name: CI env: RUSTFLAGS: -D warnings CARGO_TERM_COLOR: always jobs: lint: name: Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - uses: dtolnay/rust-toolchain@stable with: components: rustfmt, clippy - uses: Swatinem/rust-cache@720f7e45ccee46c12a7b1d7bed2ab733be9be5a1 # v2 - name: Lint (clippy) run: cargo clippy --all-features --all-targets - name: Lint (rustfmt) run: cargo xfmt --check - name: Install cargo-sync-rdme and just uses: taiki-e/install-action@5ab30948b991e8d6aa5a6c1e33c6aea130c6de65 # v2 with: tool: cargo-sync-rdme,just - name: Install nightly toolchain for cargo-sync-rdme uses: dtolnay/rust-toolchain@nightly - name: Generate readmes run: just generate-readmes - name: Check for differences run: git diff --exit-code build-rustdoc: name: Build documentation runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest] fail-fast: false steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - uses: dtolnay/rust-toolchain@stable with: components: rustfmt, clippy - uses: Swatinem/rust-cache@720f7e45ccee46c12a7b1d7bed2ab733be9be5a1 # v2 - name: Build rustdoc run: cargo doc --all-features build: name: Build and test runs-on: ${{ matrix.os }} strategy: matrix: os: - ubuntu-latest - windows-latest rust-version: ["1.72", stable] fail-fast: false steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust-version }} - uses: Swatinem/rust-cache@720f7e45ccee46c12a7b1d7bed2ab733be9be5a1 # v2 with: key: ${{ matrix.rust-version }} - name: Install tools uses: taiki-e/install-action@5ab30948b991e8d6aa5a6c1e33c6aea130c6de65 # v2 with: tool: cargo-hack,just,nextest - name: Build run: just powerset build - name: Build with all targets run: just powerset build --all-targets - name: Run tests run: just powerset nextest run - name: Run tests with cargo test run: just powerset test # Remove Cargo.lock to ensure that building with the latest versions works on stable. - name: Remove Cargo.lock and rebuild on stable if: matrix.rust-version == 'stable' run: rm Cargo.lock && cargo build - name: Build with all targets if: matrix.rust-version == 'stable' run: just powerset build --all-targets - name: Run tests on stable if: matrix.rust-version == 'stable' run: just powerset nextest run - name: Run tests with cargo test on stable if: matrix.rust-version == 'stable' run: just powerset test datatest-stable-0.3.3/.github/workflows/coverage.yml000064400000000000000000000022241046102023000206230ustar 00000000000000on: push: branches: - main pull_request: branches: - main name: Test coverage jobs: coverage: name: Collect test coverage runs-on: ubuntu-latest # nightly rust might break from time to time continue-on-error: true env: RUSTFLAGS: -D warnings CARGO_TERM_COLOR: always steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 # Nightly Rust is used for cargo llvm-cov --doc below. - uses: dtolnay/rust-toolchain@nightly with: components: llvm-tools-preview - uses: Swatinem/rust-cache@720f7e45ccee46c12a7b1d7bed2ab733be9be5a1 # v2 - name: Install tools uses: taiki-e/install-action@5ab30948b991e8d6aa5a6c1e33c6aea130c6de65 # v2 with: tool: cargo-llvm-cov,just,nextest - name: Collect coverage data run: | just coverage --lcov --output-path lcov.info - name: Upload coverage data to codecov uses: codecov/codecov-action@1e68e06f1dbfde0e4cefc87efeba9e4643565303 # v5 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: files: lcov.info datatest-stable-0.3.3/.github/workflows/docs.yml000064400000000000000000000016271046102023000177660ustar 00000000000000on: push: branches: - main name: Docs jobs: docs: name: Build and deploy documentation concurrency: ci-${{ github.ref }} runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - uses: dtolnay/rust-toolchain@stable - uses: taiki-e/install-action@just - name: Build rustdoc run: just rustdoc - name: Organize run: | rm -rf target/gh-pages mkdir target/gh-pages mv target/doc/_redirects target/gh-pages mv target/doc target/gh-pages/rustdoc - name: Publish uses: cloudflare/pages-action@1 with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} projectName: datatest-stable directory: target/gh-pages gitHubToken: ${{ secrets.GITHUB_TOKEN }} datatest-stable-0.3.3/.github/workflows/release.yml000064400000000000000000000021471046102023000204540ustar 00000000000000# adapted from https://github.com/taiki-e/cargo-hack/blob/main/.github/workflows/release.yml name: Publish releases to GitHub on: push: tags: - "*" jobs: datatest-stable-release: if: github.repository_owner == 'nextest-rs' && startsWith(github.ref_name, 'datatest-stable-') runs-on: ubuntu-latest steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: persist-credentials: false - name: Install Rust uses: dtolnay/rust-toolchain@stable - name: Install cargo release uses: taiki-e/install-action@5ab30948b991e8d6aa5a6c1e33c6aea130c6de65 # v2 with: tool: cargo-release@0.25.0 - uses: taiki-e/create-gh-release-action@26b80501670402f1999aff4b934e1574ef2d3705 # v1 with: prefix: datatest-stable changelog: CHANGELOG.md title: $prefix $version branch: main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: ./scripts/cargo-release-publish.sh env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} datatest-stable-0.3.3/.gitignore000064400000000000000000000000101046102023000146670ustar 00000000000000/target datatest-stable-0.3.3/CHANGELOG.md000064400000000000000000000155671046102023000145370ustar 00000000000000# Changelog ## [0.3.3] - 2025-09-29 ### Fixed Replaced obsolete `doc_auto_cfg` with `doc_cfg`, to fix Rust nightly builds with the `doc_cfg` flag enabled. ## [0.3.2] - 2024-12-28 ### Added - `pattern` is now optional in the `harness!` macro. If not specified, the default pattern is `r".*"` (match all files). ### Fixed - Restored the ability to use absolute paths as the `root` argument. ## [0.3.1] - 2024-12-25 ### Fixed Fixed documentation for `include_dir` invocations. They must typically be called via `include_dir!("$CARGO_MANIFEST_DIR/path/to/data")`. ## [0.3.0] - 2024-12-24 ### Added - Support for embedding data into the test binary via an optional `include-dir` feature. For more information and recommendations for when to use this, see [the readme](https://crates.io/crates/datatest-stable). ### Changed - The macro call format has changed to: ```rust datatest_stable::harness! { { test = fn_name, path = &include_dir!("path/to/data"), pattern = r"^.*$" }, // ... } ``` This is both a nicer format for expressing multiple tests, and a signal to indicate the other breaking changes in this release. - Regex patterns now match against the path relative to the *include directory*, rather than paths with respect to the crate root. This change was made for uniformity with the `include_dir` implementation. - On Windows, paths are now universally normalized to use forward slashes. This change was made to ensure that test names and paths are consistent across platforms. ## [0.2.10] - 2024-12-08 - Internal dependency updates: update `libtest-mimic` to 0.8.1, and `fancy-regex` to 0.14.0. - Update MSRV to Rust 1.72. ## [0.2.9] - 2024-04-25 ### Added Previously, the test functions supported were `fn(&Path) -> Result<()>` and `fn(&Utf8Path) -> Result<()>`. This release adds additional supported functions: - `fn(&P, String) -> datatest_stable::Result<()>` where `P` is `Path` or `Utf8Path`. If the extra `String` parameter is specified, the contents of the file will be loaded and passed in as a string (erroring out if that failed). - `fn(&P, Vec) -> datatest_stable::Result<()>` where `P` is `Path` or `Utf8Path`. If the extra `Vec` parameter is specified, the contents of the file will be loaded and passed in as a `Vec` (erroring out if that failed). ## [0.2.8] - 2024-04-24 ### Fixed - Fixed quadratic performance issue with nextest, where datatest-stable would iterate over the entire list of files for each test. Thanks [@zaneduffield](https://github.com/zaneduffield) for your first contribution! ## [0.2.7] - 2024-04-21 ### Changed - Switched to the `fancy-regex` crate, which allows for matching against regexes with lookahead/behind and backreferences. Thanks [@webbdays](https://github.com/webbdays) for your first contribution! - MSRV updated to Rust 1.66. ## [0.2.6] - 2024-04-09 - Update to `libtest-mimic 0.7.2`, and use the upstream implementation of `ExitCode`. ## [0.2.5] - 2024-04-08 - Exit main via `ExitCode` rather than `std::process::exit()`. This appears to fix coverage on Windows. ## [0.2.4] - 2024-04-08 This is a periodic maintenance release. - Update internal dependency versions, including libtest-mimic to 0.7.0. - Update "docs (main)" link to the new location at [https://datatest-stable.nexte.st](https://datatest-stable.nexte.st). - Update MSRV to Rust 1.65. ## [0.2.3] - 2023-08-29 Updated README. ## [0.2.2] - 2023-08-29 ### Added - Restored compatibility with `fn(&Path) -> Result<()>`. The harness now can take either `fn(&Path) -> Result<()>` or `fn(&Utf8Path) -> Result<()>`. ## [0.2.1] - 2023-08-29 ### Changed - The test signature is now `fn(&`[`Utf8Path`]`)` rather than `fn(&Path)`. If necessary, a `Utf8Path` can be converted to a `&Path` with [`.as_ref()`] or [`.as_std_path()`]. - Non-Unicode paths now consistently produce errors. Previously, the treatment of such paths was inconsistent -- they would either be skipped or produce errors. - Internal dependency update: libtest-mimic updated to version 0.6.1. - MSRV updated to Rust 1.60. [`Utf8Path`]: https://docs.rs/camino/latest/camino/struct.Utf8Path.html [`.as_ref()`]: https://docs.rs/camino/latest/camino/struct.Utf8Path.html#impl-AsRef%3COsStr%3E-for-Utf8Path [`.as_std_path()`]: https://docs.rs/camino/latest/camino/struct.Utf8Path.html#method.as_std_path ## [0.2.0] - 2023-08-29 This version had a publishing issue. ## [0.1.3] - 2022-08-15 ### Changed - Errors are now displayed with the `Debug` implementation, which prints out the full error chain with libraries like `anyhow` or `eyre`, rather than the `Display` implementation. Thanks [Alex Badics] for your first contribution! - MSRV updated to Rust 1.58. ### Internal improvements - datatest-stable now uses libtest-mimic 0.5.2. Thanks [Lukas Kalbertodt] (maintainer of libtest-mimic) for your first contribution! [Alex Badics]: https://github.com/badicsalex [Lukas]: https://github.com/LukasKalbertodt ## [0.1.2] - 2022-05-22 ### Changed - New internal implementation, based on top of [libtest-mimic](https://github.com/LukasKalbertodt/libtest-mimic). - Repository updated to [nextest-rs/datatest-stable](https://github.com/nextest-rs/datatest-stable). - MSRV updated to Rust 1.56. There are no functional changes in this release. ## [0.1.1] - 2021-04-16 ### Added - Initial release with basic support for data-driven tests. (Version 0.1.0 was yanked because of a metadata issue.) [0.3.3]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.3.3 [0.3.2]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.3.2 [0.3.1]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.3.1 [0.3.0]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.3.0 [0.2.10]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.10 [0.2.9]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.9 [0.2.8]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.8 [0.2.7]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.7 [0.2.6]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.6 [0.2.5]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.5 [0.2.4]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.4 [0.2.3]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.3 [0.2.2]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.2 [0.2.1]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.1 [0.1.3]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.1.3 [0.1.2]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.1.2 [0.1.1]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.1.1 datatest-stable-0.3.3/CONTRIBUTING.md000064400000000000000000000046201046102023000151430ustar 00000000000000## Prerequisites datatest-stable is designed to work with both `cargo test` and `cargo nextest`. In either case, nextest is required for one of the tests. Install it via [the instructions here](https://get.nexte.st). ## Pull Requests If you have a new feature in mind, please discuss the feature in an issue to ensure that your contributions will be accepted. 1. Fork the repo and create your branch from `main`. 2. If you've added code that should be tested, add tests. 3. If you've changed APIs, update the documentation. 4. Ensure the test suite passes with `cargo nextest run --all-features`. 5. Run `cargo xfmt` to automatically format your changes (CI will let you know if you missed this). ## Logically Separate Commits As far as possible, please try and make commits [atomic](https://en.wikipedia.org/wiki/Atomic_commit#Atomic_commit_convention) and logically separate. We understand that GitHub's pull request model doesn't work well with "stacked diffs", so if your changes are complex, then a single PR with a series of commits is preferred. ## Bisectable History It is important that the project history is bisectable, so that when regressions are identified we can easily use `git bisect` to be able to pinpoint the exact commit which introduced the regression. We'll land your commits with: - "Rebase and merge" if your commits are atomic and each commit passes CI. - "Squash and merge" if they are not. ## Maintainers Editing Commits For efficiency reasons, maintainers may choose to edit your commits before landing them. The commits will still be credited to you, and the edits will be limited to reasonable changes that are in the spirit of the PR. (Think of the changes that the maintainers would have done anyway.) To make this easier, please check the box that allows maintainers to edit your branch: ![Screenshot of GitHub new pull request page, showing "Allow edits and access to secrets by maintainers" checked](https://github.com/nextest-rs/quick-junit/assets/180618/9f4074fa-4f52-4735-af19-144464f0fb8d) If maintainers need to make changes and that box is checked, then your PR can be marked as "merged" in the web UI. Otherwise, it will be marked as "closed". ## License By contributing to `datatest-stable`, you agree that your contributions will be dual-licensed under the terms of the [`LICENSE-MIT`](LICENSE-MIT) and [`LICENSE-APACHE`](LICENSE-APACHE) files in the root directory of this source tree. datatest-stable-0.3.3/Cargo.lock0000644000000505660000000000100121070ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anstream" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon 1.0.2", "colorchoice", "is-terminal", "utf8parse", ] [[package]] name = "anstream" version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon 3.0.6", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ "windows-sys 0.48.0", ] [[package]] name = "anstyle-wincon" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" dependencies = [ "anstyle", "windows-sys 0.48.0", ] [[package]] name = "anstyle-wincon" version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", "windows-sys 0.59.0", ] [[package]] name = "bit-set" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "camino" version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" [[package]] name = "camino-tempfile" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb905055fa81e4d427f919b2cd0d76a998267de7d225ea767a1894743a5263c2" dependencies = [ "camino", "tempfile", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" version = "4.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb690e81c7840c0d7aade59f242ea3b41b9bc27bcd5997890e7702ae4b32e487" dependencies = [ "clap_builder", "clap_derive", "once_cell", ] [[package]] name = "clap_builder" version = "4.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ed2e96bc16d8d740f6f48d663eddf4b8a0983e79210fd55479b7bcd0a69860e" dependencies = [ "anstream 0.3.2", "anstyle", "clap_lex", "strsim", ] [[package]] name = "clap_derive" version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[package]] name = "clap_lex" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "datatest-stable" version = "0.3.3" dependencies = [ "camino", "camino-tempfile", "fancy-regex", "fs_extra", "include_dir", "libtest-mimic", "trybuild", "walkdir", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "escape8259" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba4f4911e3666fcd7826997b4745c8224295a6f3072f1418c3067b97a67557ee" dependencies = [ "rustversion", ] [[package]] name = "fancy-regex" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298" dependencies = [ "bit-set", "regex-automata", "regex-syntax", ] [[package]] name = "fastrand" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "fs_extra" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "include_dir" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" dependencies = [ "include_dir_macros", ] [[package]] name = "include_dir_macros" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" dependencies = [ "proc-macro2", "quote", ] [[package]] name = "indexmap" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "is-terminal" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ "hermit-abi", "libc", "windows-sys 0.52.0", ] [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libtest-mimic" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5297962ef19edda4ce33aaa484386e0a5b3d7f2f4e037cbeee00503ef6b29d33" dependencies = [ "anstream 0.6.18", "anstyle", "clap", "escape8259", ] [[package]] name = "linux-raw-sys" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "memchr" version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "proc-macro2" version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "regex-automata" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustix" version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.52.0", ] [[package]] name = "rustversion" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "serde" version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" dependencies = [ "itoa", "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" dependencies = [ "serde", ] [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" version = "2.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "target-triple" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" [[package]] name = "tempfile" version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", "rustix", "windows-sys 0.52.0", ] [[package]] name = "termcolor" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "toml" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "toml_parser", "toml_writer", "winnow", ] [[package]] name = "toml_datetime" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" dependencies = [ "serde", ] [[package]] name = "toml_parser" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" dependencies = [ "winnow", ] [[package]] name = "toml_writer" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" [[package]] name = "trybuild" version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ded9fdb81f30a5708920310bfcd9ea7482ff9cba5f54601f7a19a877d5c2392" dependencies = [ "glob", "serde", "serde_derive", "serde_json", "target-triple", "termcolor", "toml", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" datatest-stable-0.3.3/Cargo.toml0000644000000037170000000000100121260ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.72" name = "datatest-stable" version = "0.3.3" build = false publish = true autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Data-driven tests that work on stable Rust" readme = "README.md" keywords = [ "datatest", "data-driven-tests", "test-harness", ] categories = ["development-tools::testing"] license = "MIT OR Apache-2.0" repository = "https://github.com/nextest-rs/datatest-stable" [package.metadata.cargo-sync-rdme.badge.badges] maintenance = true license = true crates-io = true docs-rs = true rust-version = true [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg=doc_cfg"] [badges.maintenance] status = "passively-maintained" [features] include-dir = ["dep:include_dir"] [lib] name = "datatest_stable" path = "src/lib.rs" [[test]] name = "example" path = "tests/example.rs" harness = false [[test]] name = "integration" path = "tests/integration.rs" harness = true [dependencies.camino] version = "1.1.9" [dependencies.fancy-regex] version = "0.14.0" [dependencies.include_dir] version = "0.7.4" optional = true [dependencies.libtest-mimic] version = "0.8.1" [dependencies.walkdir] version = "2.5.0" [dev-dependencies.trybuild] version = "1.0.111" [target."cfg(unix)".dev-dependencies.camino-tempfile] version = "1.1.1" [target."cfg(unix)".dev-dependencies.fs_extra] version = "1.3.0" [lints.rust.unexpected_cfgs] level = "warn" priority = 0 check-cfg = ["cfg(doc_cfg)"] datatest-stable-0.3.3/Cargo.toml.orig000064400000000000000000000021771046102023000156060ustar 00000000000000[package] name = "datatest-stable" version = "0.3.3" description = "Data-driven tests that work on stable Rust" repository = "https://github.com/nextest-rs/datatest-stable" license = "MIT OR Apache-2.0" publish = true readme = "README.md" edition = "2021" categories = ["development-tools::testing"] keywords = ["datatest", "data-driven-tests", "test-harness"] rust-version = "1.72" [badges] maintenance = { status = "passively-maintained" } [package.metadata.cargo-sync-rdme.badge.badges] maintenance = true license = true crates-io = true docs-rs = true rust-version = true [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg=doc_cfg"] [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(doc_cfg)'] } [dependencies] camino = "1.1.9" fancy-regex = "0.14.0" include_dir = { version = "0.7.4", optional = true } libtest-mimic = "0.8.1" walkdir = "2.5.0" [dev-dependencies] trybuild = "1.0.111" [target.'cfg(unix)'.dev-dependencies] camino-tempfile = "1.1.1" fs_extra = "1.3.0" [[test]] name = "example" harness = false [[test]] name = "integration" harness = true [features] include-dir = ["dep:include_dir"] datatest-stable-0.3.3/Justfile000064400000000000000000000016641046102023000144270ustar 00000000000000# Print a help message. help: just --list # Run `cargo hack --feature-powerset` with the given arguments. powerset *args: cargo hack --feature-powerset {{args}} # Build docs for crates and direct dependencies rustdoc: @cargo tree --depth 1 -e normal --prefix none --workspace \ | gawk '{ gsub(" v", "@", $0); printf("%s\n", $1); }' \ | xargs printf -- '-p %s\n' \ | RUSTC_BOOTSTRAP=1 RUSTDOCFLAGS='--cfg=doc_cfg' xargs cargo doc --no-deps --lib --all-features echo "/ /rustdoc/datatest_stable/ 301" > target/doc/_redirects # Generate README.md files using `cargo-sync-rdme`. generate-readmes: cargo sync-rdme --toolchain nightly --all-features # Collect coverage, pass in `--html` to get an HTML report coverage *args: cargo +nightly llvm-cov --no-report nextest --all-features cargo +nightly llvm-cov --no-report --doc --all-features cargo +nightly llvm-cov report --doctests {{args}} datatest-stable-0.3.3/LICENSE-APACHE000064400000000000000000000251371046102023000146440ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. datatest-stable-0.3.3/LICENSE-MIT000064400000000000000000000017771046102023000143600ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. datatest-stable-0.3.3/README.md000064400000000000000000000220231046102023000141660ustar 00000000000000 # datatest-stable [![Maintenance: passively-maintained](https://img.shields.io/badge/maintenance-passively--maintained-yellowgreen.svg?)](https://doc.rust-lang.org/cargo/reference/manifest.html#the-badges-section) ![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/datatest-stable.svg?) [![crates.io](https://img.shields.io/crates/v/datatest-stable.svg?logo=rust)](https://crates.io/crates/datatest-stable) [![docs.rs](https://img.shields.io/docsrs/datatest-stable.svg?logo=docs.rs)](https://docs.rs/datatest-stable) [![Rust: ^1.72.0](https://img.shields.io/badge/rust-^1.72.0-93450a.svg?logo=rust)](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field) `datatest-stable` is a test harness intended to write *file-driven* or *data-driven* tests, where individual test case fixtures are specified as files and not as code. Given: * a test `my_test` that accepts a path, and optionally the contents as input * a directory to look for files (test fixtures) in * a pattern to match files on `datatest-stable` will call the `my_test` function once per matching file in the directory. Directory traversals are recursive. `datatest-stable` works with [cargo nextest](https://nexte.st/), and is part of the [nextest-rs organization](https://github.com/nextest-rs/) on GitHub. With nextest, each test case is represented as a separate test, and is run as a separate process in parallel. ## Usage 1. Configure the test target by setting `harness = false` in `Cargo.toml`: ````toml [[test]] name = "" harness = false ```` 2. Call the `datatest_stable::harness!` macro as: ````rust,ignore datatest_stable::harness! { { test = my_test, root = "path/to/fixtures", pattern = r".*" }, } ```` * `test` - The test function to be executed on each matching input. This function can be one of: * `fn(&Path) -> datatest_stable::Result<()>` * `fn(&Utf8Path) -> datatest_stable::Result<()>` ([`Utf8Path`](https://docs.rs/camino/1.1.9/camino/struct.Utf8Path.html) is part of the [`camino`](https://docs.rs/camino/1.1.9/camino/index.html) library, and is re-exported here for convenience.) * `fn(&P, String) -> datatest_stable::Result<()>` where `P` is `Path` or `Utf8Path`. If the extra `String` parameter is specified, the contents of the file will be loaded and passed in as a string (erroring out if that failed). * `fn(&P, Vec) -> datatest_stable::Result<()>` where `P` is `Path` or `Utf8Path`. If the extra `Vec` parameter is specified, the contents of the file will be loaded and passed in as a `Vec` (erroring out if that failed). * `root` - The path to the root directory where the input files (fixtures) live. Relative paths are resolved relative to the crate root (the directory where the crate’s `Cargo.toml` is located). `root` is an arbitrary expression that implements [`Display`](https://doc.rust-lang.org/nightly/core/fmt/trait.Display.html), such as `&str`, or a function call that returns a [`Utf8PathBuf`](https://docs.rs/camino/1.1.9/camino/struct.Utf8PathBuf.html). * `pattern` - a regex used to match against and select each file to be tested. Extended regexes with lookaround and backtracking are supported via the [`fancy_regex`](https://docs.rs/fancy-regex/0.14.0/fancy_regex/index.html) crate. `pattern` is an arbitrary expression that implements [`Display`](https://doc.rust-lang.org/nightly/core/fmt/trait.Display.html), such as `&str`, or a function call that returns a `String`. `pattern` is optional, and defaults to `r".*"` (match all files). The three parameters can be repeated if you have multiple sets of data-driven tests to be run: ````rust,ignore datatest_stable::harness! { { test = testfn1, root = root1, pattern = pattern1 }, { test = testfn2, root = root2 }, } ```` Trailing commas are optional. ### Relative and absolute paths The `pattern` argument is tested against the **relative** path of each file, **excluding** the `root` prefix – not the absolute path. The `Path` and `Utf8Path` passed into the test functions are created by joining `root` to the relative path of the file. * If `root` is **relative**, the paths passed in are relative to the crate root. * If `root` is **absolute**, the paths passed in are absolute. For uniformity, all relative paths use `/` as the path separator, including on Windows, and all absolute paths use the platform’s native separator throughout. ### Examples This is an example test. Use it with `harness = false`. ````rust use datatest_stable::Utf8Path; use std::path::Path; fn my_test(path: &Path) -> datatest_stable::Result<()> { // ... write test here Ok(()) } fn my_test_utf8(path: &Utf8Path, contents: String) -> datatest_stable::Result<()> { // ... write test here Ok(()) } datatest_stable::harness! { { test = my_test, root = "path/to/fixtures" }, { test = my_test_utf8, root = "path/to/fixtures", pattern = r"^.*\.txt$", }, } ```` If `path/to/fixtures` contains a file `foo/bar.txt`, then: * The pattern `r"^.*/*"` will match `foo/bar.txt`. * `my_test` and `my_test_utf8` will be called with `"path/to/fixtures/foo/bar.txt"`. ### Embedding directories at compile time With the `include-dir` feature enabled, you can use the [`include_dir`](https://docs.rs/include_dir) crate’s [`include_dir!`](https://docs.rs/include_dir_macros/0.7.4/include_dir_macros/macro.include_dir.html) macro. This allows you to embed directories into the binary at compile time. This is generally not recommended for rapidly-changing test data, since each change will force a rebuild. But it can be useful for relatively-unchanging data suites distributed separately, e.g. on crates.io. With the `include-dir` feature enabled, you can use: ````rust // The `include_dir!` macro is re-exported for convenience. use datatest_stable::include_dir; use std::path::Path; fn my_test(path: &Path, contents: Vec) -> datatest_stable::Result<()> { // ... write test here Ok(()) } datatest_stable::harness! { { test = my_test, root = include_dir!("tests/files"), pattern = r"^.*\.json$" }, } ```` You can also use directories published as `static` items in upstream crates: ````rust use datatest_stable::{include_dir, Utf8Path}; // In the upstream crate: pub static FIXTURES: include_dir::Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/tests/files"); // In your test: fn my_test(path: &Utf8Path, contents: String) -> datatest_stable::Result<()> { // ... write test here Ok(()) } datatest_stable::harness! { { test = my_test, root = &FIXTURES }, } ```` In this case, the passed-in `Path` and `Utf8Path` are always **relative** to the root of the included directory. Like elsewhere in `datatest-stable`, these relative paths always use forward slashes as separators, including on Windows. Because the files don’t exist on disk, the test functions must accept their contents as either a `String` or a `Vec`. If the argument is not provided, the harness will panic at runtime. ### Conditionally embedding directories It is also possible to conditionally include directories at compile time via a feature flag. For example, you might have an internal-only `testing` feature that you turn on locally, but users don’t on crates.io. In that case, you can use: ````rust use datatest_stable::Utf8Path; // In the library itself: pub mod fixtures { #[cfg(feature = "testing")] pub static FIXTURES: &str = "tests/files"; #[cfg(not(feature = "testing"))] pub static FIXTURES: include_dir::Dir<'static> = include_dir::include_dir!("$CARGO_MANIFEST_DIR/tests/files"); } // In the test: fn my_test(path: &Utf8Path, contents: String) -> datatest_stable::Result<()> { // ... write test here Ok(()) } datatest_stable::harness! { { test = my_test, root = &fixtures::FIXTURES, pattern = r"^inputs/.*$" }, } ```` In this case, note that `path` will be relative to the **crate directory** (e.g. `tests/files/foo/bar.txt`) if `FIXTURES` is a string, and relative to the **include directory** (e.g. `foo/bar.txt`) if `FIXTURES` is a [`Dir`](https://docs.rs/include_dir/0.7.4/include_dir/dir/struct.Dir.html). Your test should be prepared to handle either case. ## Features * `include-dir`: Enables the `include_dir!` macro, which allows embedding directories at compile time. This feature is disabled by default. ## Minimum supported Rust version (MSRV) The minimum supported Rust version is **Rust 1.72**. MSRV bumps may be accompanied by a minor version update; at any time, Rust versions from at least the last 6 months are supported. ## See also * [`datatest`](https://crates.io/crates/datatest): the original inspiration for this crate, with more features but targeting nightly Rust. * [Data-driven testing](https://en.wikipedia.org/wiki/Data-driven_testing) ## License This project is available under the terms of either the [Apache 2.0 license](LICENSE-APACHE) or the [MIT license](LICENSE-MIT). datatest-stable-0.3.3/release.toml000064400000000000000000000004431046102023000152260ustar 00000000000000sign-tag = true # Required for templates below to work consolidate-commits = false pre-release-commit-message = "[{{crate_name}}] version {{version}}" tag-message = "[{{crate_name}}] version {{version}}" tag-name = "datatest-stable-{{version}}" publish = false dependent-version = "upgrade" datatest-stable-0.3.3/scripts/cargo-release-publish.sh000075500000000000000000000007361046102023000211210ustar 00000000000000#!/bin/bash # Use cargo-release to publish crates to crates.io. set -xe -o pipefail # cargo-release requires a release off a branch (maybe it shouldn't?) # Check out this branch, creating it if it doesn't exist. git checkout -B to-release # --execute: actually does the release # --no-confirm: don't ask for confirmation, since this is a non-interactive script cargo release publish --publish --execute --no-confirm --workspace "$@" git checkout - git branch -D to-release datatest-stable-0.3.3/scripts/fix-readmes.awk000064400000000000000000000010571046102023000173120ustar 00000000000000# Fix up readmes: # * Replace ## with # in code blocks. # * Remove [] without a following () from output. BEGIN { true = 1 false = 0 in_block = false } { if (!in_block && $0 ~ /^```/) { in_block = true } else if (in_block && $0 ~ /^```$/) { in_block = false } if (in_block) { sub(/## /, "# ") print $0 } else { # Strip [] without a () that immediately follows them from # the output. subbed = gensub(/\[([^\[]+)]([^\(]|$)/, "\\1\\2", "g") print subbed } } datatest-stable-0.3.3/src/data_source.rs000064400000000000000000000333571046102023000163510ustar 00000000000000// Copyright (c) The datatest-stable Contributors // SPDX-License-Identifier: MIT OR Apache-2.0 use camino::{Utf8Component, Utf8Path, Utf8PathBuf}; #[derive(Debug)] #[doc(hidden)] pub enum DataSource { // The path has had normalize_slashes applied to it. Directory(Utf8PathBuf), #[cfg(feature = "include-dir")] IncludeDir(std::borrow::Cow<'static, include_dir::Dir<'static>>), } impl DataSource { /// Iterates over all files in the data source. /// /// This returns entries that have just been discovered, so they're expected /// to exist. pub(crate) fn walk_files(&self) -> Box> + '_> { match self { DataSource::Directory(path) => Box::new(iter_directory(path)), #[cfg(feature = "include-dir")] DataSource::IncludeDir(dir) => Box::new(iter_include_dir(dir)), } } /// Finds a test path from the filter provided. /// /// The path might or might not exist -- the caller should call `.exists()` /// to ensure it does. /// /// Used for `--exact` matches. pub(crate) fn derive_exact(&self, filter: &str, test_name: &str) -> Option { // include_dir 0.7.4 returns paths with forward slashes, including on // Windows. But that isn't part of the stable API it seems, so we call // `rel_path_to_forward_slashes` anyway. let rel_path = rel_path_to_forward_slashes( filter.strip_prefix(test_name)?.strip_prefix("::")?.as_ref(), ); match self { DataSource::Directory(path) => Some(TestEntry { source: TestSource::Path(normalize_slashes(&path.join(&rel_path))), rel_path, }), #[cfg(feature = "include-dir")] DataSource::IncludeDir(dir) => { let file = dir.get_file(&rel_path)?; Some(TestEntry { source: TestSource::IncludeDir(file), rel_path, }) } } } /// Returns true if data is not available on disk and must be provided from /// an in-memory buffer. pub(crate) fn is_in_memory(&self) -> bool { match self { DataSource::Directory(_) => false, #[cfg(feature = "include-dir")] DataSource::IncludeDir(_) => true, } } pub(crate) fn display(&self) -> String { match self { DataSource::Directory(path) => format!("directory: `{path}`"), #[cfg(feature = "include-dir")] DataSource::IncludeDir(_) => "included directory".to_string(), } } } fn iter_directory(root: &Utf8Path) -> impl Iterator> + '_ { walkdir::WalkDir::new(root) .into_iter() .filter(|res| { // Continue to bubble up all errors to the parent. res.as_ref().map_or(true, |entry| { entry.file_type().is_file() && entry .file_name() .to_str() .is_some_and(|s| !s.starts_with('.')) // Skip hidden files }) }) .map(move |res| match res { Ok(entry) => { let path = Utf8PathBuf::try_from(entry.into_path()) .map_err(|error| error.into_io_error())?; Ok(TestEntry::from_full_path(root, path)) } Err(error) => Err(error.into()), }) } #[cfg(feature = "include-dir")] fn iter_include_dir<'a>( dir: &'a include_dir::Dir<'static>, ) -> impl Iterator> + 'a { // Need to maintain a stack to do a depth-first traversal. struct IncludeDirIter<'a> { stack: Vec<&'a include_dir::DirEntry<'a>>, } impl<'a> Iterator for IncludeDirIter<'a> { type Item = &'a include_dir::File<'a>; fn next(&mut self) -> Option { while let Some(entry) = self.stack.pop() { match entry { include_dir::DirEntry::File(file) => { return Some(file); } include_dir::DirEntry::Dir(dir) => { self.stack.extend(dir.entries()); } } } None } } IncludeDirIter { stack: dir.entries().iter().collect(), } .map(|file| { // include_dir 0.7.4 returns paths with forward slashes, including on // Windows. But that isn't part of the stable API it seems, so we call // `rel_path_to_forward_slashes` anyway. let rel_path = match file.path().try_into() { Ok(path) => rel_path_to_forward_slashes(path), Err(error) => { return Err(error.into_io_error()); } }; Ok(TestEntry { source: TestSource::IncludeDir(file), rel_path, }) }) } #[derive(Debug)] pub(crate) struct TestEntry { source: TestSource, rel_path: Utf8PathBuf, } impl TestEntry { pub(crate) fn from_full_path(root: &Utf8Path, path: Utf8PathBuf) -> Self { let path = normalize_slashes(&path); let rel_path = rel_path_to_forward_slashes(path.strip_prefix(root).unwrap_or_else(|_| { panic!("failed to strip root '{}' from path '{}'", root, path) })); Self { source: TestSource::Path(path), rel_path, } } pub(crate) fn derive_test_name(&self, test_name: &str) -> String { format!("{}::{}", test_name, self.rel_path) } pub(crate) fn read(&self) -> crate::Result> { match &self.source { TestSource::Path(path) => std::fs::read(path) .map_err(|err| format!("error reading file '{path}': {err}").into()), #[cfg(feature = "include-dir")] TestSource::IncludeDir(file) => Ok(file.contents().to_vec()), } } pub(crate) fn read_as_string(&self) -> crate::Result { match &self.source { TestSource::Path(path) => std::fs::read_to_string(path) .map_err(|err| format!("error reading file '{path}' as UTF-8: {err}").into()), #[cfg(feature = "include-dir")] TestSource::IncludeDir(file) => { let contents = file.contents().to_vec(); String::from_utf8(contents).map_err(|err| { format!( "error reading included file at '{}' as UTF-8: {err}", self.rel_path ) .into() }) } } } /// Returns the path to match regexes against. /// /// This is always the relative path to the file from the include directory. pub(crate) fn match_path(&self) -> &Utf8Path { &self.rel_path } /// Returns the path to the test data, as passed into the test function. /// /// For directories on disk, this is the relative path after being joined /// with the include directory. For `include_dir` sources, this is the path /// relative to the root of the include directory. pub(crate) fn test_path(&self) -> &Utf8Path { match &self.source { TestSource::Path(path) => path, #[cfg(feature = "include-dir")] TestSource::IncludeDir(_) => { // The UTF-8-encoded version of file.path is stored in `rel_path`. &self.rel_path } } } /// Returns the path to the file on disk. /// /// If the data source is an `include_dir`, this will return `None`. pub(crate) fn disk_path(&self) -> Option<&Utf8Path> { match &self.source { TestSource::Path(path) => Some(path), #[cfg(feature = "include-dir")] TestSource::IncludeDir(_) => None, } } /// Returns true if the path exists. pub(crate) fn exists(&self) -> bool { match &self.source { TestSource::Path(path) => path.exists(), #[cfg(feature = "include-dir")] TestSource::IncludeDir(_) => { // include_dir files are guaranteed to exist. true } } } } #[cfg(windows)] #[track_caller] fn normalize_slashes(path: &Utf8Path) -> Utf8PathBuf { if is_truly_relative(path) { rel_path_to_forward_slashes(path) } else { path.as_str().replace('/', "\\").into() } } #[cfg(windows)] #[track_caller] fn rel_path_to_forward_slashes(path: &Utf8Path) -> Utf8PathBuf { assert!(is_truly_relative(path), "path {path} must be relative"); path.as_str().replace('\\', "/").into() } #[cfg(not(windows))] #[track_caller] fn normalize_slashes(path: &Utf8Path) -> Utf8PathBuf { path.to_owned() } #[cfg(not(windows))] #[track_caller] fn rel_path_to_forward_slashes(path: &Utf8Path) -> Utf8PathBuf { assert!(is_truly_relative(path), "path {path} must be relative"); path.to_owned() } /// Returns true if this is a path with no root-dir or prefix components. /// /// On Windows, unlike `path.is_relative()`, this rejects paths like "C:temp" /// and "\temp". #[track_caller] fn is_truly_relative(path: &Utf8Path) -> bool { path.components().all(|c| match c { Utf8Component::Normal(_) | Utf8Component::CurDir | Utf8Component::ParentDir => true, Utf8Component::RootDir | Utf8Component::Prefix(_) => false, }) } #[derive(Debug)] #[doc(hidden)] pub(crate) enum TestSource { /// A data source on disk, with the path being the relative path to the file /// from the crate root. Path(Utf8PathBuf), #[cfg(feature = "include-dir")] IncludeDir(&'static include_dir::File<'static>), } /// Polymorphic dispatch to resolve data sources /// /// This is similar to how `test_kinds` works. Here, we're assuming that /// `include_dir::Dir` will never implement `ToString`. This isn't provable to /// the compiler directly, but is a reasonable assumption. /// /// This could use auto(de)ref specialization to be more semver-safe, but a /// `Display` impl on `include_dir::Dir` is exceedingly unlikely by Rust /// community standards, and meanwhile this produces better error messages. #[doc(hidden)] pub mod data_source_kinds { use super::*; mod private { pub trait AsDirectorySealed {} #[cfg(feature = "include-dir")] pub trait AsIncludeDirSealed {} } // -- As directory --- pub trait AsDirectory: private::AsDirectorySealed { fn resolve_data_source(self) -> DataSource; } impl private::AsDirectorySealed for T {} impl AsDirectory for T { fn resolve_data_source(self) -> DataSource { let s = self.to_string(); let path = Utf8Path::new(&s); DataSource::Directory(normalize_slashes(path)) } } #[cfg(feature = "include-dir")] pub trait AsIncludeDir: private::AsIncludeDirSealed { fn resolve_data_source(self) -> DataSource; } #[cfg(feature = "include-dir")] impl private::AsIncludeDirSealed for include_dir::Dir<'static> {} #[cfg(feature = "include-dir")] impl AsIncludeDir for include_dir::Dir<'static> { fn resolve_data_source(self) -> DataSource { DataSource::IncludeDir(std::borrow::Cow::Owned(self)) } } #[cfg(feature = "include-dir")] impl private::AsIncludeDirSealed for &'static include_dir::Dir<'static> {} #[cfg(feature = "include-dir")] impl AsIncludeDir for &'static include_dir::Dir<'static> { fn resolve_data_source(self) -> DataSource { DataSource::IncludeDir(std::borrow::Cow::Borrowed(self)) } } } #[cfg(test)] mod tests { use super::*; #[test] fn missing_test_name() { assert_eq!(derive_test_path("root".into(), "file", "test_name"), None); } #[test] fn missing_colons() { assert_eq!( derive_test_path("root".into(), "test_name", "test_name"), None ); } #[test] fn is_relative_to_root() { assert_eq!( derive_test_path("root".into(), "test_name::file", "test_name"), Some("root/file".into()) ); assert_eq!( derive_test_path("root2".into(), "test_name::file", "test_name"), Some("root2/file".into()) ); } #[test] fn nested_dirs() { assert_eq!( derive_test_path("root".into(), "test_name::dir/dir2/file", "test_name"), Some("root/dir/dir2/file".into()) ); } #[test] fn subsequent_module_separators_remain() { assert_eq!( derive_test_path("root".into(), "test_name::mod::file", "test_name"), Some("root/mod::file".into()) ); } #[test] fn inverse_of_derive_test_name() { let root: Utf8PathBuf = "root".into(); for (path, test_name) in [ (root.join("foo/bar.txt"), "test_name"), (root.join("foo::bar.txt"), "test_name"), (root.join("foo/bar/baz"), "test_name"), (root.join("foo"), "test_name::mod"), (root.join("🦀"), "🚀::🚀"), ] { let derived_test_name = derive_test_name(&root, &path, test_name); assert_eq!( derive_test_path(&root, &derived_test_name, test_name), Some(path) ); } } fn derive_test_name(root: &Utf8Path, path: &Utf8Path, test_name: &str) -> String { TestEntry::from_full_path(root, path.to_owned()).derive_test_name(test_name) } fn derive_test_path(root: &Utf8Path, path: &str, test_name: &str) -> Option { DataSource::Directory(root.to_owned()) .derive_exact(path, test_name) .map(|entry| entry.test_path().to_owned()) } } datatest-stable-0.3.3/src/lib.rs000064400000000000000000000231631046102023000146200ustar 00000000000000// Copyright (c) The datatest-stable Contributors // SPDX-License-Identifier: MIT OR Apache-2.0 #![forbid(unsafe_code)] //! `datatest-stable` is a test harness intended to write *file-driven* or *data-driven* tests, //! where individual test case fixtures are specified as files and not as code. //! //! Given: //! //! * a test `my_test` that accepts a path, and optionally the contents as input //! * a directory to look for files (test fixtures) in //! * a pattern to match files on //! //! `datatest-stable` will call the `my_test` function once per matching file in //! the directory. Directory traversals are recursive. //! //! `datatest-stable` works with [cargo nextest](https://nexte.st/), and is part //! of the [nextest-rs organization](https://github.com/nextest-rs/) on GitHub. //! With nextest, each test case is represented as a separate test, and is run //! as a separate process in parallel. //! //! # Usage //! //! 1. Configure the test target by setting `harness = false` in `Cargo.toml`: //! //! ```toml //! [[test]] //! name = "" //! harness = false //! ``` //! //! 2. Call the `datatest_stable::harness!` macro as: //! //! ```rust,ignore //! datatest_stable::harness! { //! { test = my_test, root = "path/to/fixtures", pattern = r".*" }, //! } //! ``` //! //! * `test` - The test function to be executed on each matching input. This function can be one //! of: //! * `fn(&Path) -> datatest_stable::Result<()>` //! * `fn(&Utf8Path) -> datatest_stable::Result<()>` ([`Utf8Path`](camino::Utf8Path) is part of the //! [`camino`] library, and is re-exported here for convenience.) //! * `fn(&P, String) -> datatest_stable::Result<()>` where `P` is `Path` or `Utf8Path`. If the //! extra `String` parameter is specified, the contents of the file will be loaded and passed in //! as a string (erroring out if that failed). //! * `fn(&P, Vec) -> datatest_stable::Result<()>` where `P` is `Path` or `Utf8Path`. If the //! extra `Vec` parameter is specified, the contents of the file will be loaded and passed //! in as a `Vec` (erroring out if that failed). //! //! * `root` - The path to the root directory where the input files (fixtures) //! live. Relative paths are resolved relative to the crate root (the directory where the crate's //! `Cargo.toml` is located). //! //! `root` is an arbitrary expression that implements //! [`Display`](std::fmt::Display), such as `&str`, or a function call that //! returns a [`Utf8PathBuf`](camino::Utf8PathBuf). //! //! * `pattern` - a regex used to match against and select each file to be tested. Extended regexes //! with lookaround and backtracking are supported via the [`fancy_regex`] crate. //! //! `pattern` is an arbitrary expression that implements [`Display`](std::fmt::Display), such as //! `&str`, or a function call that returns a `String`. //! //! `pattern` is optional, and defaults to `r".*"` (match all files). //! //! The three parameters can be repeated if you have multiple sets of data-driven tests to be run: //! //! ```rust,ignore //! datatest_stable::harness! { //! { test = testfn1, root = root1, pattern = pattern1 }, //! { test = testfn2, root = root2 }, //! } //! ``` //! //! Trailing commas are optional. //! //! ## Relative and absolute paths //! //! The `pattern` argument is tested against the **relative** path of each file, //! **excluding** the `root` prefix -- not the absolute path. //! //! The `Path` and `Utf8Path` passed into the test functions are created by //! joining `root` to the relative path of the file. //! //! * If `root` is **relative**, the paths passed in are relative to the crate root. //! * If `root` is **absolute**, the paths passed in are absolute. //! //! For uniformity, all relative paths use `/` as the path separator, //! including on Windows, and all absolute paths use the platform's native //! separator throughout. //! //! ## Examples //! //! This is an example test. Use it with `harness = false`. //! //! ```rust //! use datatest_stable::Utf8Path; //! use std::path::Path; //! //! fn my_test(path: &Path) -> datatest_stable::Result<()> { //! // ... write test here //! //! Ok(()) //! } //! //! fn my_test_utf8(path: &Utf8Path, contents: String) -> datatest_stable::Result<()> { //! // ... write test here //! //! Ok(()) //! } //! //! datatest_stable::harness! { //! { test = my_test, root = "path/to/fixtures" }, //! { //! test = my_test_utf8, //! root = "path/to/fixtures", //! pattern = r"^.*\.txt$", //! }, //! } //! ``` //! //! If `path/to/fixtures` contains a file `foo/bar.txt`, then: //! //! * The pattern `r"^.*/*"` will match `foo/bar.txt`. //! * `my_test` and `my_test_utf8` will be called with `"path/to/fixtures/foo/bar.txt"`. //! //! ## Embedding directories at compile time //! //! With the `include-dir` feature enabled, you can use the //! [`include_dir`](https://docs.rs/include_dir) crate's [`include_dir!`] macro. //! This allows you to embed directories into the binary at compile time. //! //! This is generally not recommended for rapidly-changing test data, since each //! change will force a rebuild. But it can be useful for relatively-unchanging //! data suites distributed separately, e.g. on crates.io. //! //! With the `include-dir` feature enabled, you can use: //! #![cfg_attr(feature = "include-dir", doc = "```rust")] #![cfg_attr(not(feature = "include-dir"), doc = "```rust,ignore")] //! // The `include_dir!` macro is re-exported for convenience. //! use datatest_stable::include_dir; //! use std::path::Path; //! //! fn my_test(path: &Path, contents: Vec) -> datatest_stable::Result<()> { //! // ... write test here //! Ok(()) //! } //! //! datatest_stable::harness! { //! { test = my_test, root = include_dir!("tests/files"), pattern = r"^.*\.json$" }, //! } //! ``` //! //! You can also use directories published as `static` items in upstream crates: //! #![cfg_attr(feature = "include-dir", doc = "```rust")] #![cfg_attr(not(feature = "include-dir"), doc = "```rust,ignore")] //! use datatest_stable::{include_dir, Utf8Path}; //! //! // In the upstream crate: //! pub static FIXTURES: include_dir::Dir<'static> = //! include_dir!("$CARGO_MANIFEST_DIR/tests/files"); //! //! // In your test: //! fn my_test(path: &Utf8Path, contents: String) -> datatest_stable::Result<()> { //! // ... write test here //! Ok(()) //! } //! //! datatest_stable::harness! { //! { test = my_test, root = &FIXTURES }, //! } //! ``` //! //! In this case, the passed-in `Path` and `Utf8Path` are always **relative** to //! the root of the included directory. Like elsewhere in `datatest-stable`, //! these relative paths always use forward slashes as separators, including on //! Windows. //! //! Because the files don't exist on disk, the test functions must accept their //! contents as either a `String` or a `Vec`. If the argument is not //! provided, the harness will panic at runtime. //! //! ## Conditionally embedding directories //! //! It is also possible to conditionally include directories at compile time via //! a feature flag. For example, you might have an internal-only `testing` //! feature that you turn on locally, but users don't on crates.io. In that //! case, you can use: //! #![cfg_attr(feature = "include-dir", doc = "```rust")] #![cfg_attr(not(feature = "include-dir"), doc = "```rust,ignore")] //! use datatest_stable::Utf8Path; //! //! // In the library itself: //! pub mod fixtures { //! #[cfg(feature = "testing")] //! pub static FIXTURES: &str = "tests/files"; //! //! #[cfg(not(feature = "testing"))] //! pub static FIXTURES: include_dir::Dir<'static> = //! include_dir::include_dir!("$CARGO_MANIFEST_DIR/tests/files"); //! } //! //! // In the test: //! fn my_test(path: &Utf8Path, contents: String) -> datatest_stable::Result<()> { //! // ... write test here //! Ok(()) //! } //! //! datatest_stable::harness! { //! { test = my_test, root = &fixtures::FIXTURES, pattern = r"^inputs/.*$" }, //! } //! ``` //! //! In this case, note that `path` will be relative to the **crate directory** //! (e.g. `tests/files/foo/bar.txt`) if `FIXTURES` is a string, and relative to //! the **include directory** (e.g. `foo/bar.txt`) if `FIXTURES` is a //! [`Dir`](include_dir::Dir). Your test should be prepared to handle either //! case. //! //! # Features //! //! * `include-dir`: Enables the `include_dir!` macro, which allows embedding //! directories at compile time. This feature is disabled by default. //! //! # Minimum supported Rust version (MSRV) //! //! The minimum supported Rust version is **Rust 1.72**. MSRV bumps may be accompanied by a minor //! version update; at any time, Rust versions from at least the last 6 months are supported. //! //! # See also //! //! * [`datatest`](https://crates.io/crates/datatest): the original inspiration for this crate, with //! more features but targeting nightly Rust. //! * [Data-driven testing](https://en.wikipedia.org/wiki/Data-driven_testing) #![warn(missing_docs)] #![cfg_attr(doc_cfg, feature(doc_cfg))] mod data_source; mod macros; mod runner; /// The result type for `datatest-stable` tests. pub type Result = std::result::Result>; #[doc(hidden)] pub use self::data_source::{data_source_kinds, DataSource}; /// Not part of the public API, just used for macros. #[doc(hidden)] pub use self::runner::{runner, test_kinds, Requirements, TestFn}; /// A re-export of this type from the `camino` crate, since it forms part of function signatures. #[doc(no_inline)] pub use camino::Utf8Path; /// A re-export of `include_dir!` from the `include_dir` crate, for convenience. #[cfg(feature = "include-dir")] #[doc(no_inline)] pub use include_dir::include_dir; datatest-stable-0.3.3/src/macros.rs000064400000000000000000000126501046102023000153350ustar 00000000000000// Copyright (c) The datatest-stable Contributors // SPDX-License-Identifier: MIT OR Apache-2.0 /// `datatest-stable` test harness entry point. Should be declared in the test module. /// /// Also, `harness` should be set to `false` for that test module in `Cargo.toml` (see [Configuring /// a target](https://doc.rust-lang.org/cargo/reference/manifest.html#configuring-a-target)). #[macro_export] macro_rules! harness { ( $( { $($args:tt)* } ),+ $(,)* ) => { fn main() -> ::std::process::ExitCode { let mut requirements = Vec::new(); use $crate::data_source_kinds::*; use $crate::test_kinds::*; $( $crate::harness_collect!(@gather_test requirements, { $($args)*, } => { }); )+ $crate::runner(&requirements) } }; ( $( $name:path, $root:expr, $pattern:expr ),+ $(,)* ) => { // This is the old format with datatest-stable 0.2. Print a nice message // in this case. const _: () = { compile_error!( concat!(r"this format is no longer supported -- please switch to specifying as: datatest_stable::harness! { ", $(concat!(" { test = ", stringify!($name), ", root = ", stringify!($root), ", pattern = ", stringify!($pattern), " },\n"),)+ r"} note: patterns are now evaluated relative to the provided root, not to the crate root ")); }; } } #[macro_export] #[doc(hidden)] macro_rules! harness_collect { // Gather `test` (@gather_test $requirements:expr, // Note: here and below, rest always ends with at least 1 comma { test = $test:path, $($rest:tt)* } => { } ) => { $crate::harness_collect!(@gather_root $requirements, { $($rest)* } => { test = $test, } ); }; // `test` not found (@gather_test $requirements:expr, { $key:ident $($rest:tt)* } => { } ) => { compile_error!(concat!("expected `test`, found `", stringify!($key), "`")); }; // No remaining arguments (@gather_test $requirements:expr, { $(,)* } => { } ) => { compile_error!("expected `test`, but ran out of arguments"); }; // Something that isn't an identifier (@gather_test $requirements:expr, { $($rest:tt)* } => { } ) => { compile_error!(concat!("expected `test`, found non-identifier token: (rest: ", stringify!($($rest)*), ")")); }; // Gather `root` (@gather_root $requirements:expr, { root = $root:expr, $($rest:tt)* } => { $($collected:tt)* } ) => { $crate::harness_collect!(@gather_pattern $requirements, { $($rest)* } => { $($collected)* root = $root, } ); }; // `root` not found (@gather_root $requirements:expr, { $key:ident $($rest:tt)* } => { $($collected:tt)* } ) => { compile_error!(concat!("expected `root`, found `", stringify!($key), "`")); }; // No remaining arguments (@gather_root $requirements:expr, { $(,)* } => { $($collected:tt)* } ) => { compile_error!(concat!("expected `root`, but ran out of arguments (collected: ", stringify!($($collected)*), ")")); }; // Something that isn't an identifier (@gather_root $requirements:expr, { $($rest:tt)* } => { $($collected:tt)* } ) => { compile_error!(concat!("expected `root`, found non-identifier token (rest: ", stringify!($($rest)*), ")")); }; // Gather pattern (@gather_pattern $requirements:expr, { pattern = $pattern:expr, $($rest:tt)* } => { $($collected:tt)* } ) => { $crate::harness_collect!(@finish $requirements, { $($rest)* } => { $($collected)* pattern = $pattern, } ); }; // `pattern` not found (@gather_pattern $requirements:expr, { $key:ident $($rest:tt)* } => { $($collected:tt)* } ) => { compile_error!(concat!("expected `pattern`, found `", stringify!($key), "`")); }; // `pattern` not found: no remaining arguments (@gather_pattern $requirements:expr, { $(,)* } => { $($collected:tt)* } ) => { $crate::harness_collect!(@finish $requirements, { } => { $($collected)* pattern = ".*", } ); }; // Something that isn't an identifier (@gather_pattern $requirements:expr, { $($rest:tt)* } => { $($collected:tt)* } ) => { compile_error!(concat!("expected `pattern`, found non-identifier token (rest: ", stringify!($($rest)*), ")")); }; // Finish - no more arguments allowed (@finish $requirements:expr, { $(,)* } => { test = $test:path, root = $root:expr, pattern = $pattern:expr, } ) => { $requirements.push( $crate::Requirements::new( $test.kind().resolve($test), stringify!($test).to_string(), $root.resolve_data_source(), $pattern.to_string() ) ); }; // Finish - unexpected extra arguments (@finish $requirements:expr, { $($unexpected:tt)+ } => { $($collected:tt)* } ) => { compile_error!(concat!("unexpected extra arguments: ", stringify!($($unexpected)+))); }; } datatest-stable-0.3.3/src/runner.rs000064400000000000000000000355021046102023000153630ustar 00000000000000// Copyright (c) The datatest-stable Contributors // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::{data_source::TestEntry, DataSource, Result}; use camino::{Utf8Path, Utf8PathBuf}; use libtest_mimic::{Arguments, Trial}; use std::{path::Path, process::ExitCode}; #[doc(hidden)] pub fn runner(requirements: &[Requirements]) -> ExitCode { if let Some(cwd) = custom_cwd() { std::env::set_current_dir(cwd).expect("set custom working directory"); } let args = Arguments::from_args(); let tests = find_tests(&args, requirements); let conclusion = libtest_mimic::run(&args, tests); // This used to use `Conclusion::exit`, but that exits the process via `std::process::exit` as // of libtest-mimic 0.7.0. This breaks some things, e.g. llvm-cov on Windows. // https://github.com/nextest-rs/datatest-stable/issues/20 // // Use `std::process::ExitCode` instead, and return it in main. conclusion.exit_code() } /// One of our tests requires that a custom working directory be set. This function is used to do /// that. fn custom_cwd() -> Option { std::env::var("__DATATEST_CWD").ok().map(Utf8PathBuf::from) } fn find_tests(args: &Arguments, requirements: &[Requirements]) -> Vec { let tests: Vec<_> = if let Some(exact_filter) = exact_filter(args) { let exact_tests: Vec<_> = requirements .iter() .filter_map(|req| req.exact(exact_filter)) .collect(); match NextestKind::determine() { NextestKind::InUse { process_per_test } => { if exact_tests.is_empty() { panic!("Failed to find exact match for filter {exact_filter}"); } if process_per_test && exact_tests.len() > 1 { panic!( "Only expected one but found {} exact matches for filter {exact_filter}", exact_tests.len() ); } } NextestKind::NotInUse => {} } exact_tests } else if is_full_scan_forbidden(args) { panic!("Exact filter was expected to be used"); } else { let mut tests: Vec<_> = requirements.iter().flat_map(|req| req.expand()).collect(); tests.sort_unstable_by(|a, b| a.name().cmp(b.name())); tests }; tests } #[derive(Clone, Copy, Debug)] enum NextestKind { NotInUse, InUse { process_per_test: bool }, } impl NextestKind { fn determine() -> Self { if std::env::var("NEXTEST").as_deref() == Ok("1") { // Process-per-test means that exactly one test should be run. let process_per_test = std::env::var("NEXTEST_EXECUTION_MODE").as_deref() == Ok("process-per-test"); Self::InUse { process_per_test } } else { Self::NotInUse } } } fn is_full_scan_forbidden(args: &Arguments) -> bool { !args.list && std::env::var("__DATATEST_FULL_SCAN_FORBIDDEN").as_deref() == Ok("1") } fn exact_filter(args: &Arguments) -> Option<&str> { if args.exact && args.skip.is_empty() { args.filter.as_deref() } else { None } } #[doc(hidden)] pub struct Requirements { test: TestFn, test_name: String, root: DataSource, pattern: String, } impl Requirements { #[doc(hidden)] pub fn new(test: TestFn, test_name: String, root: DataSource, pattern: String) -> Self { // include_dir data sources aren't compatible with test functions that // don't accept the contents as an argument. if !test.loads_data() && root.is_in_memory() { panic!( "test data for '{}' is stored in memory, so it \ must accept file contents as an argument", test_name ); } Self { test, test_name, root, pattern, } } fn trial(&self, entry: TestEntry) -> Trial { let testfn = self.test; let name = entry.derive_test_name(&self.test_name); Trial::test(name, move || { testfn .call(entry) .map_err(|err| format!("{:?}", err).into()) }) } fn exact(&self, filter: &str) -> Option { let entry = self.root.derive_exact(filter, &self.test_name)?; entry.exists().then(|| self.trial(entry)) } /// Scans all files in a given directory, finds matching ones and generates a test descriptor /// for each of them. fn expand(&self) -> Vec { let re = fancy_regex::Regex::new(&self.pattern) .unwrap_or_else(|_| panic!("invalid regular expression: '{}'", self.pattern)); let tests: Vec<_> = self .root .walk_files() .filter_map(|entry_res| { let entry = entry_res.expect("error reading directory"); let path_str = entry.match_path().as_str(); if re.is_match(path_str).unwrap_or_else(|error| { panic!( "error matching pattern '{}' against path '{}' : {}", self.pattern, path_str, error ) }) { Some(self.trial(entry)) } else { None } }) .collect(); // We want to avoid silent fails due to typos in regexp! if tests.is_empty() { panic!( "no test cases found for test '{}' -- scanned {} with pattern '{}'", self.test_name, self.root.display(), self.pattern, ); } tests } } // -- Polymorphic dispatch -- #[derive(Clone, Copy)] #[doc(hidden)] pub enum TestFn { // Functions that work on paths. Base(TestFnBase), /// Test functions that load a file as a string (UTF-8 text). LoadString(TestFnLoadString), /// Test functions that load a file as binary data. LoadBinary(TestFnLoadBinary), } impl TestFn { fn loads_data(&self) -> bool { match self { TestFn::Base(_) => false, TestFn::LoadString(_) | TestFn::LoadBinary(_) => true, } } fn call(&self, entry: TestEntry) -> Result<()> { match self { TestFn::Base(f) => { let path = entry .disk_path() .expect("test entry being on disk was checked in the constructor"); f.call(path) } TestFn::LoadString(f) => f.call(entry), TestFn::LoadBinary(f) => f.call(entry), } } } #[derive(Clone, Copy)] #[doc(hidden)] pub enum TestFnBase { Path(fn(&Path) -> Result<()>), Utf8Path(fn(&Utf8Path) -> Result<()>), } impl TestFnBase { fn call(&self, path: &Utf8Path) -> Result<()> { match self { TestFnBase::Path(f) => f(path.as_ref()), TestFnBase::Utf8Path(f) => f(path), } } } #[derive(Clone, Copy)] #[doc(hidden)] pub enum TestFnLoadString { Path(fn(&Path, String) -> Result<()>), Utf8Path(fn(&Utf8Path, String) -> Result<()>), } impl TestFnLoadString { fn call(&self, entry: TestEntry) -> Result<()> { let contents = entry.read_as_string()?; match self { TestFnLoadString::Path(f) => f(entry.test_path().as_ref(), contents), TestFnLoadString::Utf8Path(f) => f(entry.test_path(), contents), } } } #[derive(Clone, Copy)] #[doc(hidden)] pub enum TestFnLoadBinary { Path(fn(&Path, Vec) -> Result<()>), Utf8Path(fn(&Utf8Path, Vec) -> Result<()>), } impl TestFnLoadBinary { fn call(&self, entry: TestEntry) -> Result<()> { let contents = entry.read()?; match self { TestFnLoadBinary::Path(f) => f(entry.test_path().as_ref(), contents), TestFnLoadBinary::Utf8Path(f) => f(entry.test_path(), contents), } } } /// Implementations to allow `TestFn` to be created with functions of one of several types. /// /// datatest-stable supports several options for the shape of test functions. This code allows: /// /// * the `harness!` macro to accept any of these types of functions, generating the same syntactic /// code for each. /// * for all of those functions to resolve to a single `TestFn` type which can do dynamic dispatch. /// /// There are several challenges to overcome here, the main one being that you cannot have multiple /// different kinds of `Fn`s impl the same trait. For example, rustc will not accept this code /// ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=007c814fb457bd4e95d0073745b5f8d9)): /// /// ```rust,ignore /// trait MyTrait {} /// /// impl MyTrait for F {} /// impl MyTrait for F {} /// ``` /// /// This means that it is not possible to write code that is type-system generic over different /// kinds of function types. /// /// But since `harness!` is a macro, it can expand to code that's syntactically the same for each of /// those function types, but semantically resolves to completely different types. That's exactly /// what we do here. /// /// This is a trick very similar to autoref specialization, though we don't use the automatic `&` /// Rust inserts while dereferencing methods. See [autoref-specialization]. /// /// # Notes /// /// We can't say `impl PathKind for fn(&Path) -> Result<()>` because Rust won't automatically coerce /// the concrete function type to the function pointer. (If we could, then we wouldn't need to rely /// on the macro-ness of `harness!` at all, and could just have a single trait implemented for all /// the different function pointer types.) To address this, we use a two-step process. /// /// * Step 1: Implement `PathKind` for all `F: Fn(&Path) -> Result<()>`. This allows a `.kind()` /// method to exist which returns a new `PathTag` type. /// * Step 2: Implement `PathTag::new` which only takes `fn(&Path) -> Result<()>`. *This* type can /// coerce to the function pointer, which can be stored in the `TestFn` enum. /// /// This two-step process is similar to the one documented in [autoref-specialization]. /// /// [autoref-specialization]: https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md #[doc(hidden)] pub mod test_kinds { use super::*; mod private { // We need to define a separate Sealed for each of the tags below, because Rust doesn't allow // multiple kinds of F: Fn(T) -> Result<()> to implement the same trait. pub trait PathSealed {} pub trait Utf8PathSealed {} pub trait PathStringSealed {} pub trait Utf8PathStringSealed {} pub trait PathBytesSealed {} pub trait Utf8PathBytesSealed {} } // -- Paths -- #[doc(hidden)] pub struct PathTag; impl PathTag { #[inline] pub fn resolve(self, f: fn(&Path) -> Result<()>) -> TestFn { TestFn::Base(TestFnBase::Path(f)) } } #[doc(hidden)] pub trait PathKind: private::PathSealed { #[inline] fn kind(&self) -> PathTag { PathTag } } impl Result<()>> private::PathSealed for F {} impl Result<()>> PathKind for F {} // -- UTF-8 paths -- #[doc(hidden)] pub struct Utf8PathTag; impl Utf8PathTag { #[inline] pub fn resolve(&self, f: fn(&Utf8Path) -> Result<()>) -> TestFn { TestFn::Base(TestFnBase::Utf8Path(f)) } } #[doc(hidden)] pub trait Utf8PathKind: private::Utf8PathSealed { #[inline] fn kind(&self) -> Utf8PathTag { Utf8PathTag } } impl Result<()>> private::Utf8PathSealed for F {} impl Result<()>> Utf8PathKind for F {} // -- Path, load file as string -- #[doc(hidden)] pub struct PathStringTag; impl PathStringTag { #[inline] pub fn resolve(self, f: fn(&Path, String) -> Result<()>) -> TestFn { TestFn::LoadString(TestFnLoadString::Path(f)) } } #[doc(hidden)] pub trait PathStringKind: private::PathStringSealed { #[inline] fn kind(&self) -> PathStringTag { PathStringTag } } impl Result<()>> private::PathStringSealed for F {} impl Result<()>> PathStringKind for F {} // -- Utf8Path, load file as string -- #[doc(hidden)] pub struct Utf8PathStringTag; impl Utf8PathStringTag { #[inline] pub fn resolve(self, f: fn(&Utf8Path, String) -> Result<()>) -> TestFn { TestFn::LoadString(TestFnLoadString::Utf8Path(f)) } } #[doc(hidden)] pub trait Utf8PathStringKind: private::Utf8PathStringSealed { #[inline] fn kind(&self) -> Utf8PathStringTag { Utf8PathStringTag } } impl Result<()>> private::Utf8PathStringSealed for F {} impl Result<()>> Utf8PathStringKind for F {} // -- Path, load file as binary -- #[doc(hidden)] pub struct PathBytesTag; impl PathBytesTag { #[inline] pub fn resolve(self, f: fn(&Path, Vec) -> Result<()>) -> TestFn { TestFn::LoadBinary(TestFnLoadBinary::Path(f)) } } #[doc(hidden)] pub trait PathBytesKind: private::PathBytesSealed { #[inline] fn kind(&self) -> PathBytesTag { PathBytesTag } } impl) -> Result<()>> private::PathBytesSealed for F {} impl) -> Result<()>> PathBytesKind for F {} // -- Utf8Path, load file as binary -- #[doc(hidden)] pub struct Utf8PathBytesTag; impl Utf8PathBytesTag { #[inline] pub fn resolve(self, f: fn(&Utf8Path, Vec) -> Result<()>) -> TestFn { TestFn::LoadBinary(TestFnLoadBinary::Utf8Path(f)) } } #[doc(hidden)] pub trait Utf8PathBytesKind: private::Utf8PathBytesSealed { #[inline] fn kind(&self) -> Utf8PathBytesTag { Utf8PathBytesTag } } impl) -> Result<()>> private::Utf8PathBytesSealed for F {} impl) -> Result<()>> Utf8PathBytesKind for F {} } #[cfg(all(test, feature = "include-dir"))] mod include_dir_tests { use super::*; use std::borrow::Cow; #[test] #[should_panic = "test data for 'my_test' is stored in memory, \ so it must accept file contents as an argument"] fn include_dir_without_arg() { fn my_test(_: &Path) -> Result<()> { Ok(()) } Requirements::new( TestFn::Base(TestFnBase::Path(my_test)), "my_test".to_owned(), DataSource::IncludeDir(Cow::Owned(include_dir::include_dir!("tests/files"))), "xxx".to_owned(), ); } } datatest-stable-0.3.3/tests/compile-fail/empty-arg-list.rs000064400000000000000000000000461046102023000216370ustar 00000000000000datatest_stable::harness! { { } } datatest-stable-0.3.3/tests/compile-fail/empty-arg-list.stderr000064400000000000000000000005641046102023000225230ustar 00000000000000error: expected `test`, but ran out of arguments --> tests/compile-fail/empty-arg-list.rs:1:1 | 1 | / datatest_stable::harness! { 2 | | { } 3 | | } | |_^ | = note: this error originates in the macro `$crate::harness_collect` which comes from the expansion of the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info) datatest-stable-0.3.3/tests/compile-fail/extra-args.rs000064400000000000000000000001421046102023000210330ustar 00000000000000datatest_stable::harness! { { test = my_test, root = "abc", pattern = ".*", extra = "xyz" } } datatest-stable-0.3.3/tests/compile-fail/extra-args.stderr000064400000000000000000000006551046102023000217230ustar 00000000000000error: unexpected extra arguments: extra = "xyz", --> tests/compile-fail/extra-args.rs:1:1 | 1 | / datatest_stable::harness! { 2 | | { test = my_test, root = "abc", pattern = ".*", extra = "xyz" } 3 | | } | |_^ | = note: this error originates in the macro `$crate::harness_collect` which comes from the expansion of the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info) datatest-stable-0.3.3/tests/compile-fail/incorrect-arg-in-pattern.rs000064400000000000000000000001311046102023000235720ustar 00000000000000datatest_stable::harness! { { test = my_test, root = "tests/files", foo = "bar", } } datatest-stable-0.3.3/tests/compile-fail/incorrect-arg-in-pattern.stderr000064400000000000000000000006471046102023000244650ustar 00000000000000error: expected `pattern`, found `foo` --> tests/compile-fail/incorrect-arg-in-pattern.rs:1:1 | 1 | / datatest_stable::harness! { 2 | | { test = my_test, root = "tests/files", foo = "bar", } 3 | | } | |_^ | = note: this error originates in the macro `$crate::harness_collect` which comes from the expansion of the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info) datatest-stable-0.3.3/tests/compile-fail/missing-root-comma.rs000064400000000000000000000001561046102023000225070ustar 00000000000000datatest_stable::harness! { { test = my_test, pattern = r"^.*(? tests/compile-fail/missing-root-comma.rs:1:1 | 1 | / datatest_stable::harness! { 2 | | { 3 | | test = my_test, 4 | | pattern = r"^.*(? tests/compile-fail/missing-root-no-args.rs:1:1 | 1 | / datatest_stable::harness! { 2 | | { 3 | | test = my_test, 4 | | }, 5 | | } | |_^ | = note: this error originates in the macro `$crate::harness_collect` which comes from the expansion of the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info) datatest-stable-0.3.3/tests/compile-fail/missing-root-no-comma.rs000064400000000000000000000001301046102023000231110ustar 00000000000000datatest_stable::harness! { { test = my_test, pattern = r"^.*(? tests/compile-fail/missing-root-no-comma.rs:1:1 | 1 | / datatest_stable::harness! { 2 | | { test = my_test, pattern = r"^.*(? tests/compile-fail/missing-test-comma.rs:1:1 | 1 | / datatest_stable::harness! { 2 | | { 3 | | root = "tests/files", 4 | | pattern = r"^.*(? tests/compile-fail/missing-test-no-comma.rs:1:1 | 1 | / datatest_stable::harness! { 2 | | { root = "tests/files", test = my_test, pattern = r"^.*(? tests/compile-fail/old-format.rs:1:1 | 1 | datatest_stable::harness!(foo, "path1", r"^.*/*$", bar::baz, "path2", r"^.*/*$"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this error originates in the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info) datatest-stable-0.3.3/tests/compile-fail/pattern-not-ident.rs000064400000000000000000000001201046102023000223260ustar 00000000000000datatest_stable::harness! { { test = my_test, root = "abc", "xyz" = foo } } datatest-stable-0.3.3/tests/compile-fail/pattern-not-ident.stderr000064400000000000000000000006731046102023000232220ustar 00000000000000error: expected `pattern`, found non-identifier token (rest: "xyz" = foo,) --> tests/compile-fail/pattern-not-ident.rs:1:1 | 1 | / datatest_stable::harness! { 2 | | { test = my_test, root = "abc", "xyz" = foo } 3 | | } | |_^ | = note: this error originates in the macro `$crate::harness_collect` which comes from the expansion of the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info) datatest-stable-0.3.3/tests/compile-fail/root-not-ident.rs000064400000000000000000000001021046102023000216340ustar 00000000000000datatest_stable::harness! { { test = my_test, "xyz" = foo } } datatest-stable-0.3.3/tests/compile-fail/root-not-ident.stderr000064400000000000000000000006471046102023000225310ustar 00000000000000error: expected `root`, found non-identifier token (rest: "xyz" = foo,) --> tests/compile-fail/root-not-ident.rs:1:1 | 1 | / datatest_stable::harness! { 2 | | { test = my_test, "xyz" = foo } 3 | | } | |_^ | = note: this error originates in the macro `$crate::harness_collect` which comes from the expansion of the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info) datatest-stable-0.3.3/tests/compile-fail/root-out-of-order.rs000064400000000000000000000001561046102023000222660ustar 00000000000000datatest_stable::harness! { { test = my_test, pattern = r"^.*(? tests/compile-fail/root-out-of-order.rs:1:1 | 1 | / datatest_stable::harness! { 2 | | { test = my_test, pattern = r"^.*(? tests/compile-fail/test-not-ident.rs:1:1 | 1 | / datatest_stable::harness! { 2 | | { "xyz" = foo } 3 | | } | |_^ | = note: this error originates in the macro `$crate::harness_collect` which comes from the expansion of the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info) datatest-stable-0.3.3/tests/compile-fail/test-out-of-order.rs000064400000000000000000000001361046102023000222600ustar 00000000000000datatest_stable::harness! { { root = "tests/files", pattern = r"^.*(? tests/compile-fail/test-out-of-order.rs:1:1 | 1 | / datatest_stable::harness! { 2 | | { root = "tests/files", pattern = r"^.*(? Result<()> { let mut file = File::open(path)?; let mut contents = String::new(); file.read_to_string(&mut contents)?; Ok(()) } fn test_artifact_utf8(path: &Utf8Path) -> Result<()> { test_artifact(path.as_ref()) } fn test_artifact_utf8_abs(path: &Utf8Path) -> Result<()> { // path must be absolute assert!(path.is_absolute(), "path must be absolute: {:?}", path); // On Windows, the path must be normalized to use backslashes. #[cfg(windows)] { assert!( !path.as_str().contains('/'), "path must not contain forward slashes: {:?}", path ); } test_artifact(path.as_ref()) } #[cfg(feature = "include-dir")] #[macro_use] mod with_contents { use super::*; /// Returns an `include_dir::Dir` instance. macro_rules! maybe_include_dir { () => { include_dir::include_dir!("$CARGO_MANIFEST_DIR/tests/files") }; } /// A `&'static include_dir::Dir` instance. pub(crate) static MAYBE_INCLUDE_STATIC: include_dir::Dir = include_dir::include_dir!("$CARGO_MANIFEST_DIR/tests/files"); pub(crate) fn test_artifact_string(path: &Path, contents: String) -> Result<()> { compare_contents(path, contents.as_bytes()) } pub(crate) fn test_artifact_utf8_string(path: &Utf8Path, contents: String) -> Result<()> { compare_contents(path.as_std_path(), contents.as_bytes()) } pub(crate) fn test_artifact_bytes(path: &Path, contents: Vec) -> Result<()> { compare_contents(path, &contents) } pub(crate) fn test_artifact_utf8_bytes(path: &Utf8Path, contents: Vec) -> Result<()> { compare_contents(path.as_std_path(), &contents) } fn compare_contents(path: &Path, expected: &[u8]) -> Result<()> { // The path must not begin with "tests/files". assert!( !path.to_string_lossy().starts_with("tests/files"), "path must not start with 'tests/files': {:?}", path ); // Prepend tests/files to the path to get the expected contents. In // general we can't verify the contents, but in this case we can do so // because the paths are also available on disk. let path = format!("tests/files/{}", path.to_str().unwrap()); compare(path.as_ref(), expected) } } #[cfg(not(feature = "include-dir"))] #[macro_use] mod with_contents { use super::*; /// Returns an `include_dir::Dir` instance. macro_rules! maybe_include_dir { () => { "tests/files" }; } /// A `&'static include_dir::Dir` instance. pub(crate) static MAYBE_INCLUDE_STATIC: &str = "tests/files"; pub(crate) fn test_artifact_string(path: &Path, contents: String) -> Result<()> { compare_contents(path, contents.as_bytes()) } pub(crate) fn test_artifact_utf8_string(path: &Utf8Path, contents: String) -> Result<()> { compare_contents(path.as_std_path(), contents.as_bytes()) } pub(crate) fn test_artifact_bytes(path: &Path, contents: Vec) -> Result<()> { compare_contents(path, &contents) } pub(crate) fn test_artifact_utf8_bytes(path: &Utf8Path, contents: Vec) -> Result<()> { compare_contents(path.as_std_path(), &contents) } fn compare_contents(path: &Path, expected: &[u8]) -> Result<()> { // The path must begin with "tests/files". assert!( path.to_string_lossy().starts_with("tests/files"), "path must start with 'tests/files': {:?}", path ); compare(&path, expected) } } fn compare(path: &Path, expected: &[u8]) -> Result<()> { // The path must be relative. assert!(path.is_relative(), "path must be relative: {:?}", path); // The path must not have any backslashes on Windows. assert!( !path.to_string_lossy().contains('\\'), "path must not contain backslashes: {:?}", path ); let actual = std::fs::read(path).map_err(|error| format!("failed to read file: {:?}: {error}", path))?; assert_eq!(expected, &actual, "file contents match for {:?}", path); Ok(()) } #[cfg(windows)] static TESTS_FILES_MAIN_SEP: &str = "tests\\files"; #[cfg(not(windows))] static TESTS_FILES_MAIN_SEP: &str = "tests/files"; fn tests_files_abs() -> String { std::env::current_dir() .expect("current dir obtained") .join("tests/files") .to_string_lossy() .into_owned() } datatest_stable::harness! { { test = test_artifact, root = "tests/files", // This regex pattern skips .skip.txt files. pattern = r"^.*(? String { std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()) }