Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 9 additions & 95 deletions .github/workflows/compat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ on: [push, pull_request]

name: External-testsuites

env:
CARGO_INCREMENTAL: 0

jobs:
gnu-tests:
permissions:
Expand Down Expand Up @@ -40,12 +43,9 @@ jobs:
shell: bash
run: |
cd findutils
export CARGO_INCREMENTAL=0
bash util/build-gnu.sh ||:
- name: Extract testing info
shell: bash
run: |

bash util/build-gnu.sh
env:
GNU_DIR: ${{ github.workspace }}/findutils.gnu
- name: Upload gnu-test-report
uses: actions/upload-artifact@v7
with:
Expand All @@ -59,54 +59,10 @@ jobs:
with:
name: gnu-result
path: gnu-result.json
- name: Download artifacts (gnu-result and gnu-test-report)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOTE: The previous download-and-compare steps downloaded artifacts from
the current run (${{ github.run_id }}), then compared them against
the same logs already on disk -- producing "PASS +0 / FAIL +0" every
time (confirmed in upstream CI logs). To do actual regression detection,
this would need to download artifacts from the main branch's latest
run (e.g. via dawidd6/action-download-artifact). The artifact uploads
above are preserved for the upload-annotations job.

uses: actions/github-script@v8
with:
script: |
let fs = require('fs');
fs.mkdirSync('${{ github.workspace }}/dl', { recursive: true });

async function downloadArtifact(artifactName) {
// List all artifacts from the workflow run
let artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{ github.run_id }},
});

// Find the specified artifact
let matchArtifact = artifacts.data.artifacts.find((artifact) => artifact.name === artifactName);
if (!matchArtifact) {
throw new Error(`Artifact "${artifactName}" not found.`);
}

// Download the artifact
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});

// Save the artifact to a file
fs.writeFileSync(`${{ github.workspace }}/dl/${artifactName}.zip`, Buffer.from(download.data));
}

// Download the required artifacts
await downloadArtifact("gnu-result");
await downloadArtifact("gnu-test-report");

- name: Compare failing tests against master
shell: bash
run: |
./findutils/util/diff-gnu.sh ./dl ./findutils.gnu
- name: Compare against main results
shell: bash
run: |
unzip dl/gnu-result.zip -d dl/
unzip dl/gnu-test-report.zip -d dl/
mv dl/gnu-result.json latest-gnu-result.json
python findutils/util/compare_gnu_result.py

bfs-tests:
name: Run BFS tests
Expand All @@ -133,8 +89,9 @@ jobs:
shell: bash
run: |
cd findutils
export CARGO_INCREMENTAL=0
bash util/build-bfs.sh ||:
bash util/build-bfs.sh
env:
BFS_DIR: ${{ github.workspace }}/bfs
- name: Upload bfs-test-report
uses: actions/upload-artifact@v7
with:
Expand All @@ -145,53 +102,10 @@ jobs:
with:
name: bfs-result
path: bfs-result.json
- name: Download artifacts (gnu-result and bfs-test-report)
uses: actions/github-script@v8
with:
script: |
let fs = require('fs');
fs.mkdirSync('${{ github.workspace }}/dl', { recursive: true });

async function downloadArtifact(artifactName) {
// List all artifacts from the workflow run
let artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{ github.run_id }},
});

// Find the specified artifact
let matchArtifact = artifacts.data.artifacts.find((artifact) => artifact.name === artifactName);
if (!matchArtifact) {
throw new Error(`Artifact "${artifactName}" not found.`);
}

// Download the artifact
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});

// Save the artifact to a file
fs.writeFileSync(`${{ github.workspace }}/dl/${artifactName}.zip`, Buffer.from(download.data));
}

// Download the required artifacts
await downloadArtifact("bfs-result");
await downloadArtifact("bfs-test-report");
- name: Compare failing tests against main
shell: bash
run: |
./findutils/util/diff-bfs.sh dl/tests.log bfs/tests.log
- name: Compare against main results
shell: bash
run: |
unzip dl/bfs-result.zip -d dl/
unzip dl/bfs-test-report.zip -d dl/
mv dl/bfs-result.json latest-bfs-result.json
python findutils/util/compare_bfs_result.py

upload-annotations:
name: Upload annotations
Expand Down
36 changes: 36 additions & 0 deletions Dockerfile.compat
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
FROM ubuntu:24.04

RUN apt-get update \
&& sed -i 's/^Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/ubuntu.sources \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
git curl wget ca-certificates build-essential jq \
autoconf automake autopoint texinfo dejagnu libcap2-bin \
&& apt-get build-dep -y findutils \
&& rm -rf /var/lib/apt/lists/*

# Install Rust
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"

# Clone and pre-build GNU findutils (matches CI ref)
RUN git clone https://github.com/gnu-mirror-unofficial/findutils.git /findutils.gnu \
&& cd /findutils.gnu \
&& git checkout 5768a03ddfb5e18b1682e339d6cdd24ff721c510 \
&& git submodule sync --recursive \
&& git config submodule.gnulib.url https://github.com/coreutils/gnulib.git \
&& git submodule update --init --recursive --depth 1 \
&& ./bootstrap \
&& ./configure --quiet \
&& make -j "$(nproc)"

# Clone and pre-build BFS test utilities (matches CI ref)
RUN git clone --branch 4.0 https://github.com/tavianator/bfs.git /bfs \
&& cd /bfs \
&& ./configure NOLIBS=y \
&& make -j "$(nproc)" bin/tests/mksock bin/tests/xtouch

# Build Rust artifacts to /target so we don't clobber the host's target/
ENV CARGO_TARGET_DIR=/target

WORKDIR /findutils
2 changes: 1 addition & 1 deletion tests/find_cmd_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ fn find_printf() {

println!("Actual output: '{}'", output_str.trim());

let re = Regex::new(r"^\d{4}-\d{2}-\d{2}\+\d{2}:\d{2}:\d{2}\.\d{9}0$")
let re = Regex::new(r"^\d{4}-\d{2}-\d{2}\+\d{2}:\d{2}:\d{2}\.\d+0$")
.expect("Failed to compile regex");

assert!(
Expand Down
52 changes: 24 additions & 28 deletions util/build-bfs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,27 @@

set -eo pipefail

if ! test -d ../bfs; then
echo "Could not find ../bfs"
echo "git clone https://github.com/tavianator/bfs.git"
exit 1
# shellcheck source=util/common.sh
source "$(dirname "${BASH_SOURCE[0]}")/common.sh"

if test -z "$BFS_DIR"; then
if test -d "$FINDUTILS_DIR/../bfs"; then
BFS_DIR="$FINDUTILS_DIR/../bfs"
elif test -d "$FINDUTILS_DIR/../../tavianator/bfs"; then
BFS_DIR="$FINDUTILS_DIR/../../tavianator/bfs"
else
echo "Could not find bfs checkout"
echo "Set BFS_DIR or clone:"
echo " git clone https://github.com/tavianator/bfs.git $FINDUTILS_DIR/../bfs"
exit 1
fi
fi

# build the rust implementation
cargo build --release
FIND=$(readlink -f target/release/find)
# Build the Rust implementation
build_rust
FIND="$FIND_BIN"

cd ../bfs
cd "$BFS_DIR"
./configure NOLIBS=y
make -j "$(nproc)" bin/tests/{mksock,xtouch}

Expand All @@ -28,26 +38,12 @@ PASS=$(sed -En 's|^\[PASS] *([0-9]+) / .*|\1|p' "$LOG_FILE")
SKIP=$(sed -En 's|^\[SKIP] *([0-9]+) / .*|\1|p' "$LOG_FILE")
FAIL=$(sed -En 's|^\[FAIL] *([0-9]+) / .*|\1|p' "$LOG_FILE")

# Default any missing numbers to zero (e.g. no tests skipped)
: ${PASS:=0}
: ${SKIP:=0}
: ${FAIL:=0}
: "${PASS:=0}"
: "${SKIP:=0}"
: "${FAIL:=0}"

TOTAL=$((PASS + SKIP + FAIL))
if (( TOTAL <= 1 )); then
echo "Error in the execution, failing early"
exit 1
fi

output="BFS tests summary = TOTAL: $TOTAL / PASS: $PASS / SKIP: $SKIP / FAIL: $FAIL"
echo "${output}"
if (( FAIL > 0 )); then echo "::warning ::${output}"; fi

jq -n \
--arg date "$(date --rfc-email)" \
--arg sha "$GITHUB_SHA" \
--arg total "$TOTAL" \
--arg pass "$PASS" \
--arg skip "$SKIP" \
--arg fail "$FAIL" \
'{($date): { sha: $sha, total: $total, pass: $pass, skip: $skip, fail: $fail, }}' > ../bfs-result.json
check_total "$TOTAL"
print_summary "BFS tests" "$TOTAL" "$PASS" "$SKIP" "$FAIL"
generate_result_json "${RESULT_FILE:-$BFS_DIR/../bfs-result.json}" "$TOTAL" "$PASS" "$SKIP" "$FAIL"
50 changes: 21 additions & 29 deletions util/build-gnu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,32 @@

set -e

if test ! -d ../findutils.gnu; then
echo "Could not find ../findutils.gnu"
echo "git clone https://git.savannah.gnu.org/git/findutils.git findutils.gnu"
# shellcheck source=util/common.sh
source "$(dirname "${BASH_SOURCE[0]}")/common.sh"

GNU_DIR="${GNU_DIR:-$FINDUTILS_DIR/../findutils.gnu}"

if ! test -d "$GNU_DIR"; then
echo "Could not find $GNU_DIR"
echo "Set GNU_DIR or clone:"
echo " git clone https://git.savannah.gnu.org/git/findutils.git $GNU_DIR"
exit 1
fi

# build the rust implementation
cargo build --release
cp target/release/find ../findutils.gnu/find.rust
cp target/release/xargs ../findutils.gnu/xargs.rust
# Build the Rust implementation
build_rust
cp "$FIND_BIN" "$GNU_DIR/find.rust"
cp "$XARGS_BIN" "$GNU_DIR/xargs.rust"

# Clone and build upstream repo
cd ../findutils.gnu
if test ! -f configure; then
# Build upstream GNU findutils if needed
cd "$GNU_DIR"
if ! test -f configure; then
./bootstrap
./configure --quiet
make -j "$(nproc)"
fi

# overwrite the GNU version with the rust impl
# Overwrite the GNU versions with the Rust impl
cp find.rust find/find
cp xargs.rust xargs/xargs

Expand All @@ -35,6 +41,7 @@ make check-TESTS $RUN_TEST || :
make -C find/testsuite check || :
make -C xargs/testsuite check || :

# Collect results
PASS=0
SKIP=0
FAIL=0
Expand Down Expand Up @@ -65,21 +72,6 @@ if test -f "$LOG_FILE"; then
((ERROR += $(sed -n "s/.*# ERROR: \(.*\)/\1/p" "$LOG_FILE" | tr -d '\r' | head -n1))) || :
fi

if ((TOTAL <= 1)); then
echo "Error in the execution, failing early"
exit 1
fi

output="GNU tests summary = TOTAL: $TOTAL / PASS: $PASS / FAIL: $FAIL / ERROR: $ERROR"
echo "${output}"
if [[ "$FAIL" -gt 0 || "$ERROR" -gt 0 ]]; then echo "::warning ::${output}" ; fi
jq -n \
--arg date "$(date --rfc-email)" \
--arg sha "$GITHUB_SHA" \
--arg total "$TOTAL" \
--arg pass "$PASS" \
--arg skip "$SKIP" \
--arg fail "$FAIL" \
--arg xpass "$XPASS" \
--arg error "$ERROR" \
'{($date): { sha: $sha, total: $total, pass: $pass, skip: $skip, fail: $fail, xpass: $xpass, error: $error, }}' > ../gnu-result.json
check_total "$TOTAL"
print_summary "GNU tests" "$TOTAL" "$PASS" "$SKIP" "$FAIL" "$ERROR"
generate_result_json "${RESULT_FILE:-$GNU_DIR/../gnu-result.json}" "$TOTAL" "$PASS" "$SKIP" "$FAIL" "$XPASS" "$ERROR"
46 changes: 46 additions & 0 deletions util/common.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/bin/bash
# Common utilities for findutils compatibility test scripts.

COMMON_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
FINDUTILS_DIR="$(cd "$COMMON_DIR/.." && pwd)"
CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-$FINDUTILS_DIR/target}"

FIND_BIN="$CARGO_TARGET_DIR/release/find"
XARGS_BIN="$CARGO_TARGET_DIR/release/xargs"

build_rust() {
echo "Building Rust findutils..."
(cd "$FINDUTILS_DIR" && cargo build --release)
}

check_total() {
local total="$1"
if (( total <= 1 )); then
echo "Error in the execution, failing early"
exit 1
fi
}

print_summary() {
local label="$1" total="$2" pass="$3" skip="$4" fail="$5" error="${6:-0}"
local output="$label summary = TOTAL: $total / PASS: $pass / SKIP: $skip / FAIL: $fail / ERROR: $error"
echo "$output"
if (( fail > 0 || error > 0 )); then
echo "::warning ::$output"
fi
}

generate_result_json() {
local output_file="$1" total="$2" pass="$3" skip="$4" fail="$5"
local xpass="${6:-0}" error="${7:-0}"
jq -n \
--arg date "$(date --rfc-email 2>/dev/null || date '+%a, %d %b %Y %T %z')" \
--arg sha "${GITHUB_SHA:-$(git -C "$FINDUTILS_DIR" rev-parse HEAD 2>/dev/null || echo unknown)}" \
--arg total "$total" \
--arg pass "$pass" \
--arg skip "$skip" \
--arg fail "$fail" \
--arg xpass "$xpass" \
--arg error "$error" \
'{($date): { sha: $sha, total: $total, pass: $pass, skip: $skip, fail: $fail, xpass: $xpass, error: $error, }}' > "$output_file"
}
Loading
Loading