diff --git a/e2e/assert.sh b/e2e/assert.sh index d0965ca02f..6250291ee7 100755 --- a/e2e/assert.sh +++ b/e2e/assert.sh @@ -94,6 +94,25 @@ timeout() { run_with_timeout "$seconds" "$@" } +wait_for_file() { + local file="$1" + local description="${2:-file}" + local attempts="${3:-30}" + local pid="${4:-}" + + for ((i = 0; i < attempts; i++)); do + if [[ -f $file ]]; then + return 0 + fi + if [[ -n $pid ]] && ! kill -0 "$pid" 2>/dev/null; then + fail "$description process $pid exited before writing $file" + fi + sleep 1 + done + + fail "$description did not appear within ${attempts}s: $file" +} + # Safeguard against running the test directly, which would execute in the actual user home [[ -n ${TEST_NAME:-} ]] || fail "tests should be called using run_test" diff --git a/e2e/backend/test_bun_cross_device_install b/e2e/backend/test_bun_cross_device_install index b7c59886b2..c0ce4f3157 100644 --- a/e2e/backend/test_bun_cross_device_install +++ b/e2e/backend/test_bun_cross_device_install @@ -4,7 +4,7 @@ set -euo pipefail TEST_DIR="$(mktemp -d)" -HTTP_PORT=8765 +HTTP_PORT_FILE="$TEST_DIR/http_port" SERVER_PID="" if [[ ! -d /dev/shm ]]; then @@ -28,7 +28,7 @@ trap cleanup EXIT # Serve a tiny Bun release fixture with a Python HTTP server so the test can # exercise the real Bun download + checksum + unzip path without relying on GitHub. -python3 -u - "$TEST_DIR" "$HTTP_PORT" <<'PY' >/dev/null 2>&1 & +python3 -u - "$TEST_DIR" "$HTTP_PORT_FILE" <<'PY' >/dev/null 2>&1 & import hashlib import io import sys @@ -37,7 +37,7 @@ from http.server import BaseHTTPRequestHandler, HTTPServer from pathlib import Path root = Path(sys.argv[1]) -port = int(sys.argv[2]) +port_file = Path(sys.argv[2]) # Remember the most recent archive request so SHASUMS256.txt can match it. state_file = root / "last-requested-zip.txt" script = b"""#!/usr/bin/env bash @@ -90,10 +90,13 @@ class Handler(BaseHTTPRequestHandler): self.send_error(404) -HTTPServer(("127.0.0.1", port), Handler).serve_forever() +server = HTTPServer(("127.0.0.1", 0), Handler) +port_file.write_text(str(server.server_address[1])) +server.serve_forever() PY SERVER_PID=$! -sleep 1 +wait_for_file "$HTTP_PORT_FILE" "HTTP test server port file" 30 "$SERVER_PID" +HTTP_PORT=$(cat "$HTTP_PORT_FILE") rm -rf "$MISE_DATA_DIR/installs/bun/1.3.10" MISE_URL_REPLACEMENTS="$(printf '{\"https://github.com/oven-sh/bun/releases/download\":\"http://127.0.0.1:%s\"}' "$HTTP_PORT")" diff --git a/e2e/backend/test_http_compressed_binaries b/e2e/backend/test_http_compressed_binaries index 5c874f84d1..77953f84c7 100755 --- a/e2e/backend/test_http_compressed_binaries +++ b/e2e/backend/test_http_compressed_binaries @@ -5,7 +5,7 @@ set -euo pipefail # Create a temporary directory for test artifacts TEST_DIR="$(mktemp -d)" -HTTP_PORT=8765 +HTTP_PORT_FILE="$TEST_DIR/http_port" SERVER_PID="" # Cleanup function @@ -37,16 +37,26 @@ if command -v zstd >/dev/null 2>&1; then fi # Start a simple HTTP server -echo "Starting HTTP server on port $HTTP_PORT..." -cd "$TEST_DIR" -python3 -m http.server $HTTP_PORT --bind 127.0.0.1 >/dev/null 2>&1 & +echo "Starting HTTP server..." +python3 -u - "$TEST_DIR" "$HTTP_PORT_FILE" <<'PY' >/dev/null 2>&1 & +import functools +import http.server +import socketserver +import sys +from pathlib import Path + +root = sys.argv[1] +port_file = Path(sys.argv[2]) +handler = functools.partial(http.server.SimpleHTTPRequestHandler, directory=root) +with socketserver.TCPServer(("127.0.0.1", 0), handler) as httpd: + port_file.write_text(str(httpd.server_address[1])) + httpd.serve_forever() +PY SERVER_PID=$! -# Go back to original directory -cd - - # Wait for server to start -sleep 2 +wait_for_file "$HTTP_PORT_FILE" "HTTP test server port file" 30 "$SERVER_PID" +HTTP_PORT=$(cat "$HTTP_PORT_FILE") # Test with different compressed formats echo "Testing .gz compressed binary..." diff --git a/e2e/backend/test_npm_package_manager b/e2e/backend/test_npm_package_manager index c591ad3f7e..ab91c6e37a 100644 --- a/e2e/backend/test_npm_package_manager +++ b/e2e/backend/test_npm_package_manager @@ -42,11 +42,12 @@ cat >.mise.toml </dev/null 2>&1 || true -assert_succeed "MISE_DEBUG=1 mise install >/tmp/npm_debug.log 2>&1" +NPM_DEBUG_LOG="$TMPDIR/npm_debug.log" +assert_succeed "MISE_DEBUG=1 mise install >'$NPM_DEBUG_LOG' 2>&1" # npm 11.10.0+ uses `--min-release-age={days}`; older npm uses `--before {ts}`. # Accept either since the underlying npm version varies between environments. -assert_matches "cat /tmp/npm_debug.log" "--(before|min-release-age)" -assert_contains "cat /tmp/npm_debug.log" "--foreground-scripts" +assert_matches "cat '$NPM_DEBUG_LOG'" "--(before|min-release-age)" +assert_contains "cat '$NPM_DEBUG_LOG'" "--foreground-scripts" echo "✓ npm backend successfully installs package using npm" mise uninstall npm:tiny@latest >/dev/null 2>&1 || true @@ -58,9 +59,10 @@ cat >.mise.toml </tmp/bun_debug.log 2>&1" -assert_contains "cat /tmp/bun_debug.log" "--minimum-release-age" -assert_contains "cat /tmp/bun_debug.log" "--verbose" +BUN_DEBUG_LOG="$TMPDIR/bun_debug.log" +assert_succeed "MISE_DEBUG=1 mise install >'$BUN_DEBUG_LOG' 2>&1" +assert_contains "cat '$BUN_DEBUG_LOG'" "--minimum-release-age" +assert_contains "cat '$BUN_DEBUG_LOG'" "--verbose" echo "✓ npm backend successfully installs package using bun (package_manager=bun)" mise uninstall npm:tiny@latest >/dev/null 2>&1 || true @@ -72,13 +74,14 @@ cat >.mise.toml </tmp/pnpm_debug.log 2>&1; then +PNPM_DEBUG_LOG="$TMPDIR/pnpm_debug.log" +if ! MISE_DEBUG=1 mise install >"$PNPM_DEBUG_LOG" 2>&1; then echo "Command failed. Output:" - cat /tmp/pnpm_debug.log + cat "$PNPM_DEBUG_LOG" exit 1 fi -assert_contains "cat /tmp/pnpm_debug.log" "--config.minimumReleaseAge=" -assert_contains "cat /tmp/pnpm_debug.log" "--loglevel=warn" +assert_contains "cat '$PNPM_DEBUG_LOG'" "--config.minimumReleaseAge=" +assert_contains "cat '$PNPM_DEBUG_LOG'" "--loglevel=warn" echo "✓ npm backend successfully installs package using pnpm (package_manager=pnpm)" mise uninstall npm:tiny@latest >/dev/null 2>&1 || true @@ -100,9 +103,10 @@ cat >.mise.toml </tmp/aube_debug.log 2>&1" -assert_contains "cat /tmp/aube_debug.log" "--reporter" -assert_contains "cat /tmp/aube_debug.log" "append-only" +AUBE_DEBUG_LOG="$TMPDIR/aube_debug.log" +assert_succeed "MISE_DEBUG=1 mise install >'$AUBE_DEBUG_LOG' 2>&1" +assert_contains "cat '$AUBE_DEBUG_LOG'" "--reporter" +assert_contains "cat '$AUBE_DEBUG_LOG'" "append-only" echo "✓ npm backend successfully installs package using aube (package_manager=aube)" mise uninstall npm:tiny@latest >/dev/null 2>&1 || true diff --git a/e2e/backend/test_s3_minio_slow b/e2e/backend/test_s3_minio_slow index de94e49f9f..dc0c872cde 100644 --- a/e2e/backend/test_s3_minio_slow +++ b/e2e/backend/test_s3_minio_slow @@ -6,29 +6,19 @@ # Install MinIO server and client mise install minio@latest mc@latest -# MinIO configuration -MINIO_PORT=19000 -MINIO_CONSOLE_PORT=19001 +find_available_port() { + python3 -c "import socket; s=socket.socket(); s.bind(('127.0.0.1',0)); print(s.getsockname()[1]); s.close()" +} + MINIO_ROOT_USER="minioadmin" MINIO_ROOT_PASSWORD="minioadmin" -MINIO_ENDPOINT="http://127.0.0.1:$MINIO_PORT" # Use /tmp directly to avoid disk space issues in isolated test tmp dir MINIO_DATA_DIR="/tmp/mise-minio-test-$$" +MINIO_PID="" # Create data directory mkdir -p "$MINIO_DATA_DIR" -# Start MinIO server in the background using mise x -# MINIO_CI_CD=1 relaxes storage constraints for CI/CD environments -MINIO_ROOT_USER="$MINIO_ROOT_USER" \ - MINIO_ROOT_PASSWORD="$MINIO_ROOT_PASSWORD" \ - MINIO_CI_CD=1 \ - mise x minio@latest -- minio server "$MINIO_DATA_DIR" \ - --address ":$MINIO_PORT" \ - --console-address ":$MINIO_CONSOLE_PORT" \ - >/dev/null 2>&1 & -MINIO_PID=$! - # Cleanup function cleanup() { if [[ -n ${MINIO_PID:-} ]]; then @@ -39,19 +29,51 @@ cleanup() { } trap cleanup EXIT -# Wait for MinIO to be ready -echo "Waiting for MinIO to start..." -for i in {1..30}; do - if curl -s "$MINIO_ENDPOINT/minio/health/ready" >/dev/null 2>&1; then - echo "MinIO is ready" - break - fi - if [[ $i -eq 30 ]]; then - echo "MinIO failed to start" - exit 1 - fi - sleep 1 -done +start_minio() { + for attempt in {1..5}; do + MINIO_PORT=$(find_available_port) + MINIO_CONSOLE_PORT=$(find_available_port) + if [[ $MINIO_CONSOLE_PORT == "$MINIO_PORT" ]]; then + continue + fi + MINIO_ENDPOINT="http://127.0.0.1:$MINIO_PORT" + + # Start MinIO server in the background using mise x + # MINIO_CI_CD=1 relaxes storage constraints for CI/CD environments + MINIO_ROOT_USER="$MINIO_ROOT_USER" \ + MINIO_ROOT_PASSWORD="$MINIO_ROOT_PASSWORD" \ + MINIO_CI_CD=1 \ + mise x minio@latest -- minio server "$MINIO_DATA_DIR" \ + --address ":$MINIO_PORT" \ + --console-address ":$MINIO_CONSOLE_PORT" \ + >/dev/null 2>&1 & + MINIO_PID=$! + + echo "Waiting for MinIO to start on port $MINIO_PORT..." + for _ in {1..10}; do + if curl -s "$MINIO_ENDPOINT/minio/health/ready" >/dev/null 2>&1; then + echo "MinIO is ready" + return 0 + fi + if ! kill -0 "$MINIO_PID" 2>/dev/null; then + break + fi + sleep 1 + done + + kill "$MINIO_PID" 2>/dev/null || true + wait "$MINIO_PID" 2>/dev/null || true + MINIO_PID="" + echo "MinIO did not start on attempt $attempt, retrying with new ports..." + done + + echo "MinIO failed to start" + return 1 +} + +if ! start_minio; then + exit 1 +fi # Configure mc (MinIO client) using mise x mise x mc@latest -- mc alias set testminio "$MINIO_ENDPOINT" "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD" --api S3v4 diff --git a/e2e/cli/test_install_inactive_hint b/e2e/cli/test_install_inactive_hint index b8b49e10f3..ccb7dc0a8f 100644 --- a/e2e/cli/test_install_inactive_hint +++ b/e2e/cli/test_install_inactive_hint @@ -26,10 +26,11 @@ mise use --rm dummy # Test: Installing a tool that's only configured via MISE__VERSION env # var should NOT show the hint (regression test for #9522 review feedback — # the config-files-only check was missing env-var-configured tools). -MISE_DUMMY_VERSION=1.0.0 mise install dummy 2>&1 | tee /tmp/mise-install-env.log -assert_not_contains "cat /tmp/mise-install-env.log" "not activated" -MISE_DUMMY_VERSION=1.0.0 mise install dummy@1.0.0 2>&1 | tee /tmp/mise-install-env.log -assert_not_contains "cat /tmp/mise-install-env.log" "not activated" +INSTALL_ENV_LOG="$TMPDIR/mise-install-env.log" +MISE_DUMMY_VERSION=1.0.0 mise install dummy 2>&1 | tee "$INSTALL_ENV_LOG" +assert_not_contains "cat '$INSTALL_ENV_LOG'" "not activated" +MISE_DUMMY_VERSION=1.0.0 mise install dummy@1.0.0 2>&1 | tee "$INSTALL_ENV_LOG" +assert_not_contains "cat '$INSTALL_ENV_LOG'" "not activated" # (Backend-alias coverage for env-var-configured tools — e.g. that # MISE_NODEJS_VERSION matches `node` — lives in the tool_env_vars unit test # in src/toolset/tool_request_set.rs; we don't repeat it here because diff --git a/e2e/config/test_netrc b/e2e/config/test_netrc index 284490caa3..9c260eb1e3 100755 --- a/e2e/config/test_netrc +++ b/e2e/config/test_netrc @@ -100,26 +100,26 @@ else echo "SKIP: Permission check test (non-Unix OS)" fi -# Find available port -find_available_port() { - python3 -c "import socket; s=socket.socket(); s.bind(('',0)); print(s.getsockname()[1]); s.close()" -} - # Start local HTTP test server with header logging HEADERS_LOG_DIR=$(mktemp -d) -SERVER_PORT=$(find_available_port) -python3 "${TEST_ROOT}/helpers/scripts/http_test_server.py" "$SERVER_PORT" "$HEADERS_LOG_DIR" & -SERVER_PID=$! -sleep 1 +HTTP_PORT_FILE="$TMPDIR/mise_http_test_port" +SERVER_PID="" # Ensure cleanup on exit cleanup() { - kill "$SERVER_PID" 2>/dev/null || true + if [[ -n $SERVER_PID ]]; then + kill "$SERVER_PID" 2>/dev/null || true + fi rm -rf "$HEADERS_LOG_DIR" - rm -f /tmp/mise_http_test_port + rm -f "${MISE_HTTP_TEST_PORT_FILE:-$TMPDIR/mise_http_test_port}" } trap cleanup EXIT +MISE_HTTP_TEST_PORT_FILE="$HTTP_PORT_FILE" python3 "${TEST_ROOT}/helpers/scripts/http_test_server.py" 0 "$HEADERS_LOG_DIR" & +SERVER_PID=$! +wait_for_file "$HTTP_PORT_FILE" "HTTP test server port file" 30 "$SERVER_PID" +SERVER_PORT=$(cat "$HTTP_PORT_FILE") + # Test 8: Verify Authorization header is sent when netrc has matching credentials cat >"$HOME/.netrc" < 1 else 0 - start_server(port) \ No newline at end of file + start_server(port) diff --git a/e2e/helpers/scripts/http_test_server.py b/e2e/helpers/scripts/http_test_server.py index a597d98343..8f95dc91ba 100644 --- a/e2e/helpers/scripts/http_test_server.py +++ b/e2e/helpers/scripts/http_test_server.py @@ -11,6 +11,7 @@ import http.server import json +import os import socketserver import sys from pathlib import Path @@ -65,33 +66,24 @@ def log_message(self, format, *args): pass -def find_available_port(): - """Find an available port starting from 8080""" - import socket - for port in range(8080, 8200): - try: - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - s.bind(('', port)) - return port - except OSError: - continue - raise RuntimeError("No available ports found") - - def start_server(port=None, headers_log_dir=None): """Start the HTTP test server""" global HEADERS_LOG_DIR HEADERS_LOG_DIR = headers_log_dir if port is None: - port = find_available_port() + port = 0 with socketserver.TCPServer(("", port), TestFileHandler) as httpd: - print(f"HTTP test server running on port {port}") + actual_port = httpd.server_address[1] + print(f"HTTP test server running on port {actual_port}") - # Save port info for tests - with open('/tmp/mise_http_test_port', 'w') as f: - f.write(str(port)) + # Save port info for tests. The e2e harness can place this under the + # per-test TMPDIR so parallel runs do not share state. + port_file = Path(os.environ.get('MISE_HTTP_TEST_PORT_FILE', '/tmp/mise_http_test_port')) + port_file.parent.mkdir(parents=True, exist_ok=True) + with open(port_file, 'w') as f: + f.write(str(actual_port)) try: httpd.serve_forever() @@ -99,7 +91,7 @@ def start_server(port=None, headers_log_dir=None): print("\nShutting down...") finally: # Clean up port file - Path('/tmp/mise_http_test_port').unlink(missing_ok=True) + port_file.unlink(missing_ok=True) if __name__ == '__main__': diff --git a/e2e/helpers/scripts/tool_stub_test_server.py b/e2e/helpers/scripts/tool_stub_test_server.py index c374a2d0f6..aafa93daf1 100755 --- a/e2e/helpers/scripts/tool_stub_test_server.py +++ b/e2e/helpers/scripts/tool_stub_test_server.py @@ -5,6 +5,7 @@ """ import http.server +import os import socketserver import sys import json @@ -51,7 +52,13 @@ def log_message(self, format, *args): def start_server(port): """Start the HTTP test server""" with socketserver.TCPServer(("127.0.0.1", port), ToolStubTestHandler) as httpd: - print(f"Tool stub test server running on port {port}", flush=True) + actual_port = httpd.server_address[1] + port_file = os.environ.get("MISE_TOOL_STUB_TEST_PORT_FILE") + if port_file: + port_file_path = Path(port_file) + port_file_path.parent.mkdir(parents=True, exist_ok=True) + port_file_path.write_text(str(actual_port)) + print(f"Tool stub test server running on port {actual_port}", flush=True) try: httpd.serve_forever() except KeyboardInterrupt: @@ -62,4 +69,4 @@ def start_server(port): print("Usage: tool_stub_test_server.py ", file=sys.stderr) sys.exit(1) port = int(sys.argv[1]) - start_server(port) \ No newline at end of file + start_server(port) diff --git a/e2e/run_all_tests b/e2e/run_all_tests index 57709c171d..d50c398c60 100755 --- a/e2e/run_all_tests +++ b/e2e/run_all_tests @@ -59,6 +59,9 @@ MISE_BIN="$(resolve_mise_bin)" test_count=0 skipped_count=0 status=0 +RUN_FILES=() +SUMMARY_ENTRY_TYPES=() +SUMMARY_ENTRY_VALUES=() summary "Test" "Duration" "Result" summary "---" "---" "---" @@ -77,19 +80,152 @@ for index in "${!FILES[@]}"; do if [[ ${TEST_ALL:-0} != 1 && $TEST_NAME == *_slow ]]; then # title="E2E test $TEST_NAME skipped" file="e2e/$TEST_NAME" warn "slow tests are disabled" skipped_count=$((skipped_count + 1)) - summary "$TEST_NAME" "-" ":zap:" + SUMMARY_ENTRY_TYPES+=("skip") + SUMMARY_ENTRY_VALUES+=("$TEST_NAME") continue fi - mise_wait_for_gh_rate_limit "$MISE_BIN" - # Actually run the test - "$ROOT/e2e/run_test" "$TEST_NAME" # fail fast for now - # if ! "$ROOT/e2e/run_test" "$TEST_NAME"; then - # status=1 - # fi - test_count=$((test_count + 1)) + SUMMARY_ENTRY_TYPES+=("run") + SUMMARY_ENTRY_VALUES+=("${#RUN_FILES[@]}") + RUN_FILES+=("$TEST_NAME") done +cpu_count() { + if command -v getconf >/dev/null 2>&1; then + getconf _NPROCESSORS_ONLN 2>/dev/null && return + fi + if command -v sysctl >/dev/null 2>&1; then + sysctl -n hw.ncpu 2>/dev/null && return + fi + echo 4 +} + +default_jobs="$(cpu_count)" +if [[ ! $default_jobs =~ ^[0-9]+$ || $default_jobs -lt 1 ]]; then + default_jobs=4 +fi +if [[ $default_jobs -gt 4 ]]; then + default_jobs=4 +fi +E2E_JOBS="${E2E_JOBS:-$default_jobs}" +if [[ ! $E2E_JOBS =~ ^[0-9]+$ || $E2E_JOBS -lt 1 ]]; then + err "E2E_JOBS must be a positive integer" + exit 1 +fi + +PIDS=() +LOG_FILES=() +SUMMARY_FILES=() +STATUS_FILES=() +FINISHED=() +ORIGINAL_GITHUB_STEP_SUMMARY="${GITHUB_STEP_SUMMARY:-}" + +LOG_DIR="$(mktemp -d "${TMPDIR:-/tmp}/mise-e2e-logs.XXXXXX")" +# shellcheck disable=SC2329 +cleanup() { + if ((${#PIDS[@]} > 0)); then + kill "${PIDS[@]}" 2>/dev/null || true + fi + rm -rf "$LOG_DIR" +} +trap 'cleanup' EXIT +trap 'cleanup; exit 130' INT TERM + +total=${#RUN_FILES[@]} +next=0 +active=0 +completed=0 +printed=0 + +start_test() { + local index="$1" + local test_name="${RUN_FILES[$index]}" + local log_file="$LOG_DIR/$index.log" + local summary_file="$LOG_DIR/$index.summary" + local status_file="$LOG_DIR/$index.status" + + LOG_FILES[index]="$log_file" + SUMMARY_FILES[index]="$summary_file" + STATUS_FILES[index]="$status_file" + + ( + if [[ -n $ORIGINAL_GITHUB_STEP_SUMMARY ]]; then + export GITHUB_STEP_SUMMARY="$summary_file" + else + unset GITHUB_STEP_SUMMARY + fi + test_status=0 + mise_wait_for_gh_rate_limit "$MISE_BIN" || test_status=$? + if [[ $test_status == 0 ]]; then + "$ROOT/e2e/run_test" "$test_name" || test_status=$? + fi + echo "$test_status" >"$status_file.tmp" && mv "$status_file.tmp" "$status_file" + ) >"$log_file" 2>&1 & + + PIDS[index]=$! +} + +while [[ $completed -lt $total ]]; do + while [[ $active -lt $E2E_JOBS && $next -lt $total ]]; do + start_test "$next" + next=$((next + 1)) + active=$((active + 1)) + done + + for ((index = 0; index < next; index++)); do + status_file="${STATUS_FILES[$index]:-}" + if [[ ${FINISHED[$index]:-0} == 0 && -n $status_file ]]; then + if [[ -f $status_file ]]; then + FINISHED[index]=1 + active=$((active - 1)) + completed=$((completed + 1)) + elif ! kill -0 "${PIDS[$index]}" 2>/dev/null; then + if [[ ! -f $status_file ]]; then + echo "test process exited without writing status: ${RUN_FILES[$index]}" >>"${LOG_FILES[$index]}" + echo "1" >"$status_file.tmp" && mv "$status_file.tmp" "$status_file" + fi + FINISHED[index]=1 + active=$((active - 1)) + completed=$((completed + 1)) + fi + fi + done + + while [[ $printed -lt $total && ${FINISHED[$printed]:-0} == 1 ]]; do + cat "${LOG_FILES[$printed]}" + test_status="$(cat "${STATUS_FILES[$printed]}")" + test_status="${test_status:-1}" + if [[ $test_status != 0 ]]; then + status=1 + fi + test_count=$((test_count + 1)) + printed=$((printed + 1)) + done + + if [[ $completed -lt $total ]]; then + sleep 0.2 + fi +done + +if ((${#PIDS[@]} > 0)); then + for pid in "${PIDS[@]}"; do + wait "$pid" || true + done +fi + +if ((${#SUMMARY_ENTRY_TYPES[@]} > 0)); then + for index in "${!SUMMARY_ENTRY_TYPES[@]}"; do + if [[ ${SUMMARY_ENTRY_TYPES[$index]} == skip ]]; then + summary "${SUMMARY_ENTRY_VALUES[$index]}" "-" ":zap:" + else + run_index="${SUMMARY_ENTRY_VALUES[$index]}" + if [[ -n $ORIGINAL_GITHUB_STEP_SUMMARY && -f ${SUMMARY_FILES[$run_index]} ]]; then + cat "${SUMMARY_FILES[$run_index]}" >>"$ORIGINAL_GITHUB_STEP_SUMMARY" + fi + fi + done +fi + echo "E2E: ran $test_count tests, skipped $skipped_count tests" >&2 exit "$status" diff --git a/e2e/run_test b/e2e/run_test index e277540f98..2898fae691 100755 --- a/e2e/run_test +++ b/e2e/run_test @@ -94,6 +94,7 @@ within_isolated_env() { MISE_DEBUG="${MISE_DEBUG:-0}" \ MISE_EXPERIMENTAL=1 \ MISE_GPG_VERIFY="${MISE_GPG_VERIFY:-}" \ + MISE_HTTP_TEST_PORT_FILE="$TEST_TMPDIR/mise_http_test_port" \ MISE_LOG_LEVEL="${MISE_LOG_LEVEL:-}" \ MISE_STATE_DIR="$MISE_STATE_DIR" \ MISE_SYSTEM_DIR="$MISE_SYSTEM_DIR" \ diff --git a/e2e/tasks/test_remote_task_with_tools b/e2e/tasks/test_remote_task_with_tools index 5552c96dfe..ed14aba954 100755 --- a/e2e/tasks/test_remote_task_with_tools +++ b/e2e/tasks/test_remote_task_with_tools @@ -50,19 +50,21 @@ mise trust # Run the simple local task to see if rg is available echo "Testing local task with rg:" -if mise run test_tool 2>&1 | tee /tmp/test_tool_output.txt | grep -q "ripgrep"; then +TEST_TOOL_OUTPUT="$TMPDIR/test_tool_output.txt" +if mise run test_tool 2>&1 | tee "$TEST_TOOL_OUTPUT" | grep -q "ripgrep"; then echo "✓ Local task can find rg" else echo "✗ Local task cannot find rg" - cat /tmp/test_tool_output.txt + cat "$TEST_TOOL_OUTPUT" fi # Now run the remote task echo "Testing remote task with rg:" -if mise run remote_lint 2>&1 | tee /tmp/remote_lint_output.txt | grep -E "(command not found|rg: not found)"; then +REMOTE_LINT_OUTPUT="$TMPDIR/remote_lint_output.txt" +if mise run remote_lint 2>&1 | tee "$REMOTE_LINT_OUTPUT" | grep -E "(command not found|rg: not found)"; then echo "✗ FAIL: Remote task could not find ripgrep from parent config" echo "Remote task output:" - cat /tmp/remote_lint_output.txt + cat "$REMOTE_LINT_OUTPUT" exit 1 else echo "✓ PASS: Remote task successfully accessed ripgrep from parent config" diff --git a/e2e/tasks/test_task_remote_dep_no_warning b/e2e/tasks/test_task_remote_dep_no_warning index ed62371d68..ef9878fa38 100644 --- a/e2e/tasks/test_task_remote_dep_no_warning +++ b/e2e/tasks/test_task_remote_dep_no_warning @@ -9,16 +9,23 @@ # Setup - start local git HTTP server ################################################################################# -rm -f /tmp/mise_git_http_port /tmp/mise_git_http_ready /tmp/mise_git_http_info +PORT_FILE="$TMPDIR/mise_git_http_port" +READY_FILE="$TMPDIR/mise_git_http_ready" +INFO_FILE="$TMPDIR/mise_git_http_info" -python3 "${TEST_ROOT}/helpers/scripts/git_http_backend_server.py" 0 & +rm -f "$PORT_FILE" "$READY_FILE" "$INFO_FILE" + +MISE_GIT_HTTP_PORT_FILE="$PORT_FILE" \ + MISE_GIT_HTTP_READY_FILE="$READY_FILE" \ + MISE_GIT_HTTP_INFO_FILE="$INFO_FILE" \ + python3 "${TEST_ROOT}/helpers/scripts/git_http_backend_server.py" 0 & SERVER_PID=$! wait_for_server() { local max_attempts=30 local attempt=1 while [ $attempt -le $max_attempts ]; do - if [ -f /tmp/mise_git_http_ready ] && [ -f /tmp/mise_git_http_port ]; then + if [ -f "$READY_FILE" ] && [ -f "$PORT_FILE" ]; then return 0 fi sleep 1 @@ -30,14 +37,14 @@ wait_for_server() { } wait_for_server -SERVER_PORT=$(cat /tmp/mise_git_http_port) +SERVER_PORT=$(cat "$PORT_FILE") LOCAL_GIT_URL="http://localhost:${SERVER_PORT}/repo.git" git init cleanup() { kill "$SERVER_PID" 2>/dev/null || true - rm -f /tmp/mise_git_http_port /tmp/mise_git_http_ready /tmp/mise_git_http_info + rm -f "$PORT_FILE" "$READY_FILE" "$INFO_FILE" } trap cleanup EXIT diff --git a/e2e/tasks/test_task_remote_git_https b/e2e/tasks/test_task_remote_git_https index 79738c40fb..d4545c0428 100644 --- a/e2e/tasks/test_task_remote_git_https +++ b/e2e/tasks/test_task_remote_git_https @@ -4,11 +4,18 @@ # Setup ################################################################################# +PORT_FILE="$TMPDIR/mise_git_http_port" +READY_FILE="$TMPDIR/mise_git_http_ready" +INFO_FILE="$TMPDIR/mise_git_http_info" + # Clean up any previous server files -rm -f /tmp/mise_git_http_port /tmp/mise_git_http_ready /tmp/mise_git_http_info +rm -f "$PORT_FILE" "$READY_FILE" "$INFO_FILE" # Start local git HTTP server with OS-assigned port to avoid race conditions -python3 "${TEST_ROOT}/helpers/scripts/git_http_backend_server.py" 0 & +MISE_GIT_HTTP_PORT_FILE="$PORT_FILE" \ + MISE_GIT_HTTP_READY_FILE="$READY_FILE" \ + MISE_GIT_HTTP_INFO_FILE="$INFO_FILE" \ + python3 "${TEST_ROOT}/helpers/scripts/git_http_backend_server.py" 0 & SERVER_PID=$! # Wait for server to be ready (with timeout) @@ -17,7 +24,7 @@ wait_for_server() { local attempt=1 while [ $attempt -le $max_attempts ]; do - if [ -f /tmp/mise_git_http_ready ] && [ -f /tmp/mise_git_http_port ]; then + if [ -f "$READY_FILE" ] && [ -f "$PORT_FILE" ]; then return 0 fi sleep 1 @@ -32,7 +39,7 @@ wait_for_server() { wait_for_server # Read the actual port from the file -SERVER_PORT=$(cat /tmp/mise_git_http_port) +SERVER_PORT=$(cat "$PORT_FILE") LOCAL_GIT_URL="http://localhost:${SERVER_PORT}/repo.git" # Update cache directory paths for local server @@ -46,7 +53,7 @@ git init # Ensure cleanup on exit cleanup() { kill "$SERVER_PID" 2>/dev/null || true - rm -f /tmp/mise_git_http_port /tmp/mise_git_http_ready /tmp/mise_git_http_info + rm -f "$PORT_FILE" "$READY_FILE" "$INFO_FILE" } trap cleanup EXIT diff --git a/e2e/tasks/test_task_remote_git_includes b/e2e/tasks/test_task_remote_git_includes index 7546e5fe51..2c67140c38 100755 --- a/e2e/tasks/test_task_remote_git_includes +++ b/e2e/tasks/test_task_remote_git_includes @@ -4,11 +4,18 @@ # Setup ################################################################################# +PORT_FILE="$TMPDIR/mise_git_http_port" +READY_FILE="$TMPDIR/mise_git_http_ready" +INFO_FILE="$TMPDIR/mise_git_http_info" + # Clean up any previous server files -rm -f /tmp/mise_git_http_port /tmp/mise_git_http_ready /tmp/mise_git_http_info +rm -f "$PORT_FILE" "$READY_FILE" "$INFO_FILE" # Start local git HTTP server with OS-assigned port to avoid race conditions -python3 "${TEST_ROOT}/helpers/scripts/git_http_backend_server.py" 0 & +MISE_GIT_HTTP_PORT_FILE="$PORT_FILE" \ + MISE_GIT_HTTP_READY_FILE="$READY_FILE" \ + MISE_GIT_HTTP_INFO_FILE="$INFO_FILE" \ + python3 "${TEST_ROOT}/helpers/scripts/git_http_backend_server.py" 0 & SERVER_PID=$! # Wait for server to be ready (with timeout) @@ -17,7 +24,7 @@ wait_for_server() { local attempt=1 while [ $attempt -le $max_attempts ]; do - if [ -f /tmp/mise_git_http_ready ] && [ -f /tmp/mise_git_http_port ]; then + if [ -f "$READY_FILE" ] && [ -f "$PORT_FILE" ]; then return 0 fi sleep 1 @@ -32,7 +39,7 @@ wait_for_server() { wait_for_server # Read the actual port from the file -SERVER_PORT=$(cat /tmp/mise_git_http_port) +SERVER_PORT=$(cat "$PORT_FILE") LOCAL_GIT_URL="http://localhost:${SERVER_PORT}/repo.git" # Update cache directory paths for local server @@ -43,7 +50,7 @@ git init # Ensure cleanup on exit cleanup() { kill "$SERVER_PID" 2>/dev/null || true - rm -f /tmp/mise_git_http_port /tmp/mise_git_http_ready /tmp/mise_git_http_info + rm -f "$PORT_FILE" "$READY_FILE" "$INFO_FILE" } trap cleanup EXIT diff --git a/e2e/tasks/test_task_remote_http b/e2e/tasks/test_task_remote_http index 6a1ce0c91c..8a5d0228d8 100644 --- a/e2e/tasks/test_task_remote_http +++ b/e2e/tasks/test_task_remote_http @@ -4,13 +4,9 @@ # Setup ################################################################################# -find_available_port() { - python3 -c "import socket; s=socket.socket(); s.bind(('',0)); print(s.getsockname()[1]); s.close()" -} - # Start HTTP test server -HTTP_PORT=$(find_available_port) -python3 "${TEST_ROOT}/helpers/scripts/http_test_server.py" "$HTTP_PORT" & +HTTP_PORT_FILE="$TMPDIR/mise_http_test_port" +MISE_HTTP_TEST_PORT_FILE="$HTTP_PORT_FILE" python3 "${TEST_ROOT}/helpers/scripts/http_test_server.py" 0 & HTTP_SERVER_PID=$! # Ensure cleanup on exit @@ -20,7 +16,8 @@ cleanup() { trap cleanup EXIT # Wait for server to start -sleep 1 +wait_for_file "$HTTP_PORT_FILE" "HTTP test server port file" 30 "$HTTP_SERVER_PID" +HTTP_PORT=$(cat "$HTTP_PORT_FILE") cat <mise.toml [tasks.remote_http_task] diff --git a/e2e/tasks/test_task_standalone b/e2e/tasks/test_task_standalone index da3948514d..a506357b8a 100644 --- a/e2e/tasks/test_task_standalone +++ b/e2e/tasks/test_task_standalone @@ -1,22 +1,18 @@ #!/usr/bin/env bash -# Find available port -find_available_port() { - python3 -c "import socket; s=socket.socket(); s.bind(('',0)); print(s.getsockname()[1]); s.close()" -} - # Start local HTTP test server -SERVER_PORT=$(find_available_port) -python3 "${TEST_ROOT}/helpers/scripts/http_test_server.py" "$SERVER_PORT" & +HTTP_PORT_FILE="$TMPDIR/mise_http_test_port" +MISE_HTTP_TEST_PORT_FILE="$HTTP_PORT_FILE" python3 "${TEST_ROOT}/helpers/scripts/http_test_server.py" 0 & SERVER_PID=$! # Wait for server to start -sleep 1 +wait_for_file "$HTTP_PORT_FILE" "HTTP test server port file" 30 "$SERVER_PID" +SERVER_PORT=$(cat "$HTTP_PORT_FILE") # Ensure cleanup on exit cleanup() { kill "$SERVER_PID" 2>/dev/null || true - rm -f /tmp/mise_http_test_port + rm -f "${MISE_HTTP_TEST_PORT_FILE:-$TMPDIR/mise_http_test_port}" } trap cleanup EXIT diff --git a/tasks.toml b/tasks.toml index 7a7e79c1fe..ff730e9f7e 100644 --- a/tasks.toml +++ b/tasks.toml @@ -139,6 +139,11 @@ description = "run unit tests" run = "cargo test --all-features" env = { CARGO_TERM_COLOR = "always", "RUST_TEST_THREADS" = "1" } +["test:e2e"] +description = "run end-to-end tests" +depends = ["build"] +run = "e2e/run_all_tests" + ["test:shuffle"] description = 'Run tests with shuffling enabled' run = 'cargo +nightly test --all-features -- -Z unstable-options --shuffle'