diff --git a/Dockerfile b/Dockerfile index 3faf085..6a4f528 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,27 +1,45 @@ ### Test Image specific config +FROM rustlang/rust:nightly-alpine -FROM rust:1.90-slim-trixie - -RUN apt-get update -RUN apt-get -y install pkg-config libssl-dev moreutils +# Install required packages +RUN apk add --no-cache \ + bash \ + coreutils \ + findutils \ + gawk \ + grep \ + sed \ + parallel \ + pkgconfig \ + openssl-dev \ + build-base \ + moreutils WORKDIR /app + +# Copy project structure COPY tests tests COPY tests_utility tests_utility COPY solutions solutions + +# Use sparse index for Cargo (faster network) ENV CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse -RUN parallel cargo fetch --manifest-path -- $(find tests -name Cargo.toml) + +# Pre-fetch all dependencies +RUN find tests -name Cargo.toml | parallel cargo fetch --manifest-path {} + +# Fix permissions for Rust crates (avoid “permission denied”) RUN find /usr/local/cargo/registry/src -type f -name '*.rs' -exec chmod 644 {} \; + +# Remove solutions (only student code will be mounted) RUN rm -rf solutions -### Default configs -# ℹ️ URL of the Repository -LABEL org.opencontainers.image.source=https://github.com/01-edu/rust-tests -# ℹ️ Description of the Test Image -LABEL org.opencontainers.image.description="01 Edu - Rust Test Image" -# ℹ️ Licence type – MIT by default -LABEL org.opencontainers.image.licenses=MIT +### Metadata labels +LABEL org.opencontainers.image.source="https://github.com/01-edu/rust-tests" +LABEL org.opencontainers.image.description="01 Edu - Rust Test Image (Alpine)" +LABEL org.opencontainers.image.licenses="MIT" +# Copy entrypoint and isolate script COPY entrypoint.sh ./ COPY isolate.sh ./ diff --git a/entrypoint.sh b/entrypoint.sh index d79bbc0..774e840 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,8 +1,6 @@ #!/usr/bin/env bash - set -eo pipefail -IFS=' -' +IFS=$'\n' # support both variables CODE_EDITOR_RUN_ONLY and EXAM_RUN_ONLY CODE_EDITOR_RUN_ONLY="${CODE_EDITOR_RUN_ONLY:-$EXAM_RUN_ONLY}" @@ -13,26 +11,42 @@ cp -a /app/tests . cp -a /app/isolate.sh . cp -a student solutions -if test "$CODE_EDITOR_MODE"; then - cd "solutions/$EXERCISE" - # ! to support both the old and the new version of the runner we - # ! need to check the files in the code editor - if ! echo "$EDITOR_FILES" | tr ',' '\n' | grep -q 'src/main.rs'; then - if test "$CODE_EDITOR_RUN_ONLY"; then - mv src/lib.rs src/main.rs 2>&1 ||: - fi - fi - cargo init - cd +if [ "$CODE_EDITOR_MODE" ]; then + cd "solutions/$EXERCISE" + # Support both old/new code editor runners + if ! echo "$EDITOR_FILES" | tr ',' '\n' | grep -q 'src/main.rs'; then + if [ "$CODE_EDITOR_RUN_ONLY" ]; then + mv src/lib.rs src/main.rs 2>/dev/null || true + fi + fi + cargo init + cd - fi -if ! test -f "tests/${EXERCISE}_test/Cargo.toml"; then - echo "No test file found for the exercise : $EXERCISE" - exit 1 +if [ ! -f "tests/${EXERCISE}_test/Cargo.toml" ]; then + echo "No test file found for the exercise: $EXERCISE" + exit 1 fi -if test "$CODE_EDITOR_RUN_ONLY"; then - cargo run --manifest-path "solutions/$EXERCISE/Cargo.toml" -- "$@" +# Ensure EXERCISE is inherited by isolate.sh +export EXERCISE +# Ensure current directory is in PATH for isolate.sh +export PATH=".:$PATH" + +if [ "$CODE_EDITOR_RUN_ONLY" ]; then + cargo run --manifest-path "solutions/$EXERCISE/Cargo.toml" -- "$@" else - cargo --config 'target."cfg(all())".runner="./isolate.sh"' test --manifest-path "tests/${EXERCISE}_test/Cargo.toml" + # 1) Compile tests first, without running + set +e + cargo test --no-run --manifest-path "tests/${EXERCISE}_test/Cargo.toml" + rc=$? + set -e + if [ "$rc" -ne 0 ]; then + echo "Solution did not compile." + exit 1 + fi + + # 2) Run tests with isolate.sh as runner + cargo --config 'target."cfg(all())".runner="./isolate.sh"' \ + test --manifest-path "tests/${EXERCISE}_test/Cargo.toml" fi diff --git a/isolate.sh b/isolate.sh index 6f0414e..ab82539 100755 --- a/isolate.sh +++ b/isolate.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # Use: -# cargo --config 'target."cfg(all())".runner="./isolate.sh"' test [args] +# cargo --config 'target."cfg(all())".runner="./isolate.sh"' test --manifest-path "tests/${EXERCISE}_test/Cargo.toml" set -u @@ -39,7 +39,7 @@ awk -F': test' '/: test/{print $1}' "$tmpdir/list.txt" | sed 's/[[:space:]]*$//' ( "$bin" "${filtered[@]}" 2>&1 echo "__RC__$?" -) | tee "$logfile" >/dev/null +) | tee "$logfile" rc="$(awk -F'__RC__' '/__RC__/ {v=$2} END{print v+0}' "$logfile")" # 3) Collect tests that actually produced a result line: @@ -56,18 +56,18 @@ awk ' } ' "$logfile" | sed 's/[[:space:]]*$//' > "$actual" -# 4) Decide: require (a) rc==0, (b) final summary ok, (c) every expected test appeared -if [[ "$rc" -eq 0 ]] && grep -Eq '^test result: ok\.' "$logfile"; then - sort -u "$expected" -o "$expected" - sort -u "$actual" -o "$actual" - # If no tests were expected (filters matched none), that's fine too. - if comm -23 "$expected" "$actual" | read -r _; then - # there were missing tests → failure - echo "Some tests weren't ran for the exercise \`$EXERCISE\`. Perhaps the solution forcefully exits?" - exit 1 - else - exit 0 - fi +# 4) Decide overall success/failure +sort -u "$expected" -o "$expected" +sort -u "$actual" -o "$actual" + +missing=0 +if comm -23 "$expected" "$actual" | read -r _; then + echo "Some tests weren't ran for the exercise \`$EXERCISE\`. Perhaps the solution forcefully exits?" + missing=1 +fi + +if [[ "$rc" -eq 0 ]] && grep -Eq '^test result: ok\.' "$logfile" && [[ $missing -eq 0 ]]; then + exit 0 fi exit 1