From 7fda67fc4b6ca077fe56cb7d6ea5f6b58dcba050 Mon Sep 17 00:00:00 2001 From: Alex Huszagh Date: Sun, 20 Nov 2022 14:59:59 -0600 Subject: [PATCH 1/6] Initial version of UI tests. --- .github/workflows/ci.yml | 21 + Cargo.lock | 299 +++++++++- Cargo.toml | 2 + ci/test-rest-args.sh | 39 ++ src/bin/cross.rs | 64 ++- src/cargo.rs | 49 +- src/cli.rs | 594 +++++++++++++------- src/lib.rs | 14 +- src/shell.rs | 48 +- src/tests.rs | 5 +- tests/cli.rs | 350 ++++++++++++ tests/cmd/build-no-color.stderr | 1 + tests/cmd/build-no-color.stdout | 1 + tests/cmd/build-no-color.toml | 3 + tests/cmd/color.toml | 2 + tests/cmd/double-color-value-both.stderr | 1 + tests/cmd/double-color-value-both.stdout | 1 + tests/cmd/double-color-value-both.toml | 3 + tests/cmd/double-color-value-one.stderr | 1 + tests/cmd/double-color-value-one.stdout | 1 + tests/cmd/double-color-value-one.toml | 3 + tests/cmd/double-color.stderr | 6 + tests/cmd/double-color.stdout | 0 tests/cmd/double-color.toml | 3 + tests/cmd/explain-list.stderr | 1 + tests/cmd/explain-list.stdout | 1 + tests/cmd/explain-list.toml | 2 + tests/cmd/explain.stderr | 1 + tests/cmd/explain.stdout | 5 + tests/cmd/explain.toml | 2 + tests/cmd/fake-engine.stderr | 10 + tests/cmd/fake-engine.stdout | 0 tests/cmd/fake-engine.toml | 7 + tests/cmd/fake-image.stderr | 2 + tests/cmd/fake-image.stdout | 0 tests/cmd/fake-image.toml | 6 + tests/cmd/fake-toolchain.stderr | 3 + tests/cmd/fake-toolchain.stdout | 0 tests/cmd/fake-toolchain.toml | 3 + tests/cmd/fallback.stderr | 1 + tests/cmd/help.stderr | 1 + tests/cmd/help.stdout | 13 + tests/cmd/invalid-color.toml | 3 + tests/cmd/list-explain.stderr | 1 + tests/cmd/list-explain.stdout | 1 + tests/cmd/list-explain.toml | 2 + tests/cmd/list.stderr | 1 + tests/cmd/list.stdout | 19 + tests/cmd/list.toml | 2 + tests/cmd/no-args.stderr | 1 + tests/cmd/no-args.stdout | 1 + tests/cmd/no-args.toml | 1 + tests/cmd/no-color.stderr | 7 + tests/cmd/no-color.stdout | 0 tests/cmd/no-color.toml | 3 + tests/cmd/outside-package-target-dir.stderr | 1 + tests/cmd/outside-package-target-dir.stdout | 1 + tests/cmd/outside-package-target-dir.toml | 4 + tests/cmd/outside-package.stderr | 3 + tests/cmd/outside-package.stdout | 0 tests/cmd/outside-package.toml | 4 + tests/cmd/quiet-verbose-envvar-both.stderr | 1 + tests/cmd/quiet-verbose-envvar-both.stdout | 1 + tests/cmd/quiet-verbose-envvar-both.toml | 7 + tests/cmd/quiet-verbose-envvar-one.stderr | 0 tests/cmd/quiet-verbose-envvar-one.stdout | 0 tests/cmd/quiet-verbose-envvar-one.toml | 5 + tests/cmd/quiet-verbose-list.stderr | 1 + tests/cmd/quiet-verbose-list.stdout | 5 + tests/cmd/quiet-verbose-list.toml | 2 + tests/cmd/quiet-verbose-no-command.stderr | 1 + tests/cmd/quiet-verbose-no-command.stdout | 6 + tests/cmd/quiet-verbose-no-command.toml | 2 + tests/cmd/quiet-verbose.stderr | 1 + tests/cmd/quiet-verbose.stdout | 0 tests/cmd/quiet-verbose.toml | 3 + tests/cmd/short-version.stderr | 1 + tests/cmd/short-version.stdout | 1 + tests/cmd/short-version.toml | 2 + tests/cmd/version.stderr | 1 + tests/cmd/version.stdout | 2 + tests/cmd/version.toml | 2 + xtask/Cargo.toml | 1 + xtask/src/main.rs | 5 + xtask/src/temp.rs | 35 ++ 85 files changed, 1474 insertions(+), 235 deletions(-) create mode 100755 ci/test-rest-args.sh create mode 100644 tests/cli.rs create mode 120000 tests/cmd/build-no-color.stderr create mode 120000 tests/cmd/build-no-color.stdout create mode 100644 tests/cmd/build-no-color.toml create mode 100644 tests/cmd/color.toml create mode 120000 tests/cmd/double-color-value-both.stderr create mode 120000 tests/cmd/double-color-value-both.stdout create mode 100644 tests/cmd/double-color-value-both.toml create mode 120000 tests/cmd/double-color-value-one.stderr create mode 120000 tests/cmd/double-color-value-one.stdout create mode 100644 tests/cmd/double-color-value-one.toml create mode 100644 tests/cmd/double-color.stderr create mode 100644 tests/cmd/double-color.stdout create mode 100644 tests/cmd/double-color.toml create mode 120000 tests/cmd/explain-list.stderr create mode 120000 tests/cmd/explain-list.stdout create mode 100644 tests/cmd/explain-list.toml create mode 120000 tests/cmd/explain.stderr create mode 100644 tests/cmd/explain.stdout create mode 100644 tests/cmd/explain.toml create mode 100644 tests/cmd/fake-engine.stderr create mode 100644 tests/cmd/fake-engine.stdout create mode 100644 tests/cmd/fake-engine.toml create mode 100644 tests/cmd/fake-image.stderr create mode 100644 tests/cmd/fake-image.stdout create mode 100644 tests/cmd/fake-image.toml create mode 100644 tests/cmd/fake-toolchain.stderr create mode 100644 tests/cmd/fake-toolchain.stdout create mode 100644 tests/cmd/fake-toolchain.toml create mode 100644 tests/cmd/fallback.stderr create mode 120000 tests/cmd/help.stderr create mode 100644 tests/cmd/help.stdout create mode 100644 tests/cmd/invalid-color.toml create mode 120000 tests/cmd/list-explain.stderr create mode 120000 tests/cmd/list-explain.stdout create mode 100644 tests/cmd/list-explain.toml create mode 120000 tests/cmd/list.stderr create mode 100644 tests/cmd/list.stdout create mode 100644 tests/cmd/list.toml create mode 120000 tests/cmd/no-args.stderr create mode 120000 tests/cmd/no-args.stdout create mode 100644 tests/cmd/no-args.toml create mode 100644 tests/cmd/no-color.stderr create mode 100644 tests/cmd/no-color.stdout create mode 100644 tests/cmd/no-color.toml create mode 120000 tests/cmd/outside-package-target-dir.stderr create mode 120000 tests/cmd/outside-package-target-dir.stdout create mode 100644 tests/cmd/outside-package-target-dir.toml create mode 100644 tests/cmd/outside-package.stderr create mode 100644 tests/cmd/outside-package.stdout create mode 100644 tests/cmd/outside-package.toml create mode 120000 tests/cmd/quiet-verbose-envvar-both.stderr create mode 120000 tests/cmd/quiet-verbose-envvar-both.stdout create mode 100644 tests/cmd/quiet-verbose-envvar-both.toml create mode 100644 tests/cmd/quiet-verbose-envvar-one.stderr create mode 100644 tests/cmd/quiet-verbose-envvar-one.stdout create mode 100644 tests/cmd/quiet-verbose-envvar-one.toml create mode 120000 tests/cmd/quiet-verbose-list.stderr create mode 100644 tests/cmd/quiet-verbose-list.stdout create mode 100644 tests/cmd/quiet-verbose-list.toml create mode 120000 tests/cmd/quiet-verbose-no-command.stderr create mode 100644 tests/cmd/quiet-verbose-no-command.stdout create mode 100644 tests/cmd/quiet-verbose-no-command.toml create mode 100644 tests/cmd/quiet-verbose.stderr create mode 100644 tests/cmd/quiet-verbose.stdout create mode 100644 tests/cmd/quiet-verbose.toml create mode 120000 tests/cmd/short-version.stderr create mode 120000 tests/cmd/short-version.stdout create mode 100644 tests/cmd/short-version.toml create mode 120000 tests/cmd/version.stderr create mode 100644 tests/cmd/version.stdout create mode 100644 tests/cmd/version.toml create mode 100644 xtask/src/temp.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 13ac2048b..5da2fd9d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -74,6 +74,10 @@ jobs: - name: Run unit tests run: cargo test --locked --all-targets --workspace --all-features timeout-minutes: 10 + + - name: Run ignored unit tests + run: cargo test --locked --all-targets --workspace --all-features -- --ignored + timeout-minutes: 10 check: runs-on: ubuntu-latest outputs: @@ -347,6 +351,23 @@ jobs: TARGET: aarch64-unknown-linux-gnu shell: bash + rest-args: + needs: [test, check] + runs-on: ubuntu-latest + if: github.actor == 'bors[bot]' + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/setup-rust + + - name: LLVM instrument coverage + uses: ./.github/actions/cargo-llvm-cov + with: + name: integration-remote + + - name: Run Rest Args Test + run: ./ci/test-rest-args.sh + shell: bash + publish: needs: [build, check, fmt, clippy, cargo-deny] runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index ba0226801..0e8619b50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,6 +88,12 @@ version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + [[package]] name = "cc" version = "1.0.73" @@ -179,12 +185,48 @@ dependencies = [ "tracing-error", ] +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concolor" +version = "0.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b90f9dcd9490a97db91a85ccd79e38a87e14323f0bb824659ee3274e9143ba37" +dependencies = [ + "atty", + "bitflags", + "concolor-query", +] + +[[package]] +name = "concolor-query" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82a90734b3d5dcf656e7624cca6bce9c3a90ee11f900e80141a7427ccfb3d317" + [[package]] name = "const-sha1" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb58b6451e8c2a812ad979ed1d83378caa5e927eef2622017a45f251457c2c9d" +[[package]] +name = "content_inspector" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7bda66e858c683005a53a9a60c69a4aca7eeaa45d124526e389f7aec8e62f38" +dependencies = [ + "memchr", +] + [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -217,14 +259,50 @@ dependencies = [ "serde_json", "shell-escape", "shell-words", + "snapbox", "tempfile", "thiserror", "toml", + "trycmd", "walkdir", "which", "winapi", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bf8df95e795db1a4aca2957ad884a2df35413b24bbeb3114422f3cc21498e8" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + [[package]] name = "crossbeam-utils" version = "0.8.12" @@ -316,6 +394,18 @@ dependencies = [ "instant", ] +[[package]] +name = "filetime" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys", +] + [[package]] name = "fnv" version = "1.0.7" @@ -339,6 +429,12 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + [[package]] name = "globset" version = "0.4.9" @@ -352,6 +448,12 @@ dependencies = [ "regex", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "heck" version = "0.4.0" @@ -385,6 +487,22 @@ dependencies = [ "winapi", ] +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + [[package]] name = "iana-time-zone" version = "0.1.50" @@ -422,6 +540,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "instant" version = "0.1.12" @@ -459,6 +587,15 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.3" @@ -482,9 +619,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.134" +version = "0.2.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" [[package]] name = "linux-raw-sys" @@ -507,6 +644,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + [[package]] name = "miniz_oxide" version = "0.5.4" @@ -528,6 +674,12 @@ dependencies = [ "libc", ] +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + [[package]] name = "num-integer" version = "0.1.45" @@ -547,6 +699,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "object" version = "0.29.0" @@ -562,6 +724,16 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +[[package]] +name = "os_pipe" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dceb7e43f59c35ee1548045b2c72945a5a3bb6ce6d6f07cdc13dc8f6bc4930a" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "os_str_bytes" version = "6.3.0" @@ -625,6 +797,29 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rayon" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b" +dependencies = [ + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -715,6 +910,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "semver" version = "1.0.14" @@ -782,6 +983,46 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + +[[package]] +name = "similar" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" + +[[package]] +name = "snapbox" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "827c00e91b15e2674d8a5270bae91f898693cbf9561cbb58d8eaa31974597293" +dependencies = [ + "concolor", + "content_inspector", + "dunce", + "filetime", + "libc", + "normalize-line-endings", + "os_pipe", + "similar", + "snapbox-macros", + "tempfile", + "wait-timeout", + "walkdir", + "winapi", + "yansi", +] + +[[package]] +name = "snapbox-macros" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "485e65c1203eb37244465e857d15a26d3a85a5410648ccb53b18bd44cb3a7336" + [[package]] name = "strsim" version = "0.10.0" @@ -881,6 +1122,28 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808b51e57d0ef8f71115d8f3a01e7d3750d01c79cac4b3eda910f4389fdf92fd" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1541ba70885967e662f69d31ab3aeca7b1aaecfcd58679590b893e9239c3646" +dependencies = [ + "combine", + "indexmap", + "itertools", + "serde", + "toml_datetime", +] + [[package]] name = "tracing" version = "0.1.36" @@ -923,6 +1186,22 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "trycmd" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e44a0ef010323b99f3b8fc13c6cac2e25df2e3bf5daacea5abef7bf5bd841bb8" +dependencies = [ + "glob", + "humantime", + "humantime-serde", + "rayon", + "serde", + "shlex", + "snapbox", + "toml_edit", +] + [[package]] name = "unicode-ident" version = "1.0.4" @@ -941,6 +1220,15 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.3.2" @@ -1137,8 +1425,15 @@ dependencies = [ "serde", "serde_json", "shell-words", + "tempfile", "toml", "walkdir", "which", "wildmatch", ] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/Cargo.toml b/Cargo.toml index c253bea71..aa3cda70c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,6 +63,8 @@ lto = true regex = "1" once_cell = "1.15" ignore = "0.4" +trycmd = "0.14.4" +snapbox = "0.4" [package.metadata.release] dev-version = false diff --git a/ci/test-rest-args.sh b/ci/test-rest-args.sh new file mode 100755 index 000000000..99afe394c --- /dev/null +++ b/ci/test-rest-args.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091,SC1090 + +# test that we correctly handle args after `--` + +set -x +set -eo pipefail + +ci_dir=$(dirname "${BASH_SOURCE[0]}") +ci_dir=$(realpath "${ci_dir}") +. "${ci_dir}"/shared.sh + +main() { + local td= + local parent= + local target=x86_64-unknown-dragonfly + + rustup toolchain add nightly + + retry cargo fetch + cargo build + export CROSS="${PROJECT_HOME}/target/debug/cross" + + td="$(mkcargotemp -d)" + parent=$(dirname "${td}") + pushd "${td}" + cargo init --bin --name "hello" . + + echo '[target.'"${target}"'] +build-std = true' > "${parent}/Cross.toml" + + export CROSS_CONTAINER_ENGINE="${CROSS_ENGINE}" + "${CROSS}" +nightly build --target "${target}" --verbose -- + + popd + rm -rf "${td}" +} + +main diff --git a/src/bin/cross.rs b/src/bin/cross.rs index 64536d296..d5fabb17e 100644 --- a/src/bin/cross.rs +++ b/src/bin/cross.rs @@ -1,15 +1,11 @@ #![deny(missing_debug_implementations, rust_2018_idioms)] -use std::{ - env, - io::{self, Write}, -}; +use std::env; +use std::io::{self, Write}; +use std::process::ExitStatus; -use cross::{ - cargo, cli, rustc, - shell::{self, Verbosity}, - OutputExt, Subcommand, -}; +use cross::shell::{MessageInfo, Verbosity}; +use cross::{cargo, cli, rustc, OutputExt, Subcommand}; pub fn main() -> cross::Result<()> { cross::install_panic_hook()?; @@ -18,27 +14,33 @@ pub fn main() -> cross::Result<()> { let target_list = rustc::target_list(&mut Verbosity::Quiet.into())?; let args = cli::parse(&target_list)?; let subcommand = args.subcommand; - let mut msg_info = shell::MessageInfo::create(args.verbose, args.quiet, args.color.as_deref())?; - let status = match cross::run(args, target_list, &mut msg_info)? { + // subcommands like `--version` ignore `--quiet`. + let quiet = match subcommand + .map(|x| !x.is_flag_subcommand()) + .unwrap_or_default() + { + true => args.quiet, + false => false, + }; + let mut msg_info = MessageInfo::create(args.verbose, quiet, args.color.as_deref())?; + // short-circuit to avoid external calls unless necessary + let status = match subcommand { + Some(Subcommand::Version) => { + msg_info.print(cross::version())?; + None + } + Some(sc) if sc.never_needs_docker() => None, + None => None, + Some(_) => cross::run(args, target_list, &mut msg_info)?, + }; + let status = match status { Some(status) => status, None => { // if we fallback to the host cargo, use the same invocation that was made to cross let argv: Vec = env::args().skip(1).collect(); msg_info.note("Falling back to `cargo` on the host.")?; match subcommand { - Some(Subcommand::List) => { - // this won't print in order if we have both stdout and stderr. - let out = cargo::run_and_get_output(&argv, &mut msg_info)?; - let stdout = out.stdout()?; - if out.status.success() && cli::is_subcommand_list(&stdout) { - cli::fmt_subcommands(&stdout, &mut msg_info)?; - } else { - // Not a list subcommand, which can happen with weird edge-cases. - print!("{}", stdout); - io::stdout().flush().expect("could not flush"); - } - out.status - } + Some(Subcommand::List) => fmt_list(&argv, &mut msg_info)?, _ => cargo::run(&argv, &mut msg_info)?, } } @@ -48,3 +50,17 @@ pub fn main() -> cross::Result<()> { .ok_or_else(|| eyre::Report::msg("Cargo process terminated by signal"))?; std::process::exit(code) } + +fn fmt_list(args: &[String], msg_info: &mut MessageInfo) -> cross::Result { + // this won't print in order if we have both stdout and stderr. + let out = cargo::run_and_get_output(args, msg_info)?; + let stdout = out.stdout()?; + if out.status.success() && cli::is_subcommand_list(&stdout) { + cli::fmt_subcommands(&stdout, msg_info)?; + } else { + // Not a list subcommand, which can happen with weird edge-cases. + print!("{}", stdout); + io::stdout().flush().expect("could not flush"); + } + Ok(out.status) +} diff --git a/src/cargo.rs b/src/cargo.rs index 7bda1846f..b85127fa1 100644 --- a/src/cargo.rs +++ b/src/cargo.rs @@ -21,13 +21,32 @@ pub enum Subcommand { Metadata, List, Clean, + Explain, + Version, + Help, } impl Subcommand { + #[must_use] + pub fn never_needs_docker(self) -> bool { + matches!( + self, + Subcommand::Other + | Subcommand::List + | Subcommand::Version + | Subcommand::Explain + | Subcommand::Help + ) + } + #[must_use] pub fn needs_docker(self, is_remote: bool) -> bool { match self { - Subcommand::Other | Subcommand::List => false, + Subcommand::Other + | Subcommand::List + | Subcommand::Version + | Subcommand::Explain + | Subcommand::Help => false, Subcommand::Clean if !is_remote => false, _ => true, } @@ -47,6 +66,31 @@ impl Subcommand { pub fn needs_target_in_command(self) -> bool { !matches!(self, Subcommand::Metadata) } + + #[must_use] + pub fn is_flag_subcommand(self) -> bool { + matches!( + self, + Subcommand::List | Subcommand::Version | Subcommand::Explain | Subcommand::Help + ) + } + + #[must_use] + pub fn set(self, other: Subcommand) -> Subcommand { + // choose the higher-priority subcommand when parsing two values + // the priority goes in the following order: + // 1. help > all + // 2. version > explain + // 3. explain > list + // 4. version > list + // 5. all others go left-to-right + match (self, other) { + (_, Subcommand::Help) => other, + (Subcommand::Version, Subcommand::Explain | Subcommand::List) => self, + (Subcommand::Explain, Subcommand::List) => self, + _ => self, + } + } } impl<'a> From<&'a str> for Subcommand { @@ -63,6 +107,9 @@ impl<'a> From<&'a str> for Subcommand { "clippy" => Subcommand::Clippy, "metadata" => Subcommand::Metadata, "--list" => Subcommand::List, + "--version" | "-V" => Subcommand::Version, + "--explain" => Subcommand::Explain, + "--help" => Subcommand::Help, _ => Subcommand::Other, } } diff --git a/src/cli.rs b/src/cli.rs index 4adbab983..42bd20334 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,27 +1,415 @@ -use std::env; use std::path::{Path, PathBuf}; +use std::{env, fmt}; use crate::cargo::Subcommand; use crate::errors::Result; use crate::file::{absolute_path, PathExt}; use crate::rustc::TargetList; -use crate::shell::{self, MessageInfo}; +use crate::shell::{MessageInfo, COLORS}; use crate::Target; +/// An option for a CLI flag value. +#[derive(Debug)] +pub enum FlagOption { + /// Value for flag was not provided. + Some(T), + /// Flag was not provided. + None, + /// Flag was provided multiple times. + Double, + /// Flag was not provided. + Missing, +} + +impl FlagOption { + pub fn is_some(&self) -> bool { + matches!(self, Self::Some(_)) + } + + pub fn is_none(&self) -> bool { + matches!(self, Self::Some(_)) + } + + /// If the flag is present multiple times, always set to `Double`. + pub fn set(&mut self, other: Self) { + *self = match self { + FlagOption::None => other, + _ => FlagOption::Double, + }; + } + + pub fn as_ref(&self) -> FlagOption<&T> { + match self { + FlagOption::Some(ref value) => FlagOption::Some(value), + FlagOption::None => FlagOption::None, + FlagOption::Double => FlagOption::Double, + FlagOption::Missing => FlagOption::Missing, + } + } + + pub fn as_mut(&mut self) -> FlagOption<&mut T> { + match self { + FlagOption::Some(ref mut value) => FlagOption::Some(value), + FlagOption::None => FlagOption::None, + FlagOption::Double => FlagOption::Double, + FlagOption::Missing => FlagOption::Missing, + } + } + + pub fn map U>(self, f: F) -> FlagOption { + match self { + FlagOption::Some(x) => FlagOption::Some(f(x)), + FlagOption::None => FlagOption::None, + FlagOption::Double => FlagOption::Double, + FlagOption::Missing => FlagOption::Missing, + } + } + + /// Consume self and report a fatal error message if missing or doubled up + pub fn to_option( + self, + arg: Message, + possible: Option<&[&str]>, + code: i32, + ) -> Option { + let mut msg_info = MessageInfo::default(); + match self { + FlagOption::Some(value) => Some(value), + FlagOption::None => None, + FlagOption::Double => msg_info.double_fatal_usage(arg, code), + FlagOption::Missing => msg_info.invalid_fatal_usage(arg, None, possible, code), + } + } +} + +impl FlagOption> { + pub fn flatten(self) -> FlagOption { + match self { + FlagOption::Some(value) => value.into(), + FlagOption::None => FlagOption::None, + FlagOption::Double => FlagOption::Double, + FlagOption::Missing => FlagOption::Missing, + } + } +} + +impl From> for FlagOption { + fn from(option: Option) -> Self { + match option { + Some(value) => FlagOption::Some(value), + None => FlagOption::None, + } + } +} + #[derive(Debug)] pub struct Args { pub cargo_args: Vec, pub rest_args: Vec, + pub features: Vec, pub subcommand: Option, pub channel: Option, pub target: Option, - pub features: Vec, pub target_dir: Option, pub manifest_path: Option, - pub version: bool, + pub color: Option, pub verbose: u8, pub quiet: bool, - pub color: Option, +} + +/// Internal implementation of args for the parsing, that handles +/// missing values for flags and flags which are provided multiple times. +#[derive(Debug)] +pub struct ArgsImpl { + pub cargo_args: Vec, + pub rest_args: Vec, + pub subcommand: Option, + pub channel: Option, + pub target: FlagOption, + pub target_dir: FlagOption, + pub manifest_path: FlagOption, + pub color: FlagOption, + pub features: FlagOption>, + pub verbose: u8, + pub quiet: bool, +} + +impl ArgsImpl { + pub const fn new() -> Self { + Self { + cargo_args: Vec::new(), + rest_args: Vec::new(), + subcommand: None, + channel: None, + target: FlagOption::None, + target_dir: FlagOption::None, + manifest_path: FlagOption::None, + color: FlagOption::None, + features: FlagOption::None, + verbose: 0, + quiet: false, + } + } + + fn into_args(self) -> Args { + // FIXME: cargo always goes right-to-left when printing missing + // or doubled arguments, however, we print in a fixed order. + Args { + cargo_args: self.cargo_args, + rest_args: self.rest_args, + subcommand: self.subcommand, + channel: self.channel, + target: self.target.to_option("--target ", None, 1), + target_dir: self + .target_dir + .to_option("--target-dir ", None, 1), + manifest_path: self + .manifest_path + .to_option("--manifest-path ", None, 1), + color: self.color.to_option("--color ", Some(COLORS), 1), + features: self + .features + .to_option("--features ", None, 1) + .unwrap_or_default(), + verbose: self.verbose, + quiet: self.quiet, + } + } +} + +impl Default for ArgsImpl { + fn default() -> Self { + Self::new() + } +} + +#[derive(Debug)] +enum ArgKind { + Next, + Equal, +} + +#[derive(Debug)] +struct Parser<'a, Iter: Iterator> { + result: ArgsImpl, + input: Iter, + target_list: &'a TargetList, +} + +impl<'a, Iter: Iterator> Parser<'a, Iter> { + fn parse_channel(arg: &str) -> Option { + match arg.split_at(1) { + ("+", channel) => Some(channel.to_owned()), + _ => None, + } + } + + fn parse_color(&mut self, arg: String, kind: ArgKind) -> Result<()> { + let color = match kind { + ArgKind::Next => self.parse_flag_next(arg, str_to_owned, identity)?, + ArgKind::Equal => { + FlagOption::Some(self.parse_flag_equal(arg, str_to_owned, identity)?) + } + }; + self.result.color.set(color); + + Ok(()) + } + + fn parse_manifest(&mut self, arg: String, kind: ArgKind) -> Result<()> { + let manifest_path = match kind { + ArgKind::Next => self + .parse_flag_next(arg, parse_manifest_path, store_manifest_path)? + .flatten(), + ArgKind::Equal => self + .parse_flag_equal(arg, parse_manifest_path, store_manifest_path)? + .into(), + }; + self.result.manifest_path.set(manifest_path); + + Ok(()) + } + + fn parse_target(&mut self, arg: String, kind: ArgKind) -> Result<()> { + let into = |t: &str| Ok(Target::from(t, self.target_list)); + let target = match kind { + ArgKind::Next => self.parse_flag_next(arg, into, identity)?, + ArgKind::Equal => FlagOption::Some(self.parse_flag_equal(arg, into, identity)?), + }; + self.result.target.set(target); + + Ok(()) + } + + fn parse_explain(&mut self, arg: String, kind: ArgKind) -> Result<()> { + let subcommand = Subcommand::Explain; + self.result.subcommand = Some(match self.result.subcommand { + Some(ref mut sc) => sc.set(subcommand), + None => subcommand, + }); + self.parse_flag_value(arg, kind) + } + + fn parse_target_dir(&mut self, arg: String, kind: ArgKind) -> Result<()> { + let target_dir = match kind { + ArgKind::Next => self.parse_flag_next(arg, parse_target_dir, store_target_dir)?, + ArgKind::Equal => { + FlagOption::Some(self.parse_flag_equal(arg, parse_target_dir, store_target_dir)?) + } + }; + + self.result.target_dir.set(target_dir); + + Ok(()) + } + + fn parse_features(&mut self, arg: String, kind: ArgKind) -> Result<()> { + let features = match kind { + ArgKind::Next => self.parse_flag_next(arg, str_to_owned, identity)?, + ArgKind::Equal => { + FlagOption::Some(self.parse_flag_equal(arg, str_to_owned, identity)?) + } + }; + + let features = features.map(|x| match x.is_empty() { + true => vec![], + false => vec![x], + }); + match (self.result.features.as_mut(), features) { + (FlagOption::Some(lhs), FlagOption::Some(mut rhs)) => lhs.append(&mut rhs), + (FlagOption::Some(_), _) => (), + (_, rhs) => self.result.features = rhs, + } + + Ok(()) + } + + fn parse_flag_value(&mut self, arg: String, kind: ArgKind) -> Result<()> { + self.result.cargo_args.push(arg); + match kind { + ArgKind::Next => match self.input.next() { + Some(next) if is_flag(&next) => self.parse_flag(next)?, + Some(next) => self.result.cargo_args.push(next), + None => (), + }, + ArgKind::Equal => (), + } + + Ok(()) + } + + fn parse_flag_next( + &mut self, + arg: String, + parse: impl Fn(&str) -> Result, + store: impl Fn(String) -> Result, + ) -> Result> { + // `--$flag $value` does not support flag-like values: `--target-dir --x` is invalid + self.result.cargo_args.push(arg); + match self.input.next() { + Some(next) if is_flag(&next) => { + self.parse_flag(next)?; + Ok(FlagOption::Missing) + } + Some(next) => { + let parsed = parse(&next)?; + self.result.cargo_args.push(store(next)?); + Ok(FlagOption::Some(parsed)) + } + None => Ok(FlagOption::Missing), + } + } + + fn parse_flag_equal( + &mut self, + arg: String, + parse: impl Fn(&str) -> Result, + store: impl Fn(String) -> Result, + ) -> Result { + // `--$flag=$value` supports flag-like values: `--target-dir=--x` is valid + let (first, second) = arg.split_once('=').expect("argument should contain `=`"); + let parsed = parse(second)?; + self.result + .cargo_args + .push(format!("{first}={}", store(second.to_owned())?)); + + Ok(parsed) + } + + fn parse_flag(&mut self, arg: String) -> Result<()> { + // we only consider flags accepted by `cargo` itself or `--help`, + // and we will ignore any subcommand value. this is fine + // since we will already have a subcommand, and since `--help` is + // a flag, we will never accidentally parse it as a value. + if arg == "--" { + self.result.rest_args.push(arg); + self.result.rest_args.extend(self.input.by_ref()); + } else if let v @ 1.. = is_verbose(arg.as_str()) { + self.result.verbose += v; + self.result.cargo_args.push(arg); + } else if matches!(arg.as_str(), "--quiet" | "-q") { + self.result.quiet = true; + self.result.cargo_args.push(arg); + } else if let Some(kind) = is_value_arg(&arg, "--color") { + self.parse_color(arg, kind)?; + } else if let Some(kind) = is_value_arg(&arg, "--manifest-path") { + self.parse_manifest(arg, kind)?; + } else if let Some(kind) = is_value_arg(&arg, "--target-dir") { + self.parse_target_dir(arg, kind)?; + } else if let Some(kind) = is_value_arg(&arg, "--explain") { + self.parse_explain(arg, kind)?; + } else if let Some(kind) = is_value_arg(&arg, "--config") { + self.parse_flag_value(arg, kind)?; + } else if let Some(kind) = is_value_arg(&arg, "-Z") { + self.parse_flag_value(arg, kind)?; + } else if let Some(kind) = is_value_arg(&arg, "--target") { + self.parse_target(arg, kind)?; + } else if let Some(kind) = is_value_arg(&arg, "--features") { + self.parse_features(arg, kind)?; + } else if matches!(arg.as_str(), "--help" | "--list" | "--version" | "-V") { + let subcommand = Subcommand::from(arg.as_str()); + self.result.subcommand = Some(match self.result.subcommand { + Some(ref mut sc) => sc.set(subcommand), + None => subcommand, + }); + self.result.cargo_args.push(arg); + } else { + self.result.cargo_args.push(arg); + } + + Ok(()) + } + + fn parse_value(&mut self, arg: String) -> Result<()> { + let subcommand = Subcommand::from(arg.as_ref()); + self.result.subcommand = Some(match self.result.subcommand { + Some(ref mut sc) => sc.set(subcommand), + None => subcommand, + }); + self.result.cargo_args.push(arg); + + Ok(()) + } + + fn parse_next(&mut self, arg: String) -> Result<()> { + if arg.is_empty() { + Ok(()) + } else if let Some(channel) = Self::parse_channel(&arg) { + self.result.channel = Some(channel); + Ok(()) + } else if is_flag(&arg) { + self.parse_flag(arg) + } else { + self.parse_value(arg) + } + } + + fn parse(&mut self) -> Result<()> { + while let Some(arg) = self.input.next() { + self.parse_next(arg)?; + } + Ok(()) + } } pub fn is_subcommand_list(stdout: &str) -> bool { @@ -55,7 +443,7 @@ pub fn fmt_subcommands(stdout: &str, msg_info: &mut MessageInfo) -> Result<()> { } if !host.is_empty() { msg_info.print("Host Commands:")?; - for line in &cross { + for line in &host { msg_info.print(line)?; } } @@ -82,11 +470,6 @@ fn is_verbose(arg: &str) -> u8 { } } -enum ArgKind { - Next, - Equal, -} - fn is_value_arg(arg: &str, field: &str) -> Option { if arg == field { Some(ArgKind::Next) @@ -101,35 +484,8 @@ fn is_value_arg(arg: &str, field: &str) -> Option { } } -fn parse_next_arg( - arg: String, - out: &mut Vec, - parse: impl Fn(&str) -> Result, - store_cb: impl Fn(String) -> Result, - iter: &mut impl Iterator, -) -> Result> { - out.push(arg); - match iter.next() { - Some(next) => { - let result = parse(&next)?; - out.push(store_cb(next)?); - Ok(Some(result)) - } - None => Ok(None), - } -} - -fn parse_equal_arg( - arg: String, - out: &mut Vec, - parse: impl Fn(&str) -> Result, - store_cb: impl Fn(String) -> Result, -) -> Result { - let (first, second) = arg.split_once('=').expect("argument should contain `=`"); - let result = parse(second)?; - out.push(format!("{first}={}", store_cb(second.to_owned())?)); - - Ok(result) +fn is_flag(arg: &str) -> bool { + arg.starts_with('-') } fn parse_manifest_path(path: &str) -> Result> { @@ -158,156 +514,16 @@ fn store_target_dir(_: String) -> Result { } pub fn parse(target_list: &TargetList) -> Result { - let mut channel = None; - let mut target = None; - let mut features = Vec::new(); - let mut manifest_path: Option = None; - let mut target_dir = None; - let mut sc = None; - let mut cargo_args: Vec = Vec::new(); - let mut rest_args: Vec = Vec::new(); - let mut version = false; - let mut quiet = false; - let mut verbose = 0; - let mut color = None; - - { - let mut args = env::args().skip(1); - while let Some(arg) = args.next() { - if arg.is_empty() { - continue; - } - if matches!(arg.as_str(), "--") { - rest_args.push(arg); - rest_args.extend(args.by_ref()); - } else if let v @ 1.. = is_verbose(arg.as_str()) { - verbose += v; - cargo_args.push(arg); - } else if matches!(arg.as_str(), "--version" | "-V") { - version = true; - } else if matches!(arg.as_str(), "--quiet" | "-q") { - quiet = true; - cargo_args.push(arg); - } else if let Some(kind) = is_value_arg(&arg, "--color") { - color = match kind { - ArgKind::Next => { - match parse_next_arg( - arg, - &mut cargo_args, - str_to_owned, - identity, - &mut args, - )? { - Some(c) => Some(c), - None => shell::invalid_color(None), - } - } - ArgKind::Equal => Some(parse_equal_arg( - arg, - &mut cargo_args, - str_to_owned, - identity, - )?), - }; - } else if let Some(kind) = is_value_arg(&arg, "--manifest-path") { - manifest_path = match kind { - ArgKind::Next => parse_next_arg( - arg, - &mut cargo_args, - parse_manifest_path, - store_manifest_path, - &mut args, - )? - .flatten(), - ArgKind::Equal => parse_equal_arg( - arg, - &mut cargo_args, - parse_manifest_path, - store_manifest_path, - )?, - }; - } else if let ("+", ch) = arg.split_at(1) { - channel = Some(ch.to_owned()); - } else if let Some(kind) = is_value_arg(&arg, "--target") { - let parse_target = |t: &str| Ok(Target::from(t, target_list)); - target = match kind { - ArgKind::Next => { - parse_next_arg(arg, &mut cargo_args, parse_target, identity, &mut args)? - } - ArgKind::Equal => Some(parse_equal_arg( - arg, - &mut cargo_args, - parse_target, - identity, - )?), - }; - } else if let Some(kind) = is_value_arg(&arg, "--features") { - match kind { - ArgKind::Next => { - let next = parse_next_arg( - arg, - &mut cargo_args, - str_to_owned, - identity, - &mut args, - )?; - if let Some(feature) = next { - features.push(feature); - } - } - ArgKind::Equal => { - features.push(parse_equal_arg( - arg, - &mut cargo_args, - str_to_owned, - identity, - )?); - } - } - } else if let Some(kind) = is_value_arg(&arg, "--target-dir") { - match kind { - ArgKind::Next => { - target_dir = parse_next_arg( - arg, - &mut cargo_args, - parse_target_dir, - store_target_dir, - &mut args, - )?; - } - ArgKind::Equal => { - target_dir = Some(parse_equal_arg( - arg, - &mut cargo_args, - parse_target_dir, - store_target_dir, - )?); - } - } - } else { - if (!arg.starts_with('-') || arg == "--list") && sc.is_none() { - sc = Some(Subcommand::from(arg.as_ref())); - } - - cargo_args.push(arg.clone()); - } - } - } + let result = ArgsImpl::new(); + let input = env::args().skip(1); + let mut parser = Parser { + result, + input, + target_list, + }; + parser.parse()?; - Ok(Args { - cargo_args, - rest_args, - subcommand: sc, - channel, - target, - features, - target_dir, - manifest_path, - version, - verbose, - quiet, - color, - }) + Ok(parser.result.into_args()) } #[cfg(test)] diff --git a/src/lib.rs b/src/lib.rs index e3db8d440..083eb5e61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,7 +58,7 @@ use rustc_version::Channel; use serde::{Deserialize, Serialize, Serializer}; pub use self::cargo::{cargo_command, cargo_metadata_with_args, CargoMetadata, Subcommand}; -use self::cross_toml::CrossToml; +pub use self::cross_toml::CrossToml; use self::errors::Context; use self::shell::{MessageInfo, Verbosity}; @@ -500,14 +500,6 @@ pub fn run( target_list: TargetList, msg_info: &mut MessageInfo, ) -> Result> { - if args.version && args.subcommand.is_none() { - msg_info.print(concat!( - "cross ", - env!("CARGO_PKG_VERSION"), - crate::commit_info!() - ))?; - } - let host_version_meta = rustc::version_meta()?; let cwd = std::env::current_dir()?; @@ -738,6 +730,10 @@ To override the toolchain mounted in the image, set `target.{}.image.toolchain = Ok(None) } +pub const fn version() -> &'static str { + concat!("cross ", env!("CARGO_PKG_VERSION"), crate::commit_info!()) +} + #[derive(PartialEq, Eq, Debug)] pub(crate) enum VersionMatch { Same, diff --git a/src/shell.rs b/src/shell.rs index 9834cff5c..0049ef990 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -305,19 +305,19 @@ impl MessageInfo { } } - pub fn fatal_usage( + pub fn invalid_fatal_usage( &mut self, arg: T, provided: Option<&str>, possible: Option<&[&str]>, code: i32, ) -> ! { - self.error_usage(arg, provided, possible) + self.invalid_error_usage(arg, provided, possible) .expect("could not display usage message"); std::process::exit(code); } - fn error_usage( + fn invalid_error_usage( &mut self, arg: T, provided: Option<&str>, @@ -361,7 +361,38 @@ impl MessageInfo { } _ => (), } - write_style!(stream, self, "Usage:\n"); + write_style!(stream, self, "\n"); + self.help(&mut stream)?; + stream.flush()?; + + Ok(()) + } + + pub fn double_fatal_usage(&mut self, arg: T, code: i32) -> ! { + self.double_error_usage(arg) + .expect("could not display usage message"); + std::process::exit(code); + } + + fn double_error_usage(&mut self, arg: T) -> Result<()> { + let mut stream = io::stderr(); + write_style!(stream, self, cross_prefix!("error"), bold, red); + write_style!(stream, self, ":", bold); + write_style!(stream, self, " The argument '"); + write_style!(stream, self, arg, yellow); + write_style!( + stream, + self, + format_args!("' was provided more than once, but cannot be used multiple times\n\n") + ); + self.help(&mut stream)?; + stream.flush()?; + + Ok(()) + } + + fn help(&mut self, stream: &mut (impl Stream + Write)) -> Result<()> { + write_style!(stream, self, "USAGE:\n"); write_style!( stream, self, @@ -372,8 +403,6 @@ impl MessageInfo { write_style!(stream, self, "--help", green); write_style!(stream, self, "\n"); - stream.flush()?; - Ok(()) } } @@ -412,9 +441,10 @@ pub fn cargo_envvar_bool(var: &str) -> Result { } } -pub fn invalid_color(provided: Option<&str>) -> ! { - let possible = ["auto", "always", "never"]; - MessageInfo::default().fatal_usage("--color ", provided, Some(&possible), 1); +pub const COLORS: &[&str] = &["auto", "always", "never"]; + +fn invalid_color(provided: Option<&str>) -> ! { + MessageInfo::default().invalid_fatal_usage("--color ", provided, Some(COLORS), 1); } fn get_color_choice(color: Option<&str>) -> Result { diff --git a/src/tests.rs b/src/tests.rs index 7a22907d4..304ac2ca5 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -138,7 +138,10 @@ release: {version} #[test] fn check_newlines() -> crate::Result<()> { - for file in walk_dir(get_cargo_workspace(), &[".git", "target"], |_| true) { + // tests contains the trycmd tests, which may have placeholders + for file in walk_dir(get_cargo_workspace(), &[".git", "target", "tests"], |_| { + true + }) { let file = file?; if !file.file_type().map_or(true, |f| f.is_file()) { continue; diff --git a/tests/cli.rs b/tests/cli.rs new file mode 100644 index 000000000..4a1b326cd --- /dev/null +++ b/tests/cli.rs @@ -0,0 +1,350 @@ +// simplifies addition of tests due to warning on last +// of needless borrow on last test in block. +#![allow(clippy::needless_borrow)] + +use std::path::{Path, PathBuf}; +use std::process::{Command, ExitStatus, Output, Stdio}; +use std::{fs, mem}; + +use cross::shell::MessageInfo; +use cross::{docker, CommandExt, ToUtf8}; +use snapbox::assert_matches; +use snapbox::cmd::cargo_bin; + +macro_rules! stdout_contains { + ($stdout:expr, $value:expr) => {{ + let stdout = $stdout; + assert!(stdout.contains($value), "unexpected stdout of {stdout}"); + }}; +} + +macro_rules! stderr_contains { + ($stderr:expr, $value:expr) => {{ + let stderr = $stderr; + assert!(stderr.contains($value), "unexpected stderr of {stderr}"); + }}; +} + +#[test] +#[ignore] +fn cli_tests() -> cross::Result<()> { + let target = default_target(); + + let cargo_version = run_success("cargo", &["--version"])?; + let cross_version = cross::version(); + + // FIXME: `[CARGOHELP]` doesn't currently work. + // https://github.com/assert-rs/trycmd/issues/170 + trycmd::TestCases::new() + .case("tests/cmd/*.toml") + .case("tests/cmd/*.md") + .env("CARGO_BUILD_TARGET", target) + .insert_var("[CARGOVERSION]", cargo_version.stdout)? + .insert_var("[CROSSVERSION]", cross_version)?; + + Ok(()) +} + +#[test] +#[ignore] +fn cargo_help_tests() -> cross::Result<()> { + let cargo_help = run_success("cargo", &["--help"])?; + let stderr = fallback()?; + + let short_help = run_success("cross", &["-h"])?; + assert_matches(&cargo_help.stdout, &short_help.stdout); + assert_matches(&stderr, &short_help.stderr); + + let long_help = run_success("cross", &["--help"])?; + assert_matches(&cargo_help.stdout, &long_help.stdout); + assert_matches(&stderr, &long_help.stderr); + + let list_help = run_success("cross", &["--list", "--help"])?; + assert_matches(&cargo_help.stdout, &list_help.stdout); + assert_matches(&stderr, &list_help.stderr); + + Ok(()) +} + +#[test] +#[ignore] +fn cargo_subcommand_help_tests() -> cross::Result<()> { + let flags = &["search", "--help"]; + let cargo_help = run_success("cargo", flags)?; + let stderr = fallback()?; + + let cross_help = run_success("cross", flags)?; + assert_matches(&cargo_help.stdout, &cross_help.stdout); + assert_matches(&stderr, &cross_help.stderr); + + Ok(()) +} + +#[test] +#[ignore] +fn cargo_verbose_version_tests() -> cross::Result<()> { + let flags = &["--verbose", "--version"]; + let cargo_version = run_success("cargo", flags)?; + let stderr = fallback()?; + + let cross_version = run_success("cross", flags)?; + let expected = format!( + "{}\n+ cargo --verbose --version\n{}", + cross::version(), + cargo_version.stdout, + ); + assert_matches(&expected, &cross_version.stdout); + assert_matches(&stderr, &cross_version.stderr); + + Ok(()) +} + +#[test] +#[ignore] +fn cross_command_test() -> cross::Result<()> { + let target = default_target(); + let flags = &["build", "--target", &target]; + + pull_default()?; + let test_url = "https://github.com/cross-rs/test-workspace"; + let tmpdir = temp_dir(None)?; + clone(test_url, &tmpdir)?; + + let mut msg_info = MessageInfo::default(); + msg_info.note(format_args!( + "running in-container integration tests: this may take a while" + ))?; + + let manifest_path = "--manifest-path=./workspace/Cargo.toml"; + let workspace_dir = format!("{tmpdir}/workspace"); + let debug_dir = format!("target/{target}/debug"); + + // check custom manifest path build + let libworkspace = format!("{debug_dir}/libtest_workspace.rlib"); + let build = Utf8Output::new( + run_cmd("cross", flags) + .arg(manifest_path) + .current_dir(&tmpdir) + .output()?, + )?; + assert_build_debug(&build); + check_file_exists(&workspace_dir, &libworkspace)?; + + let build = Utf8Output::new( + run_cmd("cross", flags) + .arg(manifest_path) + .arg("--quiet") + .current_dir(&tmpdir) + .output()?, + )?; + assert_build_debug_quiet(&build); + check_file_exists(&workspace_dir, &libworkspace)?; + + let build = Utf8Output::new( + run_cmd("cross", flags) + .arg(manifest_path) + .arg("--verbose") + .current_dir(&tmpdir) + .output()?, + )?; + assert_build_debug_verbose(&build); + check_file_exists(&workspace_dir, &libworkspace)?; + + // check within a workspace + let build = Utf8Output::new( + run_cmd("cross", flags) + .current_dir(&workspace_dir) + .output()?, + )?; + assert_build_debug(&build); + check_file_exists(&workspace_dir, &libworkspace)?; + + let build = Utf8Output::new( + run_cmd("cross", flags) + .args(["--features", "dependencies"]) + .current_dir(&workspace_dir) + .output()?, + )?; + assert_build_debug(&build); + check_file_exists(&workspace_dir, &format!("{debug_dir}/dependencies"))?; + + // check using a custom target directory + let build = Utf8Output::new( + run_cmd("cross", flags) + .args(["--features", "dependencies"]) + .args(["--target-dir", "custom"]) + .current_dir(&workspace_dir) + .output()?, + )?; + assert_build_debug(&build); + let custom_dir = format!("custom/{target}/debug"); + check_file_exists(&workspace_dir, &format!("{custom_dir}/dependencies"))?; + + let flags = &["run", "--target", &target]; + let binary_dir = format!("{workspace_dir}/binary"); + let run = Utf8Output::new(run_cmd("cross", flags).current_dir(&binary_dir).output()?)?; + stdout_contains!(run.stdout, "Hello from binary, binary/src/main.rs"); + stderr_contains!(run.stderr, BUILD_DEBUG); + check_file_exists(&workspace_dir, &format!("{debug_dir}/binary"))?; + + Ok(()) +} + +fn cargo_metadata(msg_info: &mut MessageInfo) -> cross::Result { + cross::cargo_metadata_with_args(Some(Path::new(env!("CARGO_MANIFEST_DIR"))), None, msg_info)? + .ok_or_else(|| eyre::eyre!("could not find cross workspace")) +} + +fn project_dir() -> cross::Result { + let mut msg_info = MessageInfo::default(); + Ok(cargo_metadata(&mut msg_info)?.workspace_root) +} + +fn fallback() -> cross::Result { + let path = project_dir()? + .join("tests") + .join("cmd") + .join("fallback.stderr"); + fs::read_to_string(path).map_err(Into::into) +} + +fn default_target() -> &'static str { + "x86_64-unknown-linux-gnu" +} + +fn default_image() -> String { + image(default_target(), "main") +} + +fn image(target: &str, tag: &str) -> String { + format!("{}/{target}:{tag}", docker::CROSS_IMAGE) +} + +fn temp_dir(parent: Option<&Path>) -> cross::Result { + let parent = match parent { + Some(parent) => parent.to_owned(), + None => project_dir()?.join("target").join("tmp"), + }; + fs::create_dir_all(&parent)?; + let dir = tempfile::TempDir::new_in(&parent)?; + let path = dir.path().to_owned(); + mem::drop(dir); + + fs::create_dir(&path)?; + Ok(path.to_utf8()?.to_owned()) +} + +fn check_file_exists(base: impl AsRef, relpath: &str) -> cross::Result<()> { + let path = base.as_ref().join(relpath); + match path.exists() { + true => Ok(()), + false => eyre::bail!("path \"{relpath}\" unexpectedly does not exist"), + } +} + +fn clone(url: &str, path: &str) -> cross::Result<()> { + let mut msg_info = MessageInfo::default(); + msg_info.note(format_args!( + "cloning repository \"{url}\": this may take a while" + ))?; + + let mut cmd = Command::new("git"); + cmd.args(["clone", "--depth", "1", "--recursive", url, path]); + if !msg_info.is_verbose() { + cmd.stderr(Stdio::null()); + } + let verbose = msg_info.is_verbose(); + cmd.run(&mut msg_info, !verbose).map_err(Into::into) +} + +fn pull(image: &str) -> cross::Result<()> { + let mut msg_info = MessageInfo::default(); + msg_info.note(format_args!( + "pulling container image \"{image}\": this may take a while" + ))?; + + let engine = docker::Engine::new(None, None, &mut msg_info)?; + let mut cmd = engine.subcommand("pull"); + cmd.arg(image); + if !msg_info.is_verbose() { + cmd.stderr(Stdio::null()); + } + let verbose = msg_info.is_verbose(); + cmd.run(&mut msg_info, !verbose).map_err(Into::into) +} + +fn pull_default() -> cross::Result<()> { + pull(&default_image()) +} + +fn run(bin: &str, args: &[&str]) -> cross::Result { + Utf8Output::new(run_cmd(bin, args).output()?) +} + +fn run_cmd(bin: &str, args: &[&str]) -> Command { + let mut cmd = match bin { + "cross" => Command::new(cargo_bin("cross")), + bin => Command::new(bin), + }; + cmd.args(args); + cmd +} + +fn run_success(bin: &str, args: &[&str]) -> cross::Result { + run_with_status(bin, args, Some(0)) +} + +fn run_with_status(bin: &str, args: &[&str], status: Option) -> cross::Result { + let output = run(bin, args)?; + if output.status.code() != status { + eyre::bail!("Unexpected exit status of {:?}", output.status); + } + Ok(output) +} + +const BUILD_DEBUG: &str = "Finished dev [unoptimized + debuginfo] target(s) in"; + +fn assert_build_debug(output: &Utf8Output) { + let stderr = &output.stderr; + assert_eq!("", &output.stdout); + stderr_contains!(stderr, BUILD_DEBUG); +} + +fn assert_build_debug_verbose(output: &Utf8Output) { + let stdout = &output.stdout; + let stderr = &output.stderr; + stdout_contains!(stdout, "+ cargo metadata --format-version 1"); + stdout_contains!(stdout, "+ rustc --print sysroot"); + stdout_contains!(stdout, "+ rustup toolchain list"); + stdout_contains!(stdout, "+ rustup target list --toolchain"); + stdout_contains!(stdout, "+ rustup component list --toolchain"); + stdout_contains!(stdout, "CARGO_HOME"); + stdout_contains!(stdout, "CARGO_TARGET_DIR"); + stdout_contains!(stdout, "CROSS_RUSTC_MAJOR_VERSION"); + stdout_contains!(stdout, "CROSS_RUSTC_MINOR_VERSION"); + stdout_contains!(stdout, "CROSS_RUSTC_PATCH_VERSION"); + stdout_contains!(stdout, "CROSS_RUST_SYSROOT"); + stderr_contains!(stderr, BUILD_DEBUG); +} + +fn assert_build_debug_quiet(output: &Utf8Output) { + assert_eq!("", &output.stdout); + assert_eq!("", &output.stderr); +} + +#[allow(dead_code)] +struct Utf8Output { + status: ExitStatus, + stdout: String, + stderr: String, +} + +impl Utf8Output { + fn new(output: Output) -> cross::Result { + Ok(Self { + status: output.status, + stdout: String::from_utf8(output.stdout)?, + stderr: String::from_utf8(output.stderr)?, + }) + } +} diff --git a/tests/cmd/build-no-color.stderr b/tests/cmd/build-no-color.stderr new file mode 120000 index 000000000..2e4a147cc --- /dev/null +++ b/tests/cmd/build-no-color.stderr @@ -0,0 +1 @@ +no-color.stderr \ No newline at end of file diff --git a/tests/cmd/build-no-color.stdout b/tests/cmd/build-no-color.stdout new file mode 120000 index 000000000..f4d41694b --- /dev/null +++ b/tests/cmd/build-no-color.stdout @@ -0,0 +1 @@ +no-color.stdout \ No newline at end of file diff --git a/tests/cmd/build-no-color.toml b/tests/cmd/build-no-color.toml new file mode 100644 index 000000000..513314db3 --- /dev/null +++ b/tests/cmd/build-no-color.toml @@ -0,0 +1,3 @@ +bin.name = "cross" +args = ["build", "--color"] +status = "failed" diff --git a/tests/cmd/color.toml b/tests/cmd/color.toml new file mode 100644 index 000000000..987d4c35b --- /dev/null +++ b/tests/cmd/color.toml @@ -0,0 +1,2 @@ +bin.name = "cross" +args = ["--color", "never"] diff --git a/tests/cmd/double-color-value-both.stderr b/tests/cmd/double-color-value-both.stderr new file mode 120000 index 000000000..fffed56f4 --- /dev/null +++ b/tests/cmd/double-color-value-both.stderr @@ -0,0 +1 @@ +double-color.stderr \ No newline at end of file diff --git a/tests/cmd/double-color-value-both.stdout b/tests/cmd/double-color-value-both.stdout new file mode 120000 index 000000000..a1658a9bd --- /dev/null +++ b/tests/cmd/double-color-value-both.stdout @@ -0,0 +1 @@ +double-color.stdout \ No newline at end of file diff --git a/tests/cmd/double-color-value-both.toml b/tests/cmd/double-color-value-both.toml new file mode 100644 index 000000000..0bd74f17c --- /dev/null +++ b/tests/cmd/double-color-value-both.toml @@ -0,0 +1,3 @@ +bin.name = "cross" +args = ["--color", "never", "--color", "never"] +status = "failed" diff --git a/tests/cmd/double-color-value-one.stderr b/tests/cmd/double-color-value-one.stderr new file mode 120000 index 000000000..fffed56f4 --- /dev/null +++ b/tests/cmd/double-color-value-one.stderr @@ -0,0 +1 @@ +double-color.stderr \ No newline at end of file diff --git a/tests/cmd/double-color-value-one.stdout b/tests/cmd/double-color-value-one.stdout new file mode 120000 index 000000000..a1658a9bd --- /dev/null +++ b/tests/cmd/double-color-value-one.stdout @@ -0,0 +1 @@ +double-color.stdout \ No newline at end of file diff --git a/tests/cmd/double-color-value-one.toml b/tests/cmd/double-color-value-one.toml new file mode 100644 index 000000000..cc98278f1 --- /dev/null +++ b/tests/cmd/double-color-value-one.toml @@ -0,0 +1,3 @@ +bin.name = "cross" +args = ["--color", "never", "--color"] +status = "failed" diff --git a/tests/cmd/double-color.stderr b/tests/cmd/double-color.stderr new file mode 100644 index 000000000..3c7997238 --- /dev/null +++ b/tests/cmd/double-color.stderr @@ -0,0 +1,6 @@ +[cross] error: The argument '--color ' was provided more than once, but cannot be used multiple times + +USAGE: + cross [+toolchain] [OPTIONS] [SUBCOMMAND] + +For more information try --help diff --git a/tests/cmd/double-color.stdout b/tests/cmd/double-color.stdout new file mode 100644 index 000000000..e69de29bb diff --git a/tests/cmd/double-color.toml b/tests/cmd/double-color.toml new file mode 100644 index 000000000..f9b8f4995 --- /dev/null +++ b/tests/cmd/double-color.toml @@ -0,0 +1,3 @@ +bin.name = "cross" +args = ["--color", "--color"] +status = "failed" diff --git a/tests/cmd/explain-list.stderr b/tests/cmd/explain-list.stderr new file mode 120000 index 000000000..caa640b95 --- /dev/null +++ b/tests/cmd/explain-list.stderr @@ -0,0 +1 @@ +explain.stderr \ No newline at end of file diff --git a/tests/cmd/explain-list.stdout b/tests/cmd/explain-list.stdout new file mode 120000 index 000000000..9a2d8407a --- /dev/null +++ b/tests/cmd/explain-list.stdout @@ -0,0 +1 @@ +explain.stdout \ No newline at end of file diff --git a/tests/cmd/explain-list.toml b/tests/cmd/explain-list.toml new file mode 100644 index 000000000..253a051ac --- /dev/null +++ b/tests/cmd/explain-list.toml @@ -0,0 +1,2 @@ +bin.name = "cross" +args = ["--explain", "E0004", "--list"] diff --git a/tests/cmd/explain.stderr b/tests/cmd/explain.stderr new file mode 120000 index 000000000..92f6c496b --- /dev/null +++ b/tests/cmd/explain.stderr @@ -0,0 +1 @@ +fallback.stderr \ No newline at end of file diff --git a/tests/cmd/explain.stdout b/tests/cmd/explain.stdout new file mode 100644 index 000000000..10b34703d --- /dev/null +++ b/tests/cmd/explain.stdout @@ -0,0 +1,5 @@ +This error indicates that the compiler cannot guarantee a matching pattern for +one or more possible inputs to a match expression. Guaranteed matches are +required in order to assign values to match expressions, or alternatively, +determine the flow of execution. +... \ No newline at end of file diff --git a/tests/cmd/explain.toml b/tests/cmd/explain.toml new file mode 100644 index 000000000..04c2c2898 --- /dev/null +++ b/tests/cmd/explain.toml @@ -0,0 +1,2 @@ +bin.name = "cross" +args = ["--explain", "E0004"] diff --git a/tests/cmd/fake-engine.stderr b/tests/cmd/fake-engine.stderr new file mode 100644 index 000000000..acfaf3273 --- /dev/null +++ b/tests/cmd/fake-engine.stderr @@ -0,0 +1,10 @@ +Error: +[..]no container engine found[..] + +Location: +[..]src/docker/engine.rs[..]:[..] + +[..]Suggestion[..]: is docker or podman installed?[..] + +Backtrace omitted. Run with RUST_BACKTRACE=1 environment variable to display it. +Run with RUST_BACKTRACE=full to include source snippets. diff --git a/tests/cmd/fake-engine.stdout b/tests/cmd/fake-engine.stdout new file mode 100644 index 000000000..e69de29bb diff --git a/tests/cmd/fake-engine.toml b/tests/cmd/fake-engine.toml new file mode 100644 index 000000000..66e6e4c2b --- /dev/null +++ b/tests/cmd/fake-engine.toml @@ -0,0 +1,7 @@ +# NOTE: `stderr` has a lot of inline replacements for bash escape codes +bin.name = "cross" +args = ["build", "--color", "never"] +status = "failed" + +[env.add] +CROSS_CONTAINER_ENGINE = "cross-fake-engine" diff --git a/tests/cmd/fake-image.stderr b/tests/cmd/fake-image.stderr new file mode 100644 index 000000000..07457685b --- /dev/null +++ b/tests/cmd/fake-image.stderr @@ -0,0 +1,2 @@ +[..]ghcr.io/cross-rs/fake-image:main[..] +... \ No newline at end of file diff --git a/tests/cmd/fake-image.stdout b/tests/cmd/fake-image.stdout new file mode 100644 index 000000000..e69de29bb diff --git a/tests/cmd/fake-image.toml b/tests/cmd/fake-image.toml new file mode 100644 index 000000000..ef2a4440e --- /dev/null +++ b/tests/cmd/fake-image.toml @@ -0,0 +1,6 @@ +bin.name = "cross" +args = ["build"] +status = "failed" + +[env.add] +CROSS_TARGET_X86_64_UNKNOWN_LINUX_GNU_IMAGE = "ghcr.io/cross-rs/fake-image:main" diff --git a/tests/cmd/fake-toolchain.stderr b/tests/cmd/fake-toolchain.stderr new file mode 100644 index 000000000..5f4eaa61f --- /dev/null +++ b/tests/cmd/fake-toolchain.stderr @@ -0,0 +1,3 @@ +[cross] warning: unable to get metadata for package +[cross] note: Falling back to `cargo` on the host. +error: toolchain 'cross-fake-toolchain' is not installed diff --git a/tests/cmd/fake-toolchain.stdout b/tests/cmd/fake-toolchain.stdout new file mode 100644 index 000000000..e69de29bb diff --git a/tests/cmd/fake-toolchain.toml b/tests/cmd/fake-toolchain.toml new file mode 100644 index 000000000..da264faae --- /dev/null +++ b/tests/cmd/fake-toolchain.toml @@ -0,0 +1,3 @@ +bin.name = "cross" +args = ["+cross-fake-toolchain", "build"] +status = "failed" diff --git a/tests/cmd/fallback.stderr b/tests/cmd/fallback.stderr new file mode 100644 index 000000000..44d5eb020 --- /dev/null +++ b/tests/cmd/fallback.stderr @@ -0,0 +1 @@ +[cross] note: Falling back to `cargo` on the host. diff --git a/tests/cmd/help.stderr b/tests/cmd/help.stderr new file mode 120000 index 000000000..92f6c496b --- /dev/null +++ b/tests/cmd/help.stderr @@ -0,0 +1 @@ +fallback.stderr \ No newline at end of file diff --git a/tests/cmd/help.stdout b/tests/cmd/help.stdout new file mode 100644 index 000000000..db333431e --- /dev/null +++ b/tests/cmd/help.stdout @@ -0,0 +1,13 @@ +Rust's package manager + +USAGE: + cargo [+toolchain] [OPTIONS] [SUBCOMMAND] + +OPTIONS: +... + +Some common cargo commands are (see all commands with --list): +... + +See 'cargo help ' for more information on a specific command. + diff --git a/tests/cmd/invalid-color.toml b/tests/cmd/invalid-color.toml new file mode 100644 index 000000000..6c1230d0e --- /dev/null +++ b/tests/cmd/invalid-color.toml @@ -0,0 +1,3 @@ +bin.name = "cross" +args = ["--color", "invalid"] +status = "failed" diff --git a/tests/cmd/list-explain.stderr b/tests/cmd/list-explain.stderr new file mode 120000 index 000000000..caa640b95 --- /dev/null +++ b/tests/cmd/list-explain.stderr @@ -0,0 +1 @@ +explain.stderr \ No newline at end of file diff --git a/tests/cmd/list-explain.stdout b/tests/cmd/list-explain.stdout new file mode 120000 index 000000000..9a2d8407a --- /dev/null +++ b/tests/cmd/list-explain.stdout @@ -0,0 +1 @@ +explain.stdout \ No newline at end of file diff --git a/tests/cmd/list-explain.toml b/tests/cmd/list-explain.toml new file mode 100644 index 000000000..1fbf38d14 --- /dev/null +++ b/tests/cmd/list-explain.toml @@ -0,0 +1,2 @@ +bin.name = "cross" +args = ["--list", "--explain", "E0004"] diff --git a/tests/cmd/list.stderr b/tests/cmd/list.stderr new file mode 120000 index 000000000..92f6c496b --- /dev/null +++ b/tests/cmd/list.stderr @@ -0,0 +1 @@ +fallback.stderr \ No newline at end of file diff --git a/tests/cmd/list.stdout b/tests/cmd/list.stdout new file mode 100644 index 000000000..31c12bcf9 --- /dev/null +++ b/tests/cmd/list.stdout @@ -0,0 +1,19 @@ +Cross Commands: + b [..] alias: build + bench [..] Execute all benchmarks of a local package + build [..] Compile a local package and all of its dependencies + c [..] alias: check + check [..] Check a local package and all of its dependencies for errors + clean [..] Remove artifacts that cargo has generated in the past + clippy [..] Checks a package to catch common mistakes and improve your Rust code. + doc [..] Build a package's documentation + metadata [..] Output the resolved dependencies of a package, the concrete used versions including overrides, in machine-readable format + r [..] alias: run + run [..] Run a binary or example of the local package + rustc [..] Compile a package, and pass extra options to the compiler + t [..] alias: test + test [..] Execute all unit and integration tests and build examples of a local package +Host Commands: +... + add Add dependencies to a Cargo.toml manifest file +... \ No newline at end of file diff --git a/tests/cmd/list.toml b/tests/cmd/list.toml new file mode 100644 index 000000000..cb70283c8 --- /dev/null +++ b/tests/cmd/list.toml @@ -0,0 +1,2 @@ +bin.name = "cross" +args = ["--list"] diff --git a/tests/cmd/no-args.stderr b/tests/cmd/no-args.stderr new file mode 120000 index 000000000..5d749eaff --- /dev/null +++ b/tests/cmd/no-args.stderr @@ -0,0 +1 @@ +help.stderr \ No newline at end of file diff --git a/tests/cmd/no-args.stdout b/tests/cmd/no-args.stdout new file mode 120000 index 000000000..2e5a3ed59 --- /dev/null +++ b/tests/cmd/no-args.stdout @@ -0,0 +1 @@ +help.stdout \ No newline at end of file diff --git a/tests/cmd/no-args.toml b/tests/cmd/no-args.toml new file mode 100644 index 000000000..4a78068ca --- /dev/null +++ b/tests/cmd/no-args.toml @@ -0,0 +1 @@ +bin.name = "cross" diff --git a/tests/cmd/no-color.stderr b/tests/cmd/no-color.stderr new file mode 100644 index 000000000..449d5a274 --- /dev/null +++ b/tests/cmd/no-color.stderr @@ -0,0 +1,7 @@ +[cross] error: The argument '--color ' requires a value but none was supplied + [possible values: auto, always, never] + +USAGE: + cross [+toolchain] [OPTIONS] [SUBCOMMAND] + +For more information try --help diff --git a/tests/cmd/no-color.stdout b/tests/cmd/no-color.stdout new file mode 100644 index 000000000..e69de29bb diff --git a/tests/cmd/no-color.toml b/tests/cmd/no-color.toml new file mode 100644 index 000000000..793a9cd55 --- /dev/null +++ b/tests/cmd/no-color.toml @@ -0,0 +1,3 @@ +bin.name = "cross" +args = ["--color"] +status = "failed" diff --git a/tests/cmd/outside-package-target-dir.stderr b/tests/cmd/outside-package-target-dir.stderr new file mode 120000 index 000000000..63f537bcc --- /dev/null +++ b/tests/cmd/outside-package-target-dir.stderr @@ -0,0 +1 @@ +outside-package.stderr \ No newline at end of file diff --git a/tests/cmd/outside-package-target-dir.stdout b/tests/cmd/outside-package-target-dir.stdout new file mode 120000 index 000000000..aa7dc1ae6 --- /dev/null +++ b/tests/cmd/outside-package-target-dir.stdout @@ -0,0 +1 @@ +outside-package.stdout \ No newline at end of file diff --git a/tests/cmd/outside-package-target-dir.toml b/tests/cmd/outside-package-target-dir.toml new file mode 100644 index 000000000..0cc9d3dc9 --- /dev/null +++ b/tests/cmd/outside-package-target-dir.toml @@ -0,0 +1,4 @@ +bin.name = "cross" +args = ["build", "--target-dir", "[ROOT]/xx"] +status = "failed" +fs.cwd = "/" diff --git a/tests/cmd/outside-package.stderr b/tests/cmd/outside-package.stderr new file mode 100644 index 000000000..dcebdabc1 --- /dev/null +++ b/tests/cmd/outside-package.stderr @@ -0,0 +1,3 @@ +[cross] warning: unable to get metadata for package +[cross] note: Falling back to `cargo` on the host. +error: could not find `Cargo.toml` in `[CWD]` or any parent directory diff --git a/tests/cmd/outside-package.stdout b/tests/cmd/outside-package.stdout new file mode 100644 index 000000000..e69de29bb diff --git a/tests/cmd/outside-package.toml b/tests/cmd/outside-package.toml new file mode 100644 index 000000000..9a38c27ea --- /dev/null +++ b/tests/cmd/outside-package.toml @@ -0,0 +1,4 @@ +bin.name = "cross" +args = ["build"] +status = "failed" +fs.cwd = "/" diff --git a/tests/cmd/quiet-verbose-envvar-both.stderr b/tests/cmd/quiet-verbose-envvar-both.stderr new file mode 120000 index 000000000..09c252b5e --- /dev/null +++ b/tests/cmd/quiet-verbose-envvar-both.stderr @@ -0,0 +1 @@ +quiet-verbose.stderr \ No newline at end of file diff --git a/tests/cmd/quiet-verbose-envvar-both.stdout b/tests/cmd/quiet-verbose-envvar-both.stdout new file mode 120000 index 000000000..b51c1b7af --- /dev/null +++ b/tests/cmd/quiet-verbose-envvar-both.stdout @@ -0,0 +1 @@ +quiet-verbose.stdout \ No newline at end of file diff --git a/tests/cmd/quiet-verbose-envvar-both.toml b/tests/cmd/quiet-verbose-envvar-both.toml new file mode 100644 index 000000000..4f267dfdc --- /dev/null +++ b/tests/cmd/quiet-verbose-envvar-both.toml @@ -0,0 +1,7 @@ +bin.name = "cross" +args = ["build"] +status = "failed" + +[env.add] +CARGO_TERM_VERBOSE = "true" +CARGO_TERM_QUIET = "true" diff --git a/tests/cmd/quiet-verbose-envvar-one.stderr b/tests/cmd/quiet-verbose-envvar-one.stderr new file mode 100644 index 000000000..e69de29bb diff --git a/tests/cmd/quiet-verbose-envvar-one.stdout b/tests/cmd/quiet-verbose-envvar-one.stdout new file mode 100644 index 000000000..e69de29bb diff --git a/tests/cmd/quiet-verbose-envvar-one.toml b/tests/cmd/quiet-verbose-envvar-one.toml new file mode 100644 index 000000000..3cf1913e7 --- /dev/null +++ b/tests/cmd/quiet-verbose-envvar-one.toml @@ -0,0 +1,5 @@ +bin.name = "cross" +args = ["search", "cross-this-does-not-exist", "--quiet"] + +[env.add] +CARGO_TERM_VERBOSE = "true" diff --git a/tests/cmd/quiet-verbose-list.stderr b/tests/cmd/quiet-verbose-list.stderr new file mode 120000 index 000000000..5d749eaff --- /dev/null +++ b/tests/cmd/quiet-verbose-list.stderr @@ -0,0 +1 @@ +help.stderr \ No newline at end of file diff --git a/tests/cmd/quiet-verbose-list.stdout b/tests/cmd/quiet-verbose-list.stdout new file mode 100644 index 000000000..8841dd591 --- /dev/null +++ b/tests/cmd/quiet-verbose-list.stdout @@ -0,0 +1,5 @@ ++ cargo --quiet --verbose --list +Cross Commands: +... +Host Commands: +... diff --git a/tests/cmd/quiet-verbose-list.toml b/tests/cmd/quiet-verbose-list.toml new file mode 100644 index 000000000..bd2540607 --- /dev/null +++ b/tests/cmd/quiet-verbose-list.toml @@ -0,0 +1,2 @@ +bin.name = "cross" +args = ["--quiet", "--verbose", "--list"] diff --git a/tests/cmd/quiet-verbose-no-command.stderr b/tests/cmd/quiet-verbose-no-command.stderr new file mode 120000 index 000000000..5d749eaff --- /dev/null +++ b/tests/cmd/quiet-verbose-no-command.stderr @@ -0,0 +1 @@ +help.stderr \ No newline at end of file diff --git a/tests/cmd/quiet-verbose-no-command.stdout b/tests/cmd/quiet-verbose-no-command.stdout new file mode 100644 index 000000000..07a3160e1 --- /dev/null +++ b/tests/cmd/quiet-verbose-no-command.stdout @@ -0,0 +1,6 @@ ++ cargo --quiet --verbose +Rust's package manager + +USAGE: + cargo [+toolchain] [OPTIONS] [SUBCOMMAND] +... \ No newline at end of file diff --git a/tests/cmd/quiet-verbose-no-command.toml b/tests/cmd/quiet-verbose-no-command.toml new file mode 100644 index 000000000..3274d0cb2 --- /dev/null +++ b/tests/cmd/quiet-verbose-no-command.toml @@ -0,0 +1,2 @@ +bin.name = "cross" +args = ["--quiet", "--verbose"] diff --git a/tests/cmd/quiet-verbose.stderr b/tests/cmd/quiet-verbose.stderr new file mode 100644 index 000000000..33a85ec8c --- /dev/null +++ b/tests/cmd/quiet-verbose.stderr @@ -0,0 +1 @@ +[cross] error: cannot set both --verbose and --quiet diff --git a/tests/cmd/quiet-verbose.stdout b/tests/cmd/quiet-verbose.stdout new file mode 100644 index 000000000..e69de29bb diff --git a/tests/cmd/quiet-verbose.toml b/tests/cmd/quiet-verbose.toml new file mode 100644 index 000000000..1bd3d1398 --- /dev/null +++ b/tests/cmd/quiet-verbose.toml @@ -0,0 +1,3 @@ +bin.name = "cross" +args = ["build", "--quiet", "--verbose"] +status = "failed" diff --git a/tests/cmd/short-version.stderr b/tests/cmd/short-version.stderr new file mode 120000 index 000000000..92f6c496b --- /dev/null +++ b/tests/cmd/short-version.stderr @@ -0,0 +1 @@ +fallback.stderr \ No newline at end of file diff --git a/tests/cmd/short-version.stdout b/tests/cmd/short-version.stdout new file mode 120000 index 000000000..6ba3df8f6 --- /dev/null +++ b/tests/cmd/short-version.stdout @@ -0,0 +1 @@ +version.stdout \ No newline at end of file diff --git a/tests/cmd/short-version.toml b/tests/cmd/short-version.toml new file mode 100644 index 000000000..f4d5d134c --- /dev/null +++ b/tests/cmd/short-version.toml @@ -0,0 +1,2 @@ +bin.name = "cross" +args = ["-V"] diff --git a/tests/cmd/version.stderr b/tests/cmd/version.stderr new file mode 120000 index 000000000..92f6c496b --- /dev/null +++ b/tests/cmd/version.stderr @@ -0,0 +1 @@ +fallback.stderr \ No newline at end of file diff --git a/tests/cmd/version.stdout b/tests/cmd/version.stdout new file mode 100644 index 000000000..7bdbd5c04 --- /dev/null +++ b/tests/cmd/version.stdout @@ -0,0 +1,2 @@ +[CROSSVERSION] +[CARGOVERSION] \ No newline at end of file diff --git a/tests/cmd/version.toml b/tests/cmd/version.toml new file mode 100644 index 000000000..c3911a329 --- /dev/null +++ b/tests/cmd/version.toml @@ -0,0 +1,2 @@ +bin.name = "cross" +args = ["--version"] diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 040352e47..02dda1d30 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -23,3 +23,4 @@ once_cell = "1.15" semver = "1" chrono = "0.4" wildmatch = "2.1.1" +tempfile = "3.3.0" diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 195bcb7d7..3a751b841 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -8,6 +8,7 @@ pub mod crosstool; pub mod hooks; pub mod install_git_hooks; pub mod target_info; +pub mod temp; pub mod util; use ci::CiJob; @@ -23,6 +24,7 @@ use self::crosstool::ConfigureCrosstool; use self::hooks::{Check, Test}; use self::install_git_hooks::InstallGitHooks; use self::target_info::TargetInfo; +use self::temp::MakeTempDir; #[derive(Parser, Debug)] #[clap(version, about, long_about = None)] @@ -65,6 +67,8 @@ enum Commands { ValidateChangelog(ValidateChangelog), /// Code generation Codegen(Codegen), + /// Create temporary directory + MakeTempDir(MakeTempDir), } fn is_toolchain(toolchain: &str) -> cross::Result { @@ -131,6 +135,7 @@ pub fn main() -> cross::Result<()> { changelog::validate_changelog(args, &mut msg_info)?; } Commands::Codegen(args) => codegen::codegen(args)?, + Commands::MakeTempDir(args) => temp::make_temp_dir(args)?, } Ok(()) diff --git a/xtask/src/temp.rs b/xtask/src/temp.rs new file mode 100644 index 000000000..f2bfba74f --- /dev/null +++ b/xtask/src/temp.rs @@ -0,0 +1,35 @@ +use std::path::{Path, PathBuf}; +use std::{fs, mem}; + +use crate::util; +use clap::Args; +use cross::shell::MessageInfo; +use cross::ToUtf8; + +/// Create and print a temporary directory to stdout. +#[derive(Args, Debug)] +pub struct MakeTempDir { + /// `tmp` to create the temporary directory inside. + /// Defaults to `"${target_dir}/tmp"`. + tmpdir: Option, +} + +pub fn make_temp_dir(MakeTempDir { tmpdir }: MakeTempDir) -> cross::Result<()> { + let mut msg_info = MessageInfo::create(0, false, None)?; + let dir = temp_dir(tmpdir.as_deref(), &mut msg_info)?; + msg_info.print(dir.to_utf8()?) +} + +pub fn temp_dir(parent: Option<&Path>, msg_info: &mut MessageInfo) -> cross::Result { + let parent = match parent { + Some(parent) => parent.to_owned(), + None => util::project_dir(msg_info)?.join("target").join("tmp"), + }; + fs::create_dir_all(&parent)?; + let dir = tempfile::TempDir::new_in(&parent)?; + let path = dir.path().to_owned(); + mem::drop(dir); + + fs::create_dir(&path)?; + Ok(path) +} From 103220369ae7c68b0599899d7ef3088c1150dfbe Mon Sep 17 00:00:00 2001 From: Alex Huszagh Date: Mon, 28 Nov 2022 21:41:22 -0600 Subject: [PATCH 2/6] Remove temp. --- Cargo.lock | 3 +-- xtask/Cargo.toml | 1 - xtask/src/main.rs | 5 ----- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e8619b50..207edab91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -705,7 +705,7 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", ] @@ -1425,7 +1425,6 @@ dependencies = [ "serde", "serde_json", "shell-words", - "tempfile", "toml", "walkdir", "which", diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 02dda1d30..040352e47 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -23,4 +23,3 @@ once_cell = "1.15" semver = "1" chrono = "0.4" wildmatch = "2.1.1" -tempfile = "3.3.0" diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 3a751b841..195bcb7d7 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -8,7 +8,6 @@ pub mod crosstool; pub mod hooks; pub mod install_git_hooks; pub mod target_info; -pub mod temp; pub mod util; use ci::CiJob; @@ -24,7 +23,6 @@ use self::crosstool::ConfigureCrosstool; use self::hooks::{Check, Test}; use self::install_git_hooks::InstallGitHooks; use self::target_info::TargetInfo; -use self::temp::MakeTempDir; #[derive(Parser, Debug)] #[clap(version, about, long_about = None)] @@ -67,8 +65,6 @@ enum Commands { ValidateChangelog(ValidateChangelog), /// Code generation Codegen(Codegen), - /// Create temporary directory - MakeTempDir(MakeTempDir), } fn is_toolchain(toolchain: &str) -> cross::Result { @@ -135,7 +131,6 @@ pub fn main() -> cross::Result<()> { changelog::validate_changelog(args, &mut msg_info)?; } Commands::Codegen(args) => codegen::codegen(args)?, - Commands::MakeTempDir(args) => temp::make_temp_dir(args)?, } Ok(()) From d77a44d7879540d0a468280a840b9e6696685991 Mon Sep 17 00:00:00 2001 From: Alex Huszagh Date: Mon, 28 Nov 2022 22:05:05 -0600 Subject: [PATCH 3/6] Add wildcard matching for walkdir. --- Cargo.lock | 1 + Cargo.toml | 1 + src/cli.rs | 1 - src/tests.rs | 21 +++++++++++---------- tests/cli.rs | 2 -- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 207edab91..5a0923e4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,6 +266,7 @@ dependencies = [ "trycmd", "walkdir", "which", + "wildmatch", "winapi", ] diff --git a/Cargo.toml b/Cargo.toml index aa3cda70c..4c705f0c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,6 +65,7 @@ once_cell = "1.15" ignore = "0.4" trycmd = "0.14.4" snapbox = "0.4" +wildmatch = "2.1.1" [package.metadata.release] dev-version = false diff --git a/src/cli.rs b/src/cli.rs index 42bd20334..00822718f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -30,7 +30,6 @@ impl FlagOption { matches!(self, Self::Some(_)) } - /// If the flag is present multiple times, always set to `Double`. pub fn set(&mut self, other: Self) { *self = match self { FlagOption::None => other, diff --git a/src/tests.rs b/src/tests.rs index 304ac2ca5..e7142a03c 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,9 +1,6 @@ mod toml; -use std::{ - ffi::OsStr, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; use once_cell::sync::OnceCell; use rustc_version::VersionMeta; @@ -27,15 +24,15 @@ pub fn get_cargo_workspace() -> &'static Path { pub fn walk_dir<'a>( root: &'_ Path, - skip: &'static [impl AsRef + Send + Sync + 'a], + skip: &'static [&str], ext: impl for<'s> Fn(Option<&'s std::ffi::OsStr>) -> bool + Sync + Send + 'static, ) -> impl Iterator> { ignore::WalkBuilder::new(root) .filter_entry(move |e| { + let file_name = e.file_name().to_utf8().unwrap(); if skip .iter() - .map(|s| -> &std::ffi::OsStr { s.as_ref() }) - .any(|dir| e.file_name() == dir) + .any(|t| wildmatch::WildMatch::new(t).matches(file_name)) { return false; } else if e.file_type().map_or(false, |f| f.is_dir()) { @@ -139,9 +136,13 @@ release: {version} #[test] fn check_newlines() -> crate::Result<()> { // tests contains the trycmd tests, which may have placeholders - for file in walk_dir(get_cargo_workspace(), &[".git", "target", "tests"], |_| { - true - }) { + // these folders are not checked due to them containing binary + // files or files which should not have trailing newlines + for file in walk_dir( + get_cargo_workspace(), + &[".git", "target", "*.stdout", "*.stderr"], + |_| true, + ) { let file = file?; if !file.file_type().map_or(true, |f| f.is_file()) { continue; diff --git a/tests/cli.rs b/tests/cli.rs index 4a1b326cd..8beb4fe0d 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -1,5 +1,3 @@ -// simplifies addition of tests due to warning on last -// of needless borrow on last test in block. #![allow(clippy::needless_borrow)] use std::path::{Path, PathBuf}; From d61ef8fd602527ac34dc1a1e88c162e55656e7cb Mon Sep 17 00:00:00 2001 From: Alex Huszagh Date: Tue, 29 Nov 2022 07:21:26 -0600 Subject: [PATCH 4/6] Remove unnecessary lifetime. --- src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests.rs b/src/tests.rs index e7142a03c..d2ca9c21f 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -22,7 +22,7 @@ pub fn get_cargo_workspace() -> &'static Path { }) } -pub fn walk_dir<'a>( +pub fn walk_dir( root: &'_ Path, skip: &'static [&str], ext: impl for<'s> Fn(Option<&'s std::ffi::OsStr>) -> bool + Sync + Send + 'static, From ae09aa46db5840a1609b3855e7ee8b4f173c80c8 Mon Sep 17 00:00:00 2001 From: Alex Huszagh Date: Tue, 29 Nov 2022 08:01:36 -0600 Subject: [PATCH 5/6] Better support for color codes. --- tests/README.md | 1 + tests/cmd/color.stderr | 1 + tests/cmd/color.stdout | 1 + tests/cmd/double-color.stderr | 4 ++-- tests/cmd/fake-toolchain.stderr | 6 +++--- tests/cmd/fallback.stderr | 2 +- tests/cmd/help-flag-color.stderr | 1 + tests/cmd/help-flag-color.stdout | 1 + tests/cmd/help-flag-color.toml | 2 ++ tests/cmd/help-subcommand-color.stderr | 7 +++++++ tests/cmd/help-subcommand-color.stdout | 0 tests/cmd/help-subcommand-color.toml | 3 +++ tests/cmd/no-color.stderr | 6 +++--- tests/cmd/outside-package.stderr | 6 +++--- tests/cmd/quiet-verbose.stderr | 2 +- 15 files changed, 30 insertions(+), 13 deletions(-) create mode 100644 tests/README.md create mode 120000 tests/cmd/color.stderr create mode 120000 tests/cmd/color.stdout create mode 120000 tests/cmd/help-flag-color.stderr create mode 120000 tests/cmd/help-flag-color.stdout create mode 100644 tests/cmd/help-flag-color.toml create mode 100644 tests/cmd/help-subcommand-color.stderr create mode 100644 tests/cmd/help-subcommand-color.stdout create mode 100644 tests/cmd/help-subcommand-color.toml diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 000000000..72e3530f6 --- /dev/null +++ b/tests/README.md @@ -0,0 +1 @@ +A lot of these outputs contain `[..][cross][..]` or similar for commands, which exists so that way optional colors codes are processed correctly both if they are whether they are present or not. diff --git a/tests/cmd/color.stderr b/tests/cmd/color.stderr new file mode 120000 index 000000000..92f6c496b --- /dev/null +++ b/tests/cmd/color.stderr @@ -0,0 +1 @@ +fallback.stderr \ No newline at end of file diff --git a/tests/cmd/color.stdout b/tests/cmd/color.stdout new file mode 120000 index 000000000..2e5a3ed59 --- /dev/null +++ b/tests/cmd/color.stdout @@ -0,0 +1 @@ +help.stdout \ No newline at end of file diff --git a/tests/cmd/double-color.stderr b/tests/cmd/double-color.stderr index 3c7997238..7ee7d02a3 100644 --- a/tests/cmd/double-color.stderr +++ b/tests/cmd/double-color.stderr @@ -1,6 +1,6 @@ -[cross] error: The argument '--color ' was provided more than once, but cannot be used multiple times +[..][cross] error[..]: The argument '[..]--color [..]' was provided more than once, but cannot be used multiple times USAGE: cross [+toolchain] [OPTIONS] [SUBCOMMAND] -For more information try --help +For more information try [..]--help[..] diff --git a/tests/cmd/fake-toolchain.stderr b/tests/cmd/fake-toolchain.stderr index 5f4eaa61f..2b390da2c 100644 --- a/tests/cmd/fake-toolchain.stderr +++ b/tests/cmd/fake-toolchain.stderr @@ -1,3 +1,3 @@ -[cross] warning: unable to get metadata for package -[cross] note: Falling back to `cargo` on the host. -error: toolchain 'cross-fake-toolchain' is not installed +[..][cross] warning[..]: unable to get metadata for package +[..][cross] note[..]: Falling back to `cargo` on the host. +[..]error:[..] toolchain 'cross-fake-toolchain' is not installed diff --git a/tests/cmd/fallback.stderr b/tests/cmd/fallback.stderr index 44d5eb020..651b03acd 100644 --- a/tests/cmd/fallback.stderr +++ b/tests/cmd/fallback.stderr @@ -1 +1 @@ -[cross] note: Falling back to `cargo` on the host. +[..][cross] note[..]: Falling back to `cargo` on the host. diff --git a/tests/cmd/help-flag-color.stderr b/tests/cmd/help-flag-color.stderr new file mode 120000 index 000000000..92f6c496b --- /dev/null +++ b/tests/cmd/help-flag-color.stderr @@ -0,0 +1 @@ +fallback.stderr \ No newline at end of file diff --git a/tests/cmd/help-flag-color.stdout b/tests/cmd/help-flag-color.stdout new file mode 120000 index 000000000..2e5a3ed59 --- /dev/null +++ b/tests/cmd/help-flag-color.stdout @@ -0,0 +1 @@ +help.stdout \ No newline at end of file diff --git a/tests/cmd/help-flag-color.toml b/tests/cmd/help-flag-color.toml new file mode 100644 index 000000000..1647d215b --- /dev/null +++ b/tests/cmd/help-flag-color.toml @@ -0,0 +1,2 @@ +bin.name = "cross" +args = ["--help", "--color", "never"] diff --git a/tests/cmd/help-subcommand-color.stderr b/tests/cmd/help-subcommand-color.stderr new file mode 100644 index 000000000..52cbbe7c6 --- /dev/null +++ b/tests/cmd/help-subcommand-color.stderr @@ -0,0 +1,7 @@ +[..][cross] note[..]: Falling back to `cargo` on the host. +[..]error:[..] The subcommand [..]'--color'[..] wasn't recognized + +USAGE: + cargo + +For more information try [..]--help[..] \ No newline at end of file diff --git a/tests/cmd/help-subcommand-color.stdout b/tests/cmd/help-subcommand-color.stdout new file mode 100644 index 000000000..e69de29bb diff --git a/tests/cmd/help-subcommand-color.toml b/tests/cmd/help-subcommand-color.toml new file mode 100644 index 000000000..16f5647ef --- /dev/null +++ b/tests/cmd/help-subcommand-color.toml @@ -0,0 +1,3 @@ +bin.name = "cross" +args = ["help", "--color", "never"] +status = "failed" diff --git a/tests/cmd/no-color.stderr b/tests/cmd/no-color.stderr index 449d5a274..cca798fdf 100644 --- a/tests/cmd/no-color.stderr +++ b/tests/cmd/no-color.stderr @@ -1,7 +1,7 @@ -[cross] error: The argument '--color ' requires a value but none was supplied - [possible values: auto, always, never] +[..][cross] error[..]: The argument '[..]--color [..]' requires a value but none was supplied + [possible values: [..]auto[..], [..]always[..], [..]never[..]] USAGE: cross [+toolchain] [OPTIONS] [SUBCOMMAND] -For more information try --help +For more information try [..]--help[..] diff --git a/tests/cmd/outside-package.stderr b/tests/cmd/outside-package.stderr index dcebdabc1..9b7cedb5e 100644 --- a/tests/cmd/outside-package.stderr +++ b/tests/cmd/outside-package.stderr @@ -1,3 +1,3 @@ -[cross] warning: unable to get metadata for package -[cross] note: Falling back to `cargo` on the host. -error: could not find `Cargo.toml` in `[CWD]` or any parent directory +[..][cross] warning[..]: unable to get metadata for package +[..][cross] note[..]: Falling back to `cargo` on the host. +[..]error:[..] could not find `Cargo.toml` in `[CWD]` or any parent directory diff --git a/tests/cmd/quiet-verbose.stderr b/tests/cmd/quiet-verbose.stderr index 33a85ec8c..41f85cc71 100644 --- a/tests/cmd/quiet-verbose.stderr +++ b/tests/cmd/quiet-verbose.stderr @@ -1 +1 @@ -[cross] error: cannot set both --verbose and --quiet +[..][cross] error[..]: cannot set both --verbose and --quiet From c8326d588deb26941aabed7f69bb11958279e2d3 Mon Sep 17 00:00:00 2001 From: Alex Huszagh Date: Sun, 4 Dec 2022 10:26:32 -0600 Subject: [PATCH 6/6] Fix testing with ANSI color codes. --- tests/cli.rs | 10 ++++++++-- tests/cmd/fake-toolchain.stderr | 6 +++--- tests/cmd/fallback.stderr | 2 +- tests/cmd/outside-package.stderr | 6 +++--- tests/cmd/quiet-verbose.stderr | 2 +- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/tests/cli.rs b/tests/cli.rs index 8beb4fe0d..bec0251cd 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -6,20 +6,26 @@ use std::{fs, mem}; use cross::shell::MessageInfo; use cross::{docker, CommandExt, ToUtf8}; +use once_cell::sync::Lazy; +use regex::Regex; use snapbox::assert_matches; use snapbox::cmd::cargo_bin; +pub static ANSI_COLOR_CODES: Lazy = Lazy::new(|| Regex::new(r"\x1b\[[0-9;]*m").unwrap()); + macro_rules! stdout_contains { ($stdout:expr, $value:expr) => {{ let stdout = $stdout; - assert!(stdout.contains($value), "unexpected stdout of {stdout}"); + let replaced = ANSI_COLOR_CODES.replace_all(&stdout, ""); + assert!(replaced.contains($value), "unexpected stdout of {stdout:?}"); }}; } macro_rules! stderr_contains { ($stderr:expr, $value:expr) => {{ let stderr = $stderr; - assert!(stderr.contains($value), "unexpected stderr of {stderr}"); + let replaced = ANSI_COLOR_CODES.replace_all(&stderr, ""); + assert!(replaced.contains($value), "unexpected stderr of {stderr:?}"); }}; } diff --git a/tests/cmd/fake-toolchain.stderr b/tests/cmd/fake-toolchain.stderr index 2b390da2c..f44d1460a 100644 --- a/tests/cmd/fake-toolchain.stderr +++ b/tests/cmd/fake-toolchain.stderr @@ -1,3 +1,3 @@ -[..][cross] warning[..]: unable to get metadata for package -[..][cross] note[..]: Falling back to `cargo` on the host. -[..]error:[..] toolchain 'cross-fake-toolchain' is not installed +[..][cross] warning[..]:[..] unable to get metadata for package +[..][cross] note[..]:[..] Falling back to `cargo` on the host. +[..]error[..]:[..] toolchain 'cross-fake-toolchain' is not installed diff --git a/tests/cmd/fallback.stderr b/tests/cmd/fallback.stderr index 651b03acd..838551921 100644 --- a/tests/cmd/fallback.stderr +++ b/tests/cmd/fallback.stderr @@ -1 +1 @@ -[..][cross] note[..]: Falling back to `cargo` on the host. +[..][cross] note[..]:[..] Falling back to `cargo` on the host. diff --git a/tests/cmd/outside-package.stderr b/tests/cmd/outside-package.stderr index 9b7cedb5e..6d78e45fb 100644 --- a/tests/cmd/outside-package.stderr +++ b/tests/cmd/outside-package.stderr @@ -1,3 +1,3 @@ -[..][cross] warning[..]: unable to get metadata for package -[..][cross] note[..]: Falling back to `cargo` on the host. -[..]error:[..] could not find `Cargo.toml` in `[CWD]` or any parent directory +[..][cross] warning[..]:[..] unable to get metadata for package +[..][cross] note[..]:[..] Falling back to `cargo` on the host. +[..]error[..]:[..] could not find `Cargo.toml` in `[CWD]` or any parent directory diff --git a/tests/cmd/quiet-verbose.stderr b/tests/cmd/quiet-verbose.stderr index 41f85cc71..946f61a0c 100644 --- a/tests/cmd/quiet-verbose.stderr +++ b/tests/cmd/quiet-verbose.stderr @@ -1 +1 @@ -[..][cross] error[..]: cannot set both --verbose and --quiet +[..][cross] error[..]:[..] cannot set both --verbose and --quiet