diff --git a/.github/workflows/run-gnu-test.yml b/.github/workflows/run-gnu-test.yml new file mode 100644 index 00000000..41e6caf8 --- /dev/null +++ b/.github/workflows/run-gnu-test.yml @@ -0,0 +1,68 @@ + +name: Run GNU Test + +on: + pull_request: + push: + branches: + - 'main' + +jobs: + native: + name: Run GNU tests (native) + runs-on: ubuntu-24.04 + steps: + - name: Checkout code (uutils/util-linux) + uses: actions/checkout@v6 + with: + path: uutils-util-linux + - name: Checkout code (util-linux/util-linux) + uses: actions/checkout@v6 + with: + repository: util-linux/util-linux + path: gnu-util-linux + ref: v2.41.4 + - uses: Swatinem/rust-cache@v2 + with: + workspaces: "./uutils-util-linux -> target" + - name: Install dependencies + run: | + sudo apt-get update --quiet + sudo apt-get install -y \ + build-essential autoconf automake autopoint pkg-config \ + libtool gettext bison \ + bc socat ntp iproute2 squashfs-tools \ + libcap-ng-dev libpam-dev libudev-dev python3-dev libmount-dev libclang-dev libsmartcols-dev curl + - name: Build GNU tests + run: | + cd gnu-util-linux + ./autogen.sh + ./configure + make -j$(nproc) check-programs + - name: Download test baseline if exists + id: download-test-baseline + continue-on-error: true + uses: actions/download-artifact@v8 + with: + name: gnu-test-failures + path: uutils-util-linux/.reference/ + + - name: Run GNU tests + env: + GNU_PROJECT_DIR: ${{ github.workspace }}/gnu-util-linux + run: | + cd uutils-util-linux + ./scripts/run-tests.sh + + - name: Update test baseline + if: github.ref == 'refs/heads/main' + run: | + cd uutils-util-linux + ./scripts/check-new-gnu-failures.sh --update-baseline + + - name: Upload test baseline artifact + if: github.ref == 'refs/heads/main' || steps.download-test-baseline.outcome == 'failure' + uses: actions/upload-artifact@v7 + with: + name: gnu-test-failures + path: uutils-util-linux/.reference/gnu-test-failures.txt diff --git a/.gitignore b/.gitignore index 5b3bd554..89305739 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ syntax: glob /target/ + +.test-helpers +.reference diff --git a/scripts/check-new-gnu-failures.sh b/scripts/check-new-gnu-failures.sh new file mode 100755 index 00000000..b19b8cbb --- /dev/null +++ b/scripts/check-new-gnu-failures.sh @@ -0,0 +1,96 @@ +#!/bin/bash +set -euo pipefail + +cd "$(dirname "$0")/../" +PROJECT_DIR="$(pwd)" +DIFF_DIR="$PROJECT_DIR/.test-helpers/tests/diff" +BASELINE="$PROJECT_DIR/.reference/gnu-test-failures.txt" +UPDATE_BASELINE=false +EMPTY_BASELINE=false + +for arg in "$@"; do + case "$arg" in + --update-baseline) UPDATE_BASELINE=true ;; + *) echo "Unknown argument: $arg" >&2; exit 2 ;; + esac +done + +# Collect current failures (exclude .err companion files) +current_tmp=$(mktemp) +trap 'rm -f "$current_tmp"' EXIT +if [[ -d "$DIFF_DIR" ]]; then + find "$DIFF_DIR" -mindepth 2 -maxdepth 2 -type f ! -name "*.err" -print0 \ + | sort -z \ + | while IFS= read -r -d '' path; do + echo "${path#"$DIFF_DIR/"}" + done > "$current_tmp" +fi + +# --update-baseline mode +if [[ "$UPDATE_BASELINE" == true ]]; then + { + echo "# Known GNU test failures - format: util/testname" + echo "# Update with: ./scripts/check-new-gnu-failures.sh --update-baseline" + cat "$current_tmp" + } > "$BASELINE" + count=$(wc -l < "$current_tmp") + echo "Baseline updated: $BASELINE ($count failures recorded)" + exit 0 +fi + +# Load baseline (strip comments and blank lines) +baseline_tmp=$(mktemp) +trap 'rm -f "$current_tmp" "$baseline_tmp"' EXIT +if [[ -f "$BASELINE" ]]; then + grep -v '^\s*#' "$BASELINE" | grep -v '^\s*$' | sort > "$baseline_tmp" +else + echo "WARNING: Baseline not found: $BASELINE" >&2 + echo " Run with --update-baseline to create it." >&2 + touch "$baseline_tmp" + EMPTY_BASELINE=true +fi + +# comm: -13 = lines only in current (new failures), -23 = lines only in baseline (fixed) +new_failures=$(comm -13 "$baseline_tmp" "$current_tmp") +fixed_tests=$(comm -23 "$baseline_tmp" "$current_tmp") + +# Report +echo "--- GNU test failure summary ---" +echo "Baseline failures: $(wc -l < "$baseline_tmp")" +echo "Current failures: $(wc -l < "$current_tmp")" +echo "" + +if [[ "$EMPTY_BASELINE" == true ]]; then + mkdir -p "$(dirname "$BASELINE")" + { + echo "# Known GNU test failures - format: util/testname" + echo "# Update with: ./scripts/check-new-gnu-failures.sh --update-baseline" + cat "$current_tmp" + } > "$BASELINE" + count=$(wc -l < "$current_tmp") + echo "Initial baseline created: $BASELINE ($count failures recorded)" + exit 0 +fi + +if [[ -n "$fixed_tests" ]]; then + echo "Tests newly FIXED:" + awk '{print " [FIXED] " $0}' <<< "$fixed_tests" + echo "" +fi + +if [[ -n "$new_failures" ]]; then + echo "Tests newly FAILING:" + awk '{print " [NEW FAILURE] " $0}' <<< "$new_failures" + echo "" + count=$(wc -l <<< "$new_failures") + echo "ERROR: $count new test failure(s) detected." >&2 + echo " Fix the regression, or if intentional, update the baseline:" >&2 + echo " ./scripts/check-new-gnu-failures.sh --update-baseline" >&2 + exit 1 +fi + +echo "No new failures. All current failures are known." +if [[ -n "$fixed_tests" ]]; then + echo "Consider updating the baseline to remove fixed tests:" + echo " ./scripts/check-new-gnu-failures.sh --update-baseline" +fi diff --git a/scripts/gen-test-helper.sh b/scripts/gen-test-helper.sh new file mode 100755 index 00000000..4eb91ad3 --- /dev/null +++ b/scripts/gen-test-helper.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +set -euxo pipefail + +UTILS=(cal dmesg hexdump lscpu lslocks lsmem) + +cd "$(dirname "$0")/../" +PROJECT_DIR="$(pwd)" +BINARY="$PROJECT_DIR/target/release/util-linux" + +mkdir -p .test-helpers + +for util in "${UTILS[@]}"; do + cat > ".test-helpers/$util" <