diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml index 91354ed..b93b718 100644 --- a/.github/workflows/windows-build.yml +++ b/.github/workflows/windows-build.yml @@ -81,6 +81,13 @@ jobs: platform: [x64, Win32] configuration: [Release] steps: + # The engine's test scripts are #!/bin/bash and the engine has no .gitattributes, + # so an autocrlf checkout would rewrite them and bash would die on $'\r'. Files + # land exactly as stored either way, which is what the Windows tooling wants too. + - name: Check out with the line endings the repos actually store + shell: pwsh + run: git config --global core.autocrlf false + - name: Checkout httrack-windows uses: actions/checkout@v4 with: @@ -366,6 +373,97 @@ jobs: } if ($bad) { throw ($bad -join "`n") } Write-Host "::notice::all three binaries report $name $want" + # The engine ships ~90 tests and not one of them has ever run on Windows -- they + # only run under `make check` on Linux/macOS. These are the offline ones: no + # network, no server, just httrack.exe driven from PATH. They are the engine's + # tests, but ours is the binary that ships, and this code has never been executed + # on this platform. + - name: Run the engine test suite (offline tests) + shell: bash + working-directory: httrack/tests + run: | + set -u + bin="$(cygpath -u "$GITHUB_WORKSPACE")/httrack-windows/WinHTTrack/bin/${{ matrix.platform }}/${{ matrix.configuration }}" + export PATH="$bin:$PATH" + + # httrack.exe is a native binary, so MSYS rewrites any argument shaped like a + # POSIX path -- and a URL path is shaped exactly like one. The mime self-test was + # handed "/a/b.html" and the engine received "C:/Program Files/Git/a/b.html". + # Switch the rewriting off, and hand the tests a temp dir that is already a Windows + # path, so nothing needs translating in either direction. + export MSYS_NO_PATHCONV=1 + export MSYS2_ARG_CONV_EXCL='*' + export TMPDIR="$(cygpath -m "${RUNNER_TEMP}")" + echo "TMPDIR=$TMPDIR" + command -v httrack >/dev/null || { echo "::error::httrack.exe is not on PATH ($bin)"; exit 1; } + echo "httrack: $(command -v httrack)" + + pass=0; fail=0; skip=0; failed="" + for t in 00_runnable.test 01_engine-*.test 01_zlib-*.test; do + rc=0 + bash "$t" >"$t.log" 2>&1 || rc=$? + case "$rc" in + 0) pass=$((pass+1)); echo "PASS $t" ;; + 77) skip=$((skip+1)); echo "SKIP $t -- $(tail -n 1 "$t.log")" ;; + *) fail=$((fail+1)); failed="$failed $t" + echo "FAIL $t (exit $rc)" + # Several of these assert with `test "$(httrack ...)" == "..." || exit 1`, + # which says nothing at all on failure. Re-run under a trace so the log + # holds the call that failed and what it actually printed. + bash -x "$t" >>"$t.log" 2>&1 || true + sed 's/^/ /' "$t.log" | tail -n 25 ;; + esac + done + total=$((pass + fail + skip)) + echo "--- ran=$total pass=$pass fail=$fail skip=$skip ---" + + # A skipped test is not a passing test. Every gate in these scripts exits 77 -- + # no python3, no HTTPS, no codec -- so a suite that quietly degrades to + # "everything skipped" would otherwise report success having tested nothing. + [ "$total" -ge 45 ] || { echo "::error::only $total tests were discovered: the glob found nothing"; exit 1; } + [ "$pass" -ge 40 ] || { echo "::error::only $pass tests actually ran and passed ($skip skipped)"; exit 1; } + [ "$fail" -eq 0 ] || { echo "::error::failing tests:$failed"; exit 1; } + + # The cache tests pipe stderr to /dev/null and print one word, so a failure says + # "FAIL" and nothing else. Run the two self-tests verbatim and keep everything they + # say: the engine side needs the assertion, not the verdict. + - name: Cache self-tests, verbatim + if: always() + shell: bash + working-directory: httrack/tests + run: | + bin="$(cygpath -u "$GITHUB_WORKSPACE")/httrack-windows/WinHTTrack/bin/${{ matrix.platform }}/${{ matrix.configuration }}" + export PATH="$bin:$PATH" + export MSYS_NO_PATHCONV=1 + export MSYS2_ARG_CONV_EXCL='*' + export TMPDIR="$(cygpath -m "${RUNNER_TEMP}")" + command -v httrack >/dev/null || exit 0 + for t in cache cache-corrupt; do + d="$TMPDIR/selftest-$t" + mkdir -p "$d" + echo "===== httrack -#test=$t $d" + httrack "-#test=$t" "$d" 2>&1 || echo "(exit $?)" + echo + done + # filterbounds prints nothing at all and the test just sees an empty string. + # An exit code tells us whether it died: 0xC00000FD is a stack overflow, and this + # self-test drives the matcher deep on purpose. + echo "===== httrack -#test=filterbounds" + httrack "-#test=filterbounds"; echo "exit=$? (139/0xC00000FD would be a stack overflow)" + + echo "===== httrack -#test=sniff 'image/svg+xml' with a BOM" + printf '\xef\xbb\xbf ' > "$TMPDIR/bom.txt" + httrack "-#test=sniff" 'image/svg+xml' "text:$(cat "$TMPDIR/bom.txt")" 2>&1 || echo "(exit $?)" + + # A 25-line tail cannot tell a real Windows bug from a POSIX assumption in the + # test. Keep every test's whole output. + - name: Upload the test logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: engine-tests-${{ matrix.platform }}-${{ matrix.configuration }} + path: httrack/tests/*.log + if-no-files-found: warn # Code signing, 1 of 2: the payload. SignPath signs an Inno installer as an opaque # PE and cannot reach the files inside it, so sign these BEFORE ISCC packages them.