From cff1226a6b6a5b2171d800f5e41d46fe7b544bdf Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Mon, 25 May 2020 17:19:14 -0500 Subject: [PATCH 01/12] Maint/CI ~ add GitHub-Actions CI (aka GHA) --- .github/workflows/CICD.yml | 287 +++++++++++++++++++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 .github/workflows/CICD.yml diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml new file mode 100644 index 0000000000..c63f353c6c --- /dev/null +++ b/.github/workflows/CICD.yml @@ -0,0 +1,287 @@ +name: CICD + +env: + PROJECT_NAME: bat + PROJECT_DESC: "A `cat` clone with wings" + PROJECT_AUTH: "sharkdp" + RUST_MIN_SRV: "1.40.0" + +on: [push, pull_request] + +jobs: + min_version: + name: MinSRV # Minimum supported rust version + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Install `rust` toolchain (v${{ env.RUST_MIN_SRV }}) + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.RUST_MIN_SRV }} + default: true + profile: minimal # minimal component installation (ie, no documentation) + - name: Test + uses: actions-rs/cargo@v1 + with: + command: test + + build: + name: Build + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + # { os, target, cargo-options, features, use-cross, toolchain } + - { os: ubuntu-latest , target: arm-unknown-linux-gnueabihf , use-cross: use-cross } + - { os: ubuntu-18.04 , target: aarch64-unknown-linux-gnu , use-cross: use-cross } + - { os: ubuntu-18.04 , target: i586-unknown-linux-gnu , use-cross: use-cross } + - { os: ubuntu-18.04 , target: i686-unknown-linux-gnu , use-cross: use-cross } + - { os: ubuntu-18.04 , target: i686-unknown-linux-musl , use-cross: use-cross } + - { os: ubuntu-18.04 , target: x86_64-unknown-linux-gnu , use-cross: use-cross } + - { os: ubuntu-18.04 , target: x86_64-unknown-linux-musl , use-cross: use-cross } + - { os: ubuntu-16.04 , target: x86_64-unknown-linux-gnu , use-cross: use-cross } + - { os: macos-latest , target: x86_64-apple-darwin } + - { os: windows-latest , target: i686-pc-windows-gnu } + - { os: windows-latest , target: i686-pc-windows-msvc } + - { os: windows-latest , target: x86_64-pc-windows-gnu } ## note: requires rust >= 1.43.0 to link correctly + - { os: windows-latest , target: x86_64-pc-windows-msvc } + steps: + - uses: actions/checkout@v1 + - name: Install any prerequisites + shell: bash + run: | + case ${{ matrix.job.target }} in + arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;; + aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;; + esac + - name: Initialize workflow variables + id: vars + shell: bash + run: | + # toolchain + TOOLCHAIN="stable" ## default to "stable" toolchain + # * specify alternate TOOLCHAIN for *-pc-windows-gnu targets; gnu targets on Windows are broken for the standard *-pc-windows-msvc toolchain (refs: , , ) + case ${{ matrix.job.target }} in *-pc-windows-gnu) TOOLCHAIN="stable-${{ matrix.job.target }}" ;; esac; + # * use requested TOOLCHAIN if specified + if [ -n "${{ matrix.job.toolchain }}" ]; then TOOLCHAIN="${{ matrix.job.toolchain }}" ; fi + echo set-output name=TOOLCHAIN::${TOOLCHAIN} + echo ::set-output name=TOOLCHAIN::${TOOLCHAIN} + # staging directory + STAGING='_staging' + echo set-output name=STAGING::${STAGING} + echo ::set-output name=STAGING::${STAGING} + # determine EXE suffix + EXE_suffix="" ; case ${{ matrix.job.target }} in *-pc-windows-*) EXE_suffix=".exe" ;; esac; + echo set-output name=EXE_suffix::${EXE_suffix} + echo ::set-output name=EXE_suffix::${EXE_suffix} + # parse commit reference info + REF_NAME=${GITHUB_REF#refs/*/} + unset REF_BRANCH ; case ${GITHUB_REF} in refs/heads/*) REF_BRANCH=${GITHUB_REF#refs/heads/} ;; esac; + unset REF_TAG ; case ${GITHUB_REF} in refs/tags/*) REF_TAG=${GITHUB_REF#refs/tags/} ;; esac; + REF_SHAS=${GITHUB_SHA:0:8} + echo set-output name=REF_NAME::${REF_NAME} + echo set-output name=REF_BRANCH::${REF_BRANCH} + echo set-output name=REF_TAG::${REF_TAG} + echo set-output name=REF_SHAS::${REF_SHAS} + echo ::set-output name=REF_NAME::${REF_NAME} + echo ::set-output name=REF_BRANCH::${REF_BRANCH} + echo ::set-output name=REF_TAG::${REF_TAG} + echo ::set-output name=REF_SHAS::${REF_SHAS} + # parse target + unset TARGET_ARCH ; case ${{ matrix.job.target }} in arm-unknown-linux-gnueabihf) TARGET_ARCH=arm ;; i686-*) TARGET_ARCH=i686 ;; x86_64-*) TARGET_ARCH=x86_64 ;; esac; + echo set-output name=TARGET_ARCH::${TARGET_ARCH} + echo ::set-output name=TARGET_ARCH::${TARGET_ARCH} + unset TARGET_OS ; case ${{ matrix.job.target }} in *-linux-*) TARGET_OS=linux ;; *-apple-*) TARGET_OS=macos ;; *-windows-*) TARGET_OS=windows ;; esac; + echo set-output name=TARGET_OS::${TARGET_OS} + echo ::set-output name=TARGET_OS::${TARGET_OS} + # package name + PKG_suffix=".tar.gz" ; case ${{ matrix.job.target }} in *-pc-windows-*) PKG_suffix=".zip" ;; esac; + PKG_BASENAME=${PROJECT_NAME}-${REF_TAG:-$REF_SHAS}-${{ matrix.job.target }} + PKG_NAME=${PKG_BASENAME}${PKG_suffix} + echo set-output name=PKG_suffix::${PKG_suffix} + echo set-output name=PKG_BASENAME::${PKG_BASENAME} + echo set-output name=PKG_NAME::${PKG_NAME} + echo ::set-output name=PKG_suffix::${PKG_suffix} + echo ::set-output name=PKG_BASENAME::${PKG_BASENAME} + echo ::set-output name=PKG_NAME::${PKG_NAME} + # deployable tag? (ie, leading "vM" or "M"; M == version number) + unset DEPLOY ; if [[ $REF_TAG =~ ^[vV]?[0-9].* ]]; then DEPLOY='true' ; fi + echo set-output name=DEPLOY::${DEPLOY:-/false} + echo ::set-output name=DEPLOY::${DEPLOY} + # DPKG architecture? + unset DPKG_ARCH ; case ${{ matrix.job.target }} in i686-*-linux-*) DPKG_ARCH=i686 ;; x86_64-*-linux-*) DPKG_ARCH=amd64 ;; esac; + echo set-output name=DPKG_ARCH::${DPKG_ARCH} + echo ::set-output name=DPKG_ARCH::${DPKG_ARCH} + # DPKG version? + unset DPKG_VERSION ; if [[ $REF_TAG =~ ^[vV]?[0-9].* ]]; then DPKG_VERSION=${REF_TAG/#[vV]/} ; fi + echo set-output name=DPKG_VERSION::${DPKG_VERSION} + echo ::set-output name=DPKG_VERSION::${DPKG_VERSION} + # DPKG base name/conflicts? + DPKG_BASENAME=${PROJECT_NAME} + DPKG_CONFLICTS=${PROJECT_NAME}-musl + case ${{ matrix.job.target }} in *-musl) DPKG_BASENAME=${PROJECT_NAME}-musl ; DPKG_CONFLICTS=${PROJECT_NAME} ;; esac; + echo set-output name=DPKG_BASENAME::${DPKG_BASENAME} + echo set-output name=DPKG_CONFLICTS::${DPKG_CONFLICTS} + echo ::set-output name=DPKG_BASENAME::${DPKG_BASENAME} + echo ::set-output name=DPKG_CONFLICTS::${DPKG_CONFLICTS} + # DPKG name + unset DPKG_NAME; + if [[ -n $DPKG_ARCH && -n $DPKG_VERSION ]]; then DPKG_NAME="${DPKG_BASENAME}_${DPKG_VERSION}_${DPKG_ARCH}.deb" ; fi + echo set-output name=DPKG_NAME::${DPKG_NAME} + echo ::set-output name=DPKG_NAME::${DPKG_NAME} + # target-specific options + # * CARGO_USE_CROSS (truthy) + CARGO_USE_CROSS='true' ; case '${{ matrix.job.use-cross }}' in ''|0|f|false|n|no) unset CARGO_USE_CROSS ;; esac; + echo set-output name=CARGO_USE_CROSS::${CARGO_USE_CROSS:-/false} + echo ::set-output name=CARGO_USE_CROSS::${CARGO_USE_CROSS} + # # * `arm` cannot be tested on ubuntu-* hosts (b/c testing is currently primarily done via comparison of target outputs with built-in outputs and the `arm` target is not executable on the host) + JOB_DO_TESTING="true" + case ${{ matrix.job.target }} in arm-*) unset JOB_DO_TESTING ;; esac; + echo set-output name=JOB_DO_TESTING::${JOB_DO_TESTING:-/false} + echo ::set-output name=JOB_DO_TESTING::${JOB_DO_TESTING} + # # * test only library units tests and binary for arm-type targets + unset CARGO_TEST_OPTIONS + unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--lib --bin ${PROJECT_NAME}" ;; esac; + echo set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS} + echo ::set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS} + # * executable for `strip`? + STRIP="strip" ; case ${{ matrix.job.target }} in arm-unknown-linux-gnueabihf) STRIP="arm-linux-gnueabihf-strip" ;; aarch64-unknown-linux-gnu) STRIP="aarch64-linux-gnu-strip" ;; *-pc-windows-msvc) STRIP="" ;; esac; + echo set-output name=STRIP::${STRIP} + echo ::set-output name=STRIP::${STRIP} + - name: Create all needed build/work directories + shell: bash + run: | + mkdir -p '${{ steps.vars.outputs.STAGING }}' + mkdir -p '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}' + mkdir -p '${{ steps.vars.outputs.STAGING }}/dpkg' + - name: rust toolchain ~ install + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ steps.vars.outputs.TOOLCHAIN }} + target: ${{ matrix.job.target }} + override: true + profile: minimal # minimal component installation (ie, no documentation) + - name: Info + shell: bash + run: | + gcc --version || true + rustup -V + rustup toolchain list + rustup default + cargo -V + rustc -V + - name: Build + uses: actions-rs/cargo@v1 + with: + use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }} + command: build + args: --release --target=${{ matrix.job.target }} ${{ matrix.job.cargo-options }} + - name: Test + uses: actions-rs/cargo@v1 + with: + use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }} + command: test + args: --target=${{ matrix.job.target }} ${{ steps.vars.outputs.CARGO_TEST_OPTIONS}} ${{ matrix.job.cargo-options }} + - name: Archive executable artifacts + uses: actions/upload-artifact@master + with: + name: ${{ env.PROJECT_NAME }}-${{ matrix.job.target }} + path: target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }} + - name: Package + shell: bash + run: | + # binary + cp 'target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}' '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/' + # `strip` binary (if needed) + if [ -n "${{ steps.vars.outputs.STRIP }}" ]; then "${{ steps.vars.outputs.STRIP }}" '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}' ; fi + # README and LICENSE + # * spell-checker:ignore EADME ICENSE + (shopt -s nullglob; for f in [R]"EADME"{,.*}; do cp $f '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/' ; done) + (shopt -s nullglob; for f in [L]"ICENSE"{-*,}{,.*}; do cp $f '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/' ; done) + # base compressed package + pushd '${{ steps.vars.outputs.STAGING }}/' >/dev/null + case ${{ matrix.job.target }} in + *-pc-windows-*) 7z -y a '${{ steps.vars.outputs.PKG_NAME }}' '${{ steps.vars.outputs.PKG_BASENAME }}'/* | tail -2 ;; + *) tar czf '${{ steps.vars.outputs.PKG_NAME }}' '${{ steps.vars.outputs.PKG_BASENAME }}'/* ;; + esac; + popd >/dev/null + # dpkg + if [ -n "${{ steps.vars.outputs.DPKG_NAME }}" ]; then + DPKG_DIR="${{ steps.vars.outputs.STAGING }}/dpkg" + # binary + install -Dm755 'target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}' "${DPKG_DIR}/usr/bin/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}" + if [ -n "${{ steps.vars.outputs.STRIP }}" ]; then "${{ steps.vars.outputs.STRIP }}" "${DPKG_DIR}/usr/bin/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}" ; fi + # README and LICENSE + (shopt -s nullglob; for f in [R]"EADME"{,.*}; do install -Dm644 "$f" "${DPKG_DIR}/usr/share/doc/${{ env.PROJECT_NAME }}/$f" ; done) + (shopt -s nullglob; for f in [L]"ICENSE"{-*,}{,.*}; do install -Dm644 "$f" "${DPKG_DIR}/usr/share/doc/${{ env.PROJECT_NAME }}/$f" ; done) + # control file + mkdir -p "${DPKG_DIR}/DEBIAN" + printf "Package: ${{ steps.vars.outputs.DPKG_BASENAME }}\nVersion: ${{ steps.vars.outputs.DPKG_VERSION }}\nSection: utils\nPriority: optional\nMaintainer: ${{ env.PROJECT_AUTH }}\nArchitecture: ${{ steps.vars.outputs.DPKG_ARCH }}\nProvides: ${{ env.PROJECT_NAME }}\nConflicts: ${{ steps.vars.outputs.DPKG_CONFLICTS }}\nDescription: ${{ env.PROJECT_DESC }}\n" > "${DPKG_DIR}/DEBIAN/control" + # build dpkg + fakeroot dpkg-deb --build "${DPKG_DIR}" "${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.DPKG_NAME }}" + fi + - name: Publish + uses: softprops/action-gh-release@v1 + if: steps.vars.outputs.DEPLOY + with: + files: | + ${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_NAME }} + ${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.DPKG_NAME }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + coverage: + name: Code Coverage + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: true + matrix: + # job: [ { os: ubuntu-latest }, { os: macos-latest }, { os: windows-latest } ] + job: + - { os: ubuntu-latest , toolchain: nightly-2020-04-29 } + - { os: macos-latest , toolchain: nightly-2020-04-29 } + - { os: windows-latest , toolchain: nightly-2020-04-29-x86_64-pc-windows-gnu } + steps: + - uses: actions/checkout@v1 + - name: Initialize workflow variables + id: vars + shell: bash + run: | + # toolchain + TOOLCHAIN="nightly" ## default to "nightly" toolchain + # * use requested TOOLCHAIN if specified + if [ -n "${{ matrix.job.toolchain }}" ]; then TOOLCHAIN="${{ matrix.job.toolchain }}" ; fi + # * use requested TOOLCHAIN if specified + if [ -n "${{ matrix.job.toolchain }}" ]; then TOOLCHAIN="${{ matrix.job.toolchain }}" ; fi + echo set-output name=TOOLCHAIN::${TOOLCHAIN} + echo ::set-output name=TOOLCHAIN::${TOOLCHAIN} + # target-specific options + # * CODECOV_FLAGS + CODECOV_FLAGS=$( echo "${{ matrix.job.os }}" | sed 's/[^[:alnum:]]/_/g' ) + echo set-output name=CODECOV_FLAGS::${CODECOV_FLAGS} + echo ::set-output name=CODECOV_FLAGS::${CODECOV_FLAGS} + - name: rust toolchain ~ install + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ steps.vars.outputs.TOOLCHAIN }} + override: true + profile: minimal # minimal component installation (ie, no documentation) + - name: Test + uses: actions-rs/cargo@v1 + with: + command: test + args: --no-fail-fast + env: + CARGO_INCREMENTAL: '0' + RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Zno-landing-pads' + - name: Generate coverage data + id: coverage + uses: actions-rs/grcov@v0.1 + - name: Upload coverage results (to Codecov.io) + uses: codecov/codecov-action@v1 + with: + file: ${{ steps.coverage.outputs.report }} + flags: ${{ steps.vars.outputs.CODECOV_FLAGS }} + name: codecov-umbrella + fail_ci_if_error: false From 6fad006b8f1f0be236c9508cf079d4931c1c3bef Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Mon, 25 May 2020 23:40:58 -0500 Subject: [PATCH 02/12] Maint/CICD ~ GHA - disable windows-gnu builds with unresolvable linker errors - `undefined reference to `_imp____acrt_iob_func'` - ref: - ref: - ref: - ref: --- .github/workflows/CICD.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index c63f353c6c..d4ccae5723 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -42,9 +42,9 @@ jobs: - { os: ubuntu-18.04 , target: x86_64-unknown-linux-musl , use-cross: use-cross } - { os: ubuntu-16.04 , target: x86_64-unknown-linux-gnu , use-cross: use-cross } - { os: macos-latest , target: x86_64-apple-darwin } - - { os: windows-latest , target: i686-pc-windows-gnu } + # - { os: windows-latest , target: i686-pc-windows-gnu } ## disabled; linker errors (missing '_imp____acrt_iob_func') - { os: windows-latest , target: i686-pc-windows-msvc } - - { os: windows-latest , target: x86_64-pc-windows-gnu } ## note: requires rust >= 1.43.0 to link correctly + # - { os: windows-latest , target: x86_64-pc-windows-gnu } ## disabled; linker errors (missing '_imp____acrt_iob_func') - { os: windows-latest , target: x86_64-pc-windows-msvc } steps: - uses: actions/checkout@v1 @@ -140,7 +140,7 @@ jobs: case ${{ matrix.job.target }} in arm-*) unset JOB_DO_TESTING ;; esac; echo set-output name=JOB_DO_TESTING::${JOB_DO_TESTING:-/false} echo ::set-output name=JOB_DO_TESTING::${JOB_DO_TESTING} - # # * test only library units tests and binary for arm-type targets + # # * test only library unit tests and binary for arm-type targets unset CARGO_TEST_OPTIONS unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--lib --bin ${PROJECT_NAME}" ;; esac; echo set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS} From 92fc7ecbae9477e0f5e1adf6f3e93e10439ed896 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Tue, 26 May 2020 21:35:18 -0500 Subject: [PATCH 03/12] Maint/CICD ~ GHA - add additional linux builds and packaging support --- .github/workflows/CICD.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index d4ccae5723..f336315cf4 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -36,6 +36,7 @@ jobs: - { os: ubuntu-latest , target: arm-unknown-linux-gnueabihf , use-cross: use-cross } - { os: ubuntu-18.04 , target: aarch64-unknown-linux-gnu , use-cross: use-cross } - { os: ubuntu-18.04 , target: i586-unknown-linux-gnu , use-cross: use-cross } + - { os: ubuntu-18.04 , target: i586-unknown-linux-musl , use-cross: use-cross } - { os: ubuntu-18.04 , target: i686-unknown-linux-gnu , use-cross: use-cross } - { os: ubuntu-18.04 , target: i686-unknown-linux-musl , use-cross: use-cross } - { os: ubuntu-18.04 , target: x86_64-unknown-linux-gnu , use-cross: use-cross } @@ -110,7 +111,13 @@ jobs: echo set-output name=DEPLOY::${DEPLOY:-/false} echo ::set-output name=DEPLOY::${DEPLOY} # DPKG architecture? - unset DPKG_ARCH ; case ${{ matrix.job.target }} in i686-*-linux-*) DPKG_ARCH=i686 ;; x86_64-*-linux-*) DPKG_ARCH=amd64 ;; esac; + unset DPKG_ARCH + case ${{ matrix.job.target }} in + aarch64-*-linux-*) DPKG_ARCH=arm64 ;; + arm-*-linux-*hf) DPKG_ARCH=armhf ;; + i686-*-linux-*) DPKG_ARCH=i686 ;; + x86_64-*-linux-*) DPKG_ARCH=amd64 ;; + esac; echo set-output name=DPKG_ARCH::${DPKG_ARCH} echo ::set-output name=DPKG_ARCH::${DPKG_ARCH} # DPKG version? From 9aae28544836df5dca531ba19e9e81096a9a672a Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Tue, 26 May 2020 22:37:37 -0500 Subject: [PATCH 04/12] Fix compiler warning for 'windows' tests (dead_code) --- src/assets.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/assets.rs b/src/assets.rs index 073cf5644f..86c3fe0b54 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -331,6 +331,7 @@ mod tests { .clone() } + #[cfg(unix)] fn syntax_for_file_os(&self, file_name: &OsStr) -> String { self.syntax_for_file_with_content_os(file_name, "") } From 377913cd05ce2874ebd3ce04d3bd8f282926b80a Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Tue, 26 May 2020 21:08:24 -0500 Subject: [PATCH 05/12] Tests ~ `echo` has portability issues; for CI, replace with `printf` - using `echo` on 'windows' platforms requires process execution indirectly via the shell - `printf` is available on all GHA CI platforms - `printf` is *not* available on usual 'windows' platforms; so this is just temporizing, awaiting a true fix --- tests/examples/bat.conf | 2 +- tests/integration_tests.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/examples/bat.conf b/tests/examples/bat.conf index 614ab40da6..6bb92c5cd1 100644 --- a/tests/examples/bat.conf +++ b/tests/examples/bat.conf @@ -2,4 +2,4 @@ --paging=always # Output a dummy message for the integration test. ---pager="echo dummy-pager-from-config" +--pager="printf dummy-pager-from-config" diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index cd6cde5ba3..88f5b637bf 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -372,30 +372,30 @@ fn do_not_exit_directory() { #[test] fn pager_basic() { bat() - .env("PAGER", "echo pager-output") + .env("PAGER", "printf pager-output") .arg("--paging=always") .arg("test.txt") .assert() .success() - .stdout("pager-output\n"); + .stdout("pager-output"); } #[test] fn pager_overwrite() { bat() - .env("PAGER", "echo other-pager") - .env("BAT_PAGER", "echo pager-output") + .env("PAGER", "printf other-pager") + .env("BAT_PAGER", "printf pager-output") .arg("--paging=always") .arg("test.txt") .assert() .success() - .stdout("pager-output\n"); + .stdout("pager-output"); } #[test] fn pager_disable() { bat() - .env("PAGER", "echo other-pager") + .env("PAGER", "printf other-pager") .env("BAT_PAGER", "") .arg("--paging=always") .arg("test.txt") @@ -421,7 +421,7 @@ fn config_read_arguments_from_file() { .arg("test.txt") .assert() .success() - .stdout("dummy-pager-from-config\n"); + .stdout("dummy-pager-from-config"); } #[test] From f8ed8aa74b620a8eb56c6b95c10e821daf416345 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Tue, 26 May 2020 22:05:24 -0500 Subject: [PATCH 06/12] Tests ~ add instrumentation to visualize text differences in failing tests --- tests/integration_tests.rs | 321 +++++++++++++++++++++++++++---------- 1 file changed, 235 insertions(+), 86 deletions(-) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 88f5b637bf..20273af5a3 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -24,9 +24,14 @@ fn bat() -> Command { #[test] fn basic() { - bat() + let assert = bat() .arg("test.txt") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + let stderr = String::from_utf8_lossy(&assert.get_output().stderr); + println!("stdout={:#?}", stdout); + println!("stderr={:#?}", stderr); + assert .success() .stdout("hello world\n") .stderr(""); @@ -34,168 +39,216 @@ fn basic() { #[test] fn stdin() { - bat() + let assert = bat() .write_stdin("foo\nbar\n") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("foo\nbar\n"); } #[test] fn concatenate() { - bat() + let assert = bat() .arg("test.txt") .arg("test.txt") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("hello world\nhello world\n"); } #[test] fn concatenate_stdin() { - bat() + let assert = bat() .arg("test.txt") .arg("-") .arg("test.txt") .write_stdin("stdin\n") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("hello world\nstdin\nhello world\n"); } #[test] fn concatenate_empty_first() { - bat() + let assert = bat() .arg("empty.txt") .arg("test.txt") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("hello world\n"); } #[test] fn concatenate_empty_last() { - bat() + let assert = bat() .arg("test.txt") .arg("empty.txt") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("hello world\n"); } #[test] fn concatenate_empty_both() { - bat() + let assert = bat() .arg("empty.txt") .arg("empty.txt") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout(""); } #[test] fn concatenate_empty_between() { - bat() + let assert = bat() .arg("test.txt") .arg("empty.txt") .arg("test.txt") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("hello world\nhello world\n"); } #[test] fn concatenate_empty_first_and_last() { - bat() + let assert = bat() .arg("empty.txt") .arg("test.txt") .arg("empty.txt") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("hello world\n"); } #[test] fn concatenate_single_line() { - bat() + let assert = bat() .arg("single-line.txt") .arg("single-line.txt") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("Single LineSingle Line"); } #[test] fn concatenate_single_line_empty() { - bat() + let assert = bat() .arg("single-line.txt") .arg("empty.txt") .arg("single-line.txt") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("Single LineSingle Line"); } #[test] fn line_numbers() { - bat() + let assert = bat() .arg("multiline.txt") .arg("--style=numbers") .arg("--decorations=always") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout(" 1 line 1\n 2 line 2\n 3 line 3\n 4 line 4\n"); } #[test] fn line_range_2_3() { - bat() + let assert = bat() .arg("multiline.txt") .arg("--line-range=2:3") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("line 2\nline 3\n"); } #[test] fn line_range_first_two() { - bat() + let assert = bat() .arg("multiline.txt") .arg("--line-range=:2") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("line 1\nline 2\n"); } #[test] fn line_range_last_3() { - bat() + let assert = bat() .arg("multiline.txt") .arg("--line-range=2:") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("line 2\nline 3\nline 4\n"); } #[test] fn line_range_multiple() { - bat() + let assert = bat() .arg("multiline.txt") .arg("--line-range=1:2") .arg("--line-range=4:4") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("line 1\nline 2\nline 4\n"); } #[test] fn tabs_numbers() { - bat() + let assert = bat() .arg("tabs.txt") .arg("--tabs=4") .arg("--style=numbers") .arg("--decorations=always") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout( " 1 1 2 3 4 @@ -213,12 +266,15 @@ fn tabs_numbers() { #[test] fn tabs_passthrough_wrapped() { - bat() + let assert = bat() .arg("tabs.txt") .arg("--tabs=0") .arg("--style=plain") .arg("--decorations=always") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout( " 1 2 3 4 @@ -236,12 +292,15 @@ fn tabs_passthrough_wrapped() { #[test] fn tabs_4_wrapped() { - bat() + let assert = bat() .arg("tabs.txt") .arg("--tabs=4") .arg("--style=plain") .arg("--decorations=always") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout( " 1 2 3 4 @@ -259,12 +318,15 @@ fn tabs_4_wrapped() { #[test] fn tabs_8_wrapped() { - bat() + let assert = bat() .arg("tabs.txt") .arg("--tabs=8") .arg("--style=plain") .arg("--decorations=always") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout( " 1 2 3 4 @@ -282,12 +344,15 @@ fn tabs_8_wrapped() { #[test] fn tabs_passthrough() { - bat() + let assert = bat() .arg("tabs.txt") .arg("--tabs=0") .arg("--style=plain") .arg("--decorations=always") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout( " 1 2 3 4 @@ -305,12 +370,15 @@ fn tabs_passthrough() { #[test] fn tabs_4() { - bat() + let assert = bat() .arg("tabs.txt") .arg("--tabs=4") .arg("--style=plain") .arg("--decorations=always") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout( " 1 2 3 4 @@ -328,12 +396,15 @@ fn tabs_4() { #[test] fn tabs_8() { - bat() + let assert = bat() .arg("tabs.txt") .arg("--tabs=8") .arg("--style=plain") .arg("--decorations=always") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout( " 1 2 3 4 @@ -361,65 +432,83 @@ fn fail_directory() { #[test] fn do_not_exit_directory() { - bat() + let assert = bat() .arg("sub_directory") .arg("test.txt") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .stdout("hello world\n") .failure(); } #[test] fn pager_basic() { - bat() + let assert = bat() .env("PAGER", "printf pager-output") .arg("--paging=always") .arg("test.txt") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("pager-output"); } #[test] fn pager_overwrite() { - bat() + let assert = bat() .env("PAGER", "printf other-pager") .env("BAT_PAGER", "printf pager-output") .arg("--paging=always") .arg("test.txt") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("pager-output"); } #[test] fn pager_disable() { - bat() + let assert = bat() .env("PAGER", "printf other-pager") .env("BAT_PAGER", "") .arg("--paging=always") .arg("test.txt") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("hello world\n"); } #[test] fn config_location_test() { - bat_with_config() + let assert = bat_with_config() .env("BAT_CONFIG_PATH", "bat.conf") .arg("--config-file") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("bat.conf\n"); } #[test] fn config_read_arguments_from_file() { - bat_with_config() + let assert = bat_with_config() .env("BAT_CONFIG_PATH", "bat.conf") .arg("test.txt") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("dummy-pager-from-config"); } @@ -427,20 +516,28 @@ fn config_read_arguments_from_file() { #[test] fn utf16() { // The output will be converted to UTF-8 with a leading UTF-8 BOM - bat() + let assert = bat() .arg("--plain") .arg("--decorations=always") .arg("test_UTF-16LE.txt") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout(std::str::from_utf8(b"\xEF\xBB\xBFhello world\n").unwrap()); } #[test] fn can_print_file_named_cache() { - bat_with_config() + let assert = bat_with_config() .arg("cache") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + let stderr = String::from_utf8_lossy(&assert.get_output().stderr); + println!("stdout={:#?}", stdout); + println!("stderr={:#?}", stderr); + assert .success() .stdout("test\n") .stderr(""); @@ -448,10 +545,15 @@ fn can_print_file_named_cache() { #[test] fn can_print_file_named_cache_with_additional_argument() { - bat_with_config() + let assert = bat_with_config() .arg("cache") .arg("test.txt") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + let stderr = String::from_utf8_lossy(&assert.get_output().stderr); + println!("stdout={:#?}", stdout); + println!("stderr={:#?}", stderr); + assert .success() .stdout("test\nhello world\n") .stderr(""); @@ -459,9 +561,14 @@ fn can_print_file_named_cache_with_additional_argument() { #[test] fn can_print_file_starting_with_cache() { - bat_with_config() + let assert = bat_with_config() .arg("cache.c") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + let stderr = String::from_utf8_lossy(&assert.get_output().stderr); + println!("stdout={:#?}", stdout); + println!("stderr={:#?}", stderr); + assert .success() .stdout("test\n") .stderr(""); @@ -474,12 +581,15 @@ fn does_not_print_unwanted_file_named_cache() { #[test] fn unicode_wrap() { - bat_with_config() + let assert = bat_with_config() .arg("unicode-wrap.txt") .arg("--style=numbers,snip") .arg("--decorations=always") .arg("--terminal-width=40") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout( " 1 ビタミンA ビタミンD ビタミンE ビ @@ -516,14 +626,17 @@ fn unicode_wrap() { #[test] fn snip() { - bat() + let assert = bat() .arg("multiline.txt") .arg("--style=numbers,snip") .arg("--decorations=always") .arg("--line-range=1:2") .arg("--line-range=4:") .arg("--terminal-width=80") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout( " 1 line 1 @@ -536,25 +649,33 @@ fn snip() { #[test] fn empty_file_leads_to_empty_output_with_grid_enabled() { - bat() + let assert = bat() .arg("empty.txt") .arg("--style=grid") .arg("--decorations=always") .arg("--terminal-width=80") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout(""); } #[test] fn filename_basic() { - bat() + let assert = bat() .arg("test.txt") .arg("--decorations=always") .arg("--style=header") .arg("-r=0:0") .arg("--file-name=foo") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + let stderr = String::from_utf8_lossy(&assert.get_output().stderr); + println!("stdout={:#?}", stdout); + println!("stderr={:#?}", stderr); + assert .success() .stdout("File: foo\n") .stderr(""); @@ -562,13 +683,18 @@ fn filename_basic() { #[test] fn filename_binary() { - bat() + let assert = bat() .arg("test.binary") .arg("--decorations=always") .arg("--style=header") .arg("-r=0:0") .arg("--file-name=foo") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + let stderr = String::from_utf8_lossy(&assert.get_output().stderr); + println!("stdout={:#?}", stdout); + println!("stderr={:#?}", stderr); + assert .success() .stdout("File: foo \n") .stderr(""); @@ -576,14 +702,19 @@ fn filename_binary() { #[test] fn filename_stdin() { - bat() + let assert = bat() .arg("--decorations=always") .arg("--style=header") .arg("-r=0:0") .arg("-") .write_stdin("stdin\n") .arg("--file-name=foo") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + let stderr = String::from_utf8_lossy(&assert.get_output().stderr); + println!("stdout={:#?}", stdout); + println!("stderr={:#?}", stderr); + assert .success() .stdout("File: foo\n") .stderr(""); @@ -592,12 +723,17 @@ fn filename_stdin() { #[test] fn filename_stdin_binary() { let vec = vec![0; 1]; - bat_with_config() + let assert = bat_with_config() .arg("--decorations=always") .arg("--style=header") .write_stdin(vec) .arg("--file-name=foo") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + let stderr = String::from_utf8_lossy(&assert.get_output().stderr); + println!("stdout={:#?}", stdout); + println!("stderr={:#?}", stderr); + assert .success() .stdout("File: foo \n") .stderr(""); @@ -605,7 +741,7 @@ fn filename_stdin_binary() { #[test] fn filename_multiple_ok() { - bat() + let assert = bat() .arg("--decorations=always") .arg("--style=header") .arg("-r=0:0") @@ -613,7 +749,12 @@ fn filename_multiple_ok() { .arg("--file-name=foo") .arg("single-line.txt") .arg("--file-name=bar") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + let stderr = String::from_utf8_lossy(&assert.get_output().stderr); + println!("stdout={:#?}", stdout); + println!("stderr={:#?}", stderr); + assert .success() .stdout("File: foo\n\nFile: bar\n") .stderr(""); @@ -634,12 +775,17 @@ fn filename_multiple_err() { #[test] fn header_padding() { - bat() + let assert = bat() .arg("--decorations=always") .arg("--style=header") .arg("test.txt") .arg("single-line.txt") - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + let stderr = String::from_utf8_lossy(&assert.get_output().stderr); + println!("stdout={:#?}", stdout); + println!("stderr={:#?}", stderr); + assert .stdout("File: test.txt\nhello world\n\nFile: single-line.txt\nSingle Line\n") .stderr(""); } @@ -663,9 +809,12 @@ fn file_with_invalid_utf8_filename() { writeln!(file, "dummy content").expect("can write to file"); } - bat() + let assert = bat() .arg(file_path.as_os_str()) - .assert() + .assert(); + let stdout = String::from_utf8_lossy(&assert.get_output().stdout); + println!("stdout={:#?}", stdout); + assert .success() .stdout("dummy content\n"); } From 99190d331bd715e735182f1fd5d54ccadc4f545f Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Tue, 26 May 2020 23:33:15 -0500 Subject: [PATCH 07/12] Tests ~ (fix) always check out test fixtures with known LF line endings - fixes windows test failures - avoids `git` platform-dependent conversion of line endings for checkout of test fixtures (*tests/examples/...*) --- tests/.gitattributes | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/.gitattributes b/tests/.gitattributes index 2cab29b43f..ed47e9f231 100644 --- a/tests/.gitattributes +++ b/tests/.gitattributes @@ -1,3 +1,11 @@ +# force LF EOLs for test fixtures +examples/** text=auto eol=lf +snapshots/** text=auto eol=lf + +# BAT/CMD files always need CRLF EOLs +*.[Bb][Aa][Tt] text eol=crlf +*.[Cc][Mm][Dd] text eol=crlf + examples/* linguist-vendored snapshots/* linguist-vendored benchmarks/* linguist-vendored From 084f1839dad8656d8d85461060163156bdd02317 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Wed, 27 May 2020 21:58:28 -0500 Subject: [PATCH 08/12] Fix pager process execution under 'windows' ## [why] For 'windows' platforms, directly spawning a process (eg, called PATHNAME) bypasses the usual windows shell machinery for determining which process to execute. Specifically, the extensions in PATHEXT will not be used to determine the final executable. So, `PATHNAME.bat`, `PATHNAME.cmd`, ... will *not* be executed even if on they exist on the PATH; and this is counter to the usual expectation of a Windows user. Additionally, built-in commands, such as `echo` and `dir`, will never be accessible as they do not have a PATH to execute and, so, will never be found. To use the usual machinery, giving access to PATHNAME.bat and `echo`, execute the PATHNAME using the windows shell, eg `cmd /d/c PATHNAME`. Note this may expose the constructed command line to the windows shell quoting vagaries (sadly, that may be part of the price). Following Windows standards, the ComSpec environment variable is used to determine which shell to use, with a fallback to the "modern", built-in `cmd` shell. --- src/output.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/output.rs b/src/output.rs index e7d59d6707..31b2994ae6 100644 --- a/src/output.rs +++ b/src/output.rs @@ -74,8 +74,18 @@ impl OutputType { let is_less = pager_path.file_stem() == Some(&OsString::from("less")); + #[cfg(windows)] + let (pager_path, args) = { + let p = std::env::var("ComSpec").unwrap_or_else(|_| "cmd".to_string()); + let mut a = args.to_vec(); + a.insert(0, pager_path.to_str().unwrap().to_string()); + a.insert(0, "/d/c".to_string()); + (p, a) + }; + + let mut p = Command::new(&pager_path); + let mut process = if is_less { - let mut p = Command::new(&pager_path); if args.is_empty() || replace_arguments_to_less { p.arg("--RAW-CONTROL-CHARS"); if quit_if_one_screen { @@ -106,7 +116,6 @@ impl OutputType { p.env("LESSCHARSET", "UTF-8"); p } else { - let mut p = Command::new(&pager_path); p.args(args); p }; From 604c3a454e5dd55f99f769ab62462624f2bb9c7c Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Wed, 27 May 2020 22:30:51 -0500 Subject: [PATCH 09/12] Tests ~ revert to `echo` (after fixed 'windows' pager process execution) --- tests/examples/bat.conf | 2 +- tests/integration_tests.rs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/examples/bat.conf b/tests/examples/bat.conf index 6bb92c5cd1..614ab40da6 100644 --- a/tests/examples/bat.conf +++ b/tests/examples/bat.conf @@ -2,4 +2,4 @@ --paging=always # Output a dummy message for the integration test. ---pager="printf dummy-pager-from-config" +--pager="echo dummy-pager-from-config" diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 20273af5a3..f10221ec64 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -446,7 +446,7 @@ fn do_not_exit_directory() { #[test] fn pager_basic() { let assert = bat() - .env("PAGER", "printf pager-output") + .env("PAGER", "echo pager-output") .arg("--paging=always") .arg("test.txt") .assert(); @@ -454,14 +454,14 @@ fn pager_basic() { println!("stdout={:#?}", stdout); assert .success() - .stdout("pager-output"); + .stdout("pager-output\n"); } #[test] fn pager_overwrite() { let assert = bat() - .env("PAGER", "printf other-pager") - .env("BAT_PAGER", "printf pager-output") + .env("PAGER", "echo other-pager") + .env("BAT_PAGER", "echo pager-output") .arg("--paging=always") .arg("test.txt") .assert(); @@ -469,13 +469,13 @@ fn pager_overwrite() { println!("stdout={:#?}", stdout); assert .success() - .stdout("pager-output"); + .stdout("pager-output\n"); } #[test] fn pager_disable() { let assert = bat() - .env("PAGER", "printf other-pager") + .env("PAGER", "echo other-pager") .env("BAT_PAGER", "") .arg("--paging=always") .arg("test.txt") @@ -510,7 +510,7 @@ fn config_read_arguments_from_file() { println!("stdout={:#?}", stdout); assert .success() - .stdout("dummy-pager-from-config"); + .stdout("dummy-pager-from-config\n"); } #[test] From b5edb3771d8229a7786fe16cf8a21aaa48933284 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Thu, 28 May 2020 09:43:51 -0500 Subject: [PATCH 10/12] Tests ~ add predicate normalization for tests using `echo` --- Cargo.toml | 1 + tests/integration_tests.rs | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b53b11b74c..949375bdc7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,6 +76,7 @@ default-features = false [dev-dependencies] tempdir = "0.3" assert_cmd = "1.0.1" +predicates = "1.0.4" [build-dependencies] clap = { version = "2.33", optional = true } diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index f10221ec64..b5236342cd 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,4 +1,5 @@ use assert_cmd::Command; +use predicates::{prelude::predicate,str::PredicateStrExt}; use std::path::Path; use std::str::from_utf8; @@ -454,7 +455,7 @@ fn pager_basic() { println!("stdout={:#?}", stdout); assert .success() - .stdout("pager-output\n"); + .stdout(predicate::eq("pager-output\n").normalize()); } #[test] @@ -469,7 +470,7 @@ fn pager_overwrite() { println!("stdout={:#?}", stdout); assert .success() - .stdout("pager-output\n"); + .stdout(predicate::eq("pager-output\n").normalize()); } #[test] @@ -484,7 +485,7 @@ fn pager_disable() { println!("stdout={:#?}", stdout); assert .success() - .stdout("hello world\n"); + .stdout(predicate::eq("hello world\n").normalize()); } #[test] @@ -510,7 +511,7 @@ fn config_read_arguments_from_file() { println!("stdout={:#?}", stdout); assert .success() - .stdout("dummy-pager-from-config\n"); + .stdout(predicate::eq("dummy-pager-from-config\n").normalize()); } #[test] From e8cdbd62f1b7caef24e8d31e623235d02fe66aab Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Mon, 1 Jun 2020 10:30:29 -0500 Subject: [PATCH 11/12] Revert 9ed8db22c ('windows' pager process execution fix) based on PR feedback/owner request --- src/output.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/output.rs b/src/output.rs index 31b2994ae6..e7d59d6707 100644 --- a/src/output.rs +++ b/src/output.rs @@ -74,18 +74,8 @@ impl OutputType { let is_less = pager_path.file_stem() == Some(&OsString::from("less")); - #[cfg(windows)] - let (pager_path, args) = { - let p = std::env::var("ComSpec").unwrap_or_else(|_| "cmd".to_string()); - let mut a = args.to_vec(); - a.insert(0, pager_path.to_str().unwrap().to_string()); - a.insert(0, "/d/c".to_string()); - (p, a) - }; - - let mut p = Command::new(&pager_path); - let mut process = if is_less { + let mut p = Command::new(&pager_path); if args.is_empty() || replace_arguments_to_less { p.arg("--RAW-CONTROL-CHARS"); if quit_if_one_screen { @@ -116,6 +106,7 @@ impl OutputType { p.env("LESSCHARSET", "UTF-8"); p } else { + let mut p = Command::new(&pager_path); p.args(args); p }; From bb5ac2a1696913c51e4d99f54285dd90a370c417 Mon Sep 17 00:00:00 2001 From: Roy Ivy III Date: Mon, 1 Jun 2020 16:50:24 -0500 Subject: [PATCH 12/12] Revert f8ed8aa7 (add test instrumentation to help understand failing tests) per PR feedback/owner request - reverts commit f8ed8aa74b620a8eb56c6b95c10e821daf416345 --- tests/integration_tests.rs | 321 ++++++++++--------------------------- 1 file changed, 86 insertions(+), 235 deletions(-) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index b5236342cd..c1fce864db 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -25,14 +25,9 @@ fn bat() -> Command { #[test] fn basic() { - let assert = bat() + bat() .arg("test.txt") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - let stderr = String::from_utf8_lossy(&assert.get_output().stderr); - println!("stdout={:#?}", stdout); - println!("stderr={:#?}", stderr); - assert + .assert() .success() .stdout("hello world\n") .stderr(""); @@ -40,216 +35,168 @@ fn basic() { #[test] fn stdin() { - let assert = bat() + bat() .write_stdin("foo\nbar\n") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout("foo\nbar\n"); } #[test] fn concatenate() { - let assert = bat() + bat() .arg("test.txt") .arg("test.txt") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout("hello world\nhello world\n"); } #[test] fn concatenate_stdin() { - let assert = bat() + bat() .arg("test.txt") .arg("-") .arg("test.txt") .write_stdin("stdin\n") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout("hello world\nstdin\nhello world\n"); } #[test] fn concatenate_empty_first() { - let assert = bat() + bat() .arg("empty.txt") .arg("test.txt") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout("hello world\n"); } #[test] fn concatenate_empty_last() { - let assert = bat() + bat() .arg("test.txt") .arg("empty.txt") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout("hello world\n"); } #[test] fn concatenate_empty_both() { - let assert = bat() + bat() .arg("empty.txt") .arg("empty.txt") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout(""); } #[test] fn concatenate_empty_between() { - let assert = bat() + bat() .arg("test.txt") .arg("empty.txt") .arg("test.txt") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout("hello world\nhello world\n"); } #[test] fn concatenate_empty_first_and_last() { - let assert = bat() + bat() .arg("empty.txt") .arg("test.txt") .arg("empty.txt") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout("hello world\n"); } #[test] fn concatenate_single_line() { - let assert = bat() + bat() .arg("single-line.txt") .arg("single-line.txt") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout("Single LineSingle Line"); } #[test] fn concatenate_single_line_empty() { - let assert = bat() + bat() .arg("single-line.txt") .arg("empty.txt") .arg("single-line.txt") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout("Single LineSingle Line"); } #[test] fn line_numbers() { - let assert = bat() + bat() .arg("multiline.txt") .arg("--style=numbers") .arg("--decorations=always") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout(" 1 line 1\n 2 line 2\n 3 line 3\n 4 line 4\n"); } #[test] fn line_range_2_3() { - let assert = bat() + bat() .arg("multiline.txt") .arg("--line-range=2:3") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout("line 2\nline 3\n"); } #[test] fn line_range_first_two() { - let assert = bat() + bat() .arg("multiline.txt") .arg("--line-range=:2") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout("line 1\nline 2\n"); } #[test] fn line_range_last_3() { - let assert = bat() + bat() .arg("multiline.txt") .arg("--line-range=2:") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout("line 2\nline 3\nline 4\n"); } #[test] fn line_range_multiple() { - let assert = bat() + bat() .arg("multiline.txt") .arg("--line-range=1:2") .arg("--line-range=4:4") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout("line 1\nline 2\nline 4\n"); } #[test] fn tabs_numbers() { - let assert = bat() + bat() .arg("tabs.txt") .arg("--tabs=4") .arg("--style=numbers") .arg("--decorations=always") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout( " 1 1 2 3 4 @@ -267,15 +214,12 @@ fn tabs_numbers() { #[test] fn tabs_passthrough_wrapped() { - let assert = bat() + bat() .arg("tabs.txt") .arg("--tabs=0") .arg("--style=plain") .arg("--decorations=always") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout( " 1 2 3 4 @@ -293,15 +237,12 @@ fn tabs_passthrough_wrapped() { #[test] fn tabs_4_wrapped() { - let assert = bat() + bat() .arg("tabs.txt") .arg("--tabs=4") .arg("--style=plain") .arg("--decorations=always") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout( " 1 2 3 4 @@ -319,15 +260,12 @@ fn tabs_4_wrapped() { #[test] fn tabs_8_wrapped() { - let assert = bat() + bat() .arg("tabs.txt") .arg("--tabs=8") .arg("--style=plain") .arg("--decorations=always") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout( " 1 2 3 4 @@ -345,15 +283,12 @@ fn tabs_8_wrapped() { #[test] fn tabs_passthrough() { - let assert = bat() + bat() .arg("tabs.txt") .arg("--tabs=0") .arg("--style=plain") .arg("--decorations=always") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout( " 1 2 3 4 @@ -371,15 +306,12 @@ fn tabs_passthrough() { #[test] fn tabs_4() { - let assert = bat() + bat() .arg("tabs.txt") .arg("--tabs=4") .arg("--style=plain") .arg("--decorations=always") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout( " 1 2 3 4 @@ -397,15 +329,12 @@ fn tabs_4() { #[test] fn tabs_8() { - let assert = bat() + bat() .arg("tabs.txt") .arg("--tabs=8") .arg("--style=plain") .arg("--decorations=always") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout( " 1 2 3 4 @@ -433,83 +362,65 @@ fn fail_directory() { #[test] fn do_not_exit_directory() { - let assert = bat() + bat() .arg("sub_directory") .arg("test.txt") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .stdout("hello world\n") .failure(); } #[test] fn pager_basic() { - let assert = bat() + bat() .env("PAGER", "echo pager-output") .arg("--paging=always") .arg("test.txt") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout(predicate::eq("pager-output\n").normalize()); } #[test] fn pager_overwrite() { - let assert = bat() + bat() .env("PAGER", "echo other-pager") .env("BAT_PAGER", "echo pager-output") .arg("--paging=always") .arg("test.txt") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout(predicate::eq("pager-output\n").normalize()); } #[test] fn pager_disable() { - let assert = bat() + bat() .env("PAGER", "echo other-pager") .env("BAT_PAGER", "") .arg("--paging=always") .arg("test.txt") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout(predicate::eq("hello world\n").normalize()); } #[test] fn config_location_test() { - let assert = bat_with_config() + bat_with_config() .env("BAT_CONFIG_PATH", "bat.conf") .arg("--config-file") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout("bat.conf\n"); } #[test] fn config_read_arguments_from_file() { - let assert = bat_with_config() + bat_with_config() .env("BAT_CONFIG_PATH", "bat.conf") .arg("test.txt") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout(predicate::eq("dummy-pager-from-config\n").normalize()); } @@ -517,28 +428,20 @@ fn config_read_arguments_from_file() { #[test] fn utf16() { // The output will be converted to UTF-8 with a leading UTF-8 BOM - let assert = bat() + bat() .arg("--plain") .arg("--decorations=always") .arg("test_UTF-16LE.txt") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout(std::str::from_utf8(b"\xEF\xBB\xBFhello world\n").unwrap()); } #[test] fn can_print_file_named_cache() { - let assert = bat_with_config() + bat_with_config() .arg("cache") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - let stderr = String::from_utf8_lossy(&assert.get_output().stderr); - println!("stdout={:#?}", stdout); - println!("stderr={:#?}", stderr); - assert + .assert() .success() .stdout("test\n") .stderr(""); @@ -546,15 +449,10 @@ fn can_print_file_named_cache() { #[test] fn can_print_file_named_cache_with_additional_argument() { - let assert = bat_with_config() + bat_with_config() .arg("cache") .arg("test.txt") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - let stderr = String::from_utf8_lossy(&assert.get_output().stderr); - println!("stdout={:#?}", stdout); - println!("stderr={:#?}", stderr); - assert + .assert() .success() .stdout("test\nhello world\n") .stderr(""); @@ -562,14 +460,9 @@ fn can_print_file_named_cache_with_additional_argument() { #[test] fn can_print_file_starting_with_cache() { - let assert = bat_with_config() + bat_with_config() .arg("cache.c") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - let stderr = String::from_utf8_lossy(&assert.get_output().stderr); - println!("stdout={:#?}", stdout); - println!("stderr={:#?}", stderr); - assert + .assert() .success() .stdout("test\n") .stderr(""); @@ -582,15 +475,12 @@ fn does_not_print_unwanted_file_named_cache() { #[test] fn unicode_wrap() { - let assert = bat_with_config() + bat_with_config() .arg("unicode-wrap.txt") .arg("--style=numbers,snip") .arg("--decorations=always") .arg("--terminal-width=40") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout( " 1 ビタミンA ビタミンD ビタミンE ビ @@ -627,17 +517,14 @@ fn unicode_wrap() { #[test] fn snip() { - let assert = bat() + bat() .arg("multiline.txt") .arg("--style=numbers,snip") .arg("--decorations=always") .arg("--line-range=1:2") .arg("--line-range=4:") .arg("--terminal-width=80") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout( " 1 line 1 @@ -650,33 +537,25 @@ fn snip() { #[test] fn empty_file_leads_to_empty_output_with_grid_enabled() { - let assert = bat() + bat() .arg("empty.txt") .arg("--style=grid") .arg("--decorations=always") .arg("--terminal-width=80") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout(""); } #[test] fn filename_basic() { - let assert = bat() + bat() .arg("test.txt") .arg("--decorations=always") .arg("--style=header") .arg("-r=0:0") .arg("--file-name=foo") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - let stderr = String::from_utf8_lossy(&assert.get_output().stderr); - println!("stdout={:#?}", stdout); - println!("stderr={:#?}", stderr); - assert + .assert() .success() .stdout("File: foo\n") .stderr(""); @@ -684,18 +563,13 @@ fn filename_basic() { #[test] fn filename_binary() { - let assert = bat() + bat() .arg("test.binary") .arg("--decorations=always") .arg("--style=header") .arg("-r=0:0") .arg("--file-name=foo") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - let stderr = String::from_utf8_lossy(&assert.get_output().stderr); - println!("stdout={:#?}", stdout); - println!("stderr={:#?}", stderr); - assert + .assert() .success() .stdout("File: foo \n") .stderr(""); @@ -703,19 +577,14 @@ fn filename_binary() { #[test] fn filename_stdin() { - let assert = bat() + bat() .arg("--decorations=always") .arg("--style=header") .arg("-r=0:0") .arg("-") .write_stdin("stdin\n") .arg("--file-name=foo") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - let stderr = String::from_utf8_lossy(&assert.get_output().stderr); - println!("stdout={:#?}", stdout); - println!("stderr={:#?}", stderr); - assert + .assert() .success() .stdout("File: foo\n") .stderr(""); @@ -724,17 +593,12 @@ fn filename_stdin() { #[test] fn filename_stdin_binary() { let vec = vec![0; 1]; - let assert = bat_with_config() + bat_with_config() .arg("--decorations=always") .arg("--style=header") .write_stdin(vec) .arg("--file-name=foo") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - let stderr = String::from_utf8_lossy(&assert.get_output().stderr); - println!("stdout={:#?}", stdout); - println!("stderr={:#?}", stderr); - assert + .assert() .success() .stdout("File: foo \n") .stderr(""); @@ -742,7 +606,7 @@ fn filename_stdin_binary() { #[test] fn filename_multiple_ok() { - let assert = bat() + bat() .arg("--decorations=always") .arg("--style=header") .arg("-r=0:0") @@ -750,12 +614,7 @@ fn filename_multiple_ok() { .arg("--file-name=foo") .arg("single-line.txt") .arg("--file-name=bar") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - let stderr = String::from_utf8_lossy(&assert.get_output().stderr); - println!("stdout={:#?}", stdout); - println!("stderr={:#?}", stderr); - assert + .assert() .success() .stdout("File: foo\n\nFile: bar\n") .stderr(""); @@ -776,17 +635,12 @@ fn filename_multiple_err() { #[test] fn header_padding() { - let assert = bat() + bat() .arg("--decorations=always") .arg("--style=header") .arg("test.txt") .arg("single-line.txt") - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - let stderr = String::from_utf8_lossy(&assert.get_output().stderr); - println!("stdout={:#?}", stdout); - println!("stderr={:#?}", stderr); - assert + .assert() .stdout("File: test.txt\nhello world\n\nFile: single-line.txt\nSingle Line\n") .stderr(""); } @@ -810,12 +664,9 @@ fn file_with_invalid_utf8_filename() { writeln!(file, "dummy content").expect("can write to file"); } - let assert = bat() + bat() .arg(file_path.as_os_str()) - .assert(); - let stdout = String::from_utf8_lossy(&assert.get_output().stdout); - println!("stdout={:#?}", stdout); - assert + .assert() .success() .stdout("dummy content\n"); }