diff --git a/new_changelogs/20261109T225917Z-changed-openbsd-test-portability.md b/new_changelogs/20261109T225917Z-changed-openbsd-test-portability.md new file mode 100644 index 000000000..87cfdedfc --- /dev/null +++ b/new_changelogs/20261109T225917Z-changed-openbsd-test-portability.md @@ -0,0 +1 @@ +- **The shell test suite runs on OpenBSD** (`tests/lib/portable.sh`, `tests/lib/syslibs.sh`, `tests/lib/test_openssl_conf.sh`, and the tests that use them). Hand-written C link lines name `-ldl`/`-lrt` only on Linux (`$AE_DL_LIBS`, `$AE_RT_LIBS`); `head -c`, `sha256sum`/`shasum`, `tar -T`, `nm -D`, `od -tu` (OpenBSD zero-pads it) and `file` (which prints `data` for wasm, COFF and Mach-O, and `AArch64` for `aarch64`) go through `head_c`, `sha256_of`, `copy_listed`, `dyn_syms`, `od_uint` and `file_desc`; the 75 `grep "a\|b"` alternations, which OpenBSD's grep reads as a literal `|` (so a "warning filter" let warnings into compared output), are `grep -E`; `\b` and `\s` are POSIX classes; `grep -r --exclude` is `find` (OpenBSD matches `--exclude` against the whole path). `x509 -req` is told `-sha256`: LibreSSL signs with SHA-1 by default and the pure TLS stack rightly refuses that. Tests whose subject cannot exist on the host skip and say why: `ae version install` where there is no release build, musl targets where zig cannot build musl's libc.a, dual-stack `::` where sockets are IPv6-only, the poisoning allocator where libc has no `malloc_usable_size`, FORTIFY where libc has no `__*_chk`, `spawn_sandboxed` off Linux and FreeBSD, and the checks that need OpenSSL's `ssl_conf`, `s_server -cert_chain`, `s_client -sigalgs` or Ed25519 certificates in TLS. diff --git a/new_changelogs/20261109T225917Z-fixed-openbsd-harness-product.md b/new_changelogs/20261109T225917Z-fixed-openbsd-harness-product.md new file mode 100644 index 000000000..aeca55792 --- /dev/null +++ b/new_changelogs/20261109T225917Z-fixed-openbsd-harness-product.md @@ -0,0 +1 @@ +- **OpenBSD: four toolchain and runtime gaps the test suite exposed** (`tools/ae.c`, `tools/ae_checksec.c`, `std/os/aether_os.c`, `std/net/aether_http.c`). A program importing `std.audio` no longer fails to link with "unable to find library -ldl": OpenBSD keeps `dlopen` in libc, and `ae` now leaves the `-ldl` that codegen's link table names for `std.audio` off the link line there (as it already does on MinGW). `ae checksec` reads OpenBSD's RETGUARD return-address cookies (`__retguard_*`, and `__stack_smash_handler`) as the stack canary; it reported `canary no` for every OpenBSD binary because that clang does not emit `__stack_chk_fail` for a plain stack array. A spawned child no longer keeps a close-on-exec pipe that took a closed stdout's number as its stdout on OpenBSD and NetBSD: their pipes all report device 0, inode 0, and the pipe time that told them apart is the kernel's coarse clock (10 ms), so a spawn pipe made within a tick of the process start was taken for the stream itself; a stream that was not close-on-exec at start can no longer match a close-on-exec one (`os_spawn_stdio_slot` failed there). The OpenSSL-backed HTTPS client now honours `SSL_CERT_FILE` and `SSL_CERT_DIR` when built against LibreSSL, which ignores both in `SSL_CTX_set_default_verify_paths` (the pure-Aether client and OpenSSL already did). diff --git a/std/net/aether_http.c b/std/net/aether_http.c index d01211ecf..ffb420d4f 100644 --- a/std/net/aether_http.c +++ b/std/net/aether_http.c @@ -539,7 +539,26 @@ static SSL_CTX* get_ssl_ctx(void) { #else // Linux/macOS: the system trust store lives at the path OpenSSL was // compiled with — /etc/ssl/certs and the macOS keychain bridge. +#ifdef LIBRESSL_VERSION_NUMBER + // LibreSSL (OpenBSD's libcrypto) does not read SSL_CERT_FILE or + // SSL_CERT_DIR in SSL_CTX_set_default_verify_paths, where OpenSSL does and + // the pure-Aether client does. Name them here, with the same rule: when + // either is set it is the whole store, and one that cannot be loaded + // leaves no anchors, so the handshake fails closed. + const char* env_file = getenv("SSL_CERT_FILE"); + const char* env_dir = getenv("SSL_CERT_DIR"); + if (env_file && !env_file[0]) env_file = NULL; + if (env_dir && !env_dir[0]) env_dir = NULL; + if (env_file || env_dir) { + if (env_file) SSL_CTX_load_verify_locations(ctx, env_file, NULL); + if (env_dir) SSL_CTX_load_verify_locations(ctx, NULL, env_dir); + ERR_clear_error(); + } else { + SSL_CTX_set_default_verify_paths(ctx); + } +#else SSL_CTX_set_default_verify_paths(ctx); +#endif #endif SSL_CTX* expected = NULL; diff --git a/std/os/aether_os.c b/std/os/aether_os.c index 692a3d01f..7090152f6 100644 --- a/std/os/aether_os.c +++ b/std/os/aether_os.c @@ -1551,18 +1551,31 @@ static struct { int open; dev_t dev; ino_t ino; #ifdef AETHER_STD_STREAM_MADE struct timespec made; + int cloexec; /* close-on-exec when the process started */ #endif } aether_std_stream[3]; -/* Whether `st` is the file stream `fd` was at the start. */ -static int aether_std_stream_same(int fd, const struct stat* st) { +/* Whether `st` is the file stream `fd` was at the start. `fl` is the + * descriptor's flags now, and the caller only asks about a close-on-exec one. */ +static int aether_std_stream_same(int fd, const struct stat* st, int fl) { + (void)fl; if (st->st_dev != aether_std_stream[fd].dev || st->st_ino != aether_std_stream[fd].ino) return 0; #ifdef AETHER_STD_STREAM_MADE - if (st->st_dev == 0 && st->st_ino == 0 && - (AETHER_STD_STREAM_MADE(st).tv_sec != aether_std_stream[fd].made.tv_sec || - AETHER_STD_STREAM_MADE(st).tv_nsec != aether_std_stream[fd].made.tv_nsec)) - return 0; + if (st->st_dev == 0 && st->st_ino == 0) { + if (AETHER_STD_STREAM_MADE(st).tv_sec != aether_std_stream[fd].made.tv_sec || + AETHER_STD_STREAM_MADE(st).tv_nsec != aether_std_stream[fd].made.tv_nsec) + return 0; + /* The pipe's time is the kernel's coarse clock (10 ms on OpenBSD), + * so two pipes made within a tick report the same one: a spawn pipe + * that took the number of a closed stdout a few milliseconds after + * the process started looked like the stream itself. Of the two + * that cannot be told apart, only one is close-on-exec if the + * stream was not when the process started: the parent that marks its + * streams (Node) has done so before this library loads. */ + if ((fl & FD_CLOEXEC) && !aether_std_stream[fd].cloexec) + return 0; + } #endif return 1; } @@ -1577,6 +1590,7 @@ static void aether_record_std_streams(void) { aether_std_stream[fd].ino = st.st_ino; #ifdef AETHER_STD_STREAM_MADE aether_std_stream[fd].made = AETHER_STD_STREAM_MADE(&st); + aether_std_stream[fd].cloexec = (fcntl(fd, F_GETFD) & FD_CLOEXEC) != 0; #endif } } @@ -1587,7 +1601,7 @@ static void aether_child_keep_stdio(void) { int fl = fcntl(fd, F_GETFD); if (fl < 0 || !(fl & FD_CLOEXEC) || !aether_std_stream[fd].open) continue; struct stat st; - if (fstat(fd, &st) != 0 || !aether_std_stream_same(fd, &st)) + if (fstat(fd, &st) != 0 || !aether_std_stream_same(fd, &st, fl)) continue; fcntl(fd, F_SETFD, fl & ~FD_CLOEXEC); } diff --git a/tests/compiler/test_msvc_compat.sh b/tests/compiler/test_msvc_compat.sh index 92755a378..bdbd058a1 100755 --- a/tests/compiler/test_msvc_compat.sh +++ b/tests/compiler/test_msvc_compat.sh @@ -483,7 +483,7 @@ if [ -n "$use_gcc" ] && [ "${MSVC_COMPAT_SWEEP:-0}" = 1 ]; then for b in "$SWEEP_TMP"/bad_*; do n="${b#"$SWEEP_TMP"/bad_}" echo " $(cat "$b")" - grep -B1 -A2 "braced-groups\|fatal error" "$SWEEP_TMP/$n.log" 2>/dev/null | head -8 | sed 's/^/ /' + grep -B1 -A2 -E "braced-groups|fatal error" "$SWEEP_TMP/$n.log" 2>/dev/null | head -8 | sed 's/^/ /' done echo " [FAIL] $bad of the sweep's programs have GCC extensions on the non-GCC path" exit 1 diff --git a/tests/integration/abi_matrix/test_abi_matrix.sh b/tests/integration/abi_matrix/test_abi_matrix.sh index a285aadf4..181092c97 100644 --- a/tests/integration/abi_matrix/test_abi_matrix.sh +++ b/tests/integration/abi_matrix/test_abi_matrix.sh @@ -41,6 +41,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" AE="$ROOT/build/ae" CC="${CC:-cc}" export AETHER_STACK_TRACE=0 @@ -269,7 +270,7 @@ preflight: refused, the library was made for Aether runtime "*) ;; esac if (cd "$TMP/oldsh" && cp "$SCRIPT_DIR/app.ae" . && AETHER_HOME="$ROOT" "$AE" build app.ae -o app > app_build.log 2>&1); then bad "this importer took the $old_version shared-runtime library" - elif ! grep -q "host ABI level 0\|was made for the Aether runtime" "$TMP/oldsh/app_build.log"; then + elif ! grep -q -E "host ABI level 0|was made for the Aether runtime" "$TMP/oldsh/app_build.log"; then bad "this importer's refusal of the $old_version shared-runtime library says why" "$TMP/oldsh/app_build.log" grep -A2 '^note: .* could not be loaded' "$TMP/oldsh/app_build.log" | sed 's/^/ /' fi diff --git a/tests/integration/abi_policy/test_abi_policy.sh b/tests/integration/abi_policy/test_abi_policy.sh index c5a2af230..c36a12d5f 100644 --- a/tests/integration/abi_policy/test_abi_policy.sh +++ b/tests/integration/abi_policy/test_abi_policy.sh @@ -17,6 +17,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" AE="$ROOT/build/ae" AETHERC="$ROOT/build/aetherc" CC="${CC:-cc}" diff --git a/tests/integration/actor_ref_field_reject/test_actor_ref_field_reject.sh b/tests/integration/actor_ref_field_reject/test_actor_ref_field_reject.sh index b8431e294..c826caaab 100644 --- a/tests/integration/actor_ref_field_reject/test_actor_ref_field_reject.sh +++ b/tests/integration/actor_ref_field_reject/test_actor_ref_field_reject.sh @@ -24,7 +24,7 @@ refused() { fail "aetherc accepted $1" "$TMPDIR/err.log" fi grep -q "$2" "$TMPDIR/err.log" || fail "$1: no error containing '$2'" "$TMPDIR/err.log" - if grep -q "request for member\|not a structure or union" "$TMPDIR/err.log"; then + if grep -q -E "request for member|not a structure or union" "$TMPDIR/err.log"; then fail "$1 reached the C compiler" "$TMPDIR/err.log" fi } diff --git a/tests/integration/actor_state_ref_name_not_type/test_actor_state_ref_name_not_type.sh b/tests/integration/actor_state_ref_name_not_type/test_actor_state_ref_name_not_type.sh index dd6fdf7d1..4941dcc04 100644 --- a/tests/integration/actor_state_ref_name_not_type/test_actor_state_ref_name_not_type.sh +++ b/tests/integration/actor_state_ref_name_not_type/test_actor_state_ref_name_not_type.sh @@ -69,7 +69,7 @@ AE want="v 30 hops 3 3" -got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "$want" ]; then echo " [FAIL] actor_state_ref_name_not_type: wrong output" printf 'got:\n%s\nwant:\n%s\n' "$got" "$want" | sed 's/^/ /' @@ -172,7 +172,7 @@ main() { println("hits ${s.hits} ${t.hits} tally ${m} greeted ${gh} ${hh}") } AE -got="$("$AE" run "$tmp/uses.ae" 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$("$AE" run "$tmp/uses.ae" 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "hits 10 2 tally 42 greeted 1 1" ]; then echo " [FAIL] actor_state_ref_name_not_type: a reference field used from outside the actor" printf 'got:\n%s\nwant:\nhits 10 2 tally 42 greeted 1 1\n' "$got" | sed 's/^/ /' diff --git a/tests/integration/actor_state_typed_declarations/test_actor_state_typed_declarations.sh b/tests/integration/actor_state_typed_declarations/test_actor_state_typed_declarations.sh index 5ff4674d4..7ae76a1f6 100644 --- a/tests/integration/actor_state_typed_declarations/test_actor_state_typed_declarations.sh +++ b/tests/integration/actor_state_typed_declarations/test_actor_state_typed_declarations.sh @@ -74,7 +74,7 @@ AE want="b8 4 h16 9 big 50 null 1 q null" -got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "$want" ]; then echo " [FAIL] actor_state_typed_declarations: wrong output" printf 'got:\n%s\nwant:\n%s\n' "$got" "$want" | sed 's/^/ /' diff --git a/tests/integration/ae_add_binary_package/test_ae_add_binary_package.sh b/tests/integration/ae_add_binary_package/test_ae_add_binary_package.sh index a70eceaa3..0fc7eca39 100755 --- a/tests/integration/ae_add_binary_package/test_ae_add_binary_package.sh +++ b/tests/integration/ae_add_binary_package/test_ae_add_binary_package.sh @@ -31,6 +31,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/portable.sh" AE="$ROOT/build/ae" [ -n "${EXE_EXT:-}" ] && AE="$AE$EXE_EXT" @@ -43,9 +44,7 @@ case "$(uname -s)" in *) command -v curl >/dev/null 2>&1 || { echo " [SKIP] ae_add_binary_package: curl needed for the file:// forge"; exit 0; } ;; esac -if command -v sha256sum >/dev/null 2>&1; then SHA="sha256sum" -elif command -v shasum >/dev/null 2>&1; then SHA="shasum -a 256" -else echo " [SKIP] ae_add_binary_package: no sha256 tool"; exit 0; fi +SHA=sha256_of # The binary-package path is per-host-triple; on a host we can't name a triple # for, ae_host_triple() returns NULL and everything correctly falls to git. @@ -107,7 +106,7 @@ mk_binpkg() { asset="$STEM-$tag-$TRIPLE$EXT" # A small but real binary payload (not a real dlopen target — this test # exercises ae add's FETCH + INSTALL, not loading). - head -c 4096 /dev/urandom > "$d/$asset" + head_c 4096 /dev/urandom > "$d/$asset" if [ "$mode" = "badsum" ]; then echo "0000000000000000000000000000000000000000000000000000000000000000 $asset" > "$d/$asset.sha256" elif [ "$mode" = "nosum" ]; then @@ -122,7 +121,7 @@ mk_binpkg() { *) FT="linux-x86_64"; FEXT=".so" ;; esac fasset="$STEM-$tag-$FT$FEXT" - head -c 4096 /dev/urandom > "$d/$fasset" + head_c 4096 /dev/urandom > "$d/$fasset" ( cd "$d" && $SHA "$fasset" | awk -v n="$fasset" '{print $1" "n}' > "$fasset.sha256" ) } mk_binpkg v1.0.0 ok diff --git a/tests/integration/ae_add_release_artifact/test_ae_add_release_artifact.sh b/tests/integration/ae_add_release_artifact/test_ae_add_release_artifact.sh index 3a5cc9759..f828937e3 100755 --- a/tests/integration/ae_add_release_artifact/test_ae_add_release_artifact.sh +++ b/tests/integration/ae_add_release_artifact/test_ae_add_release_artifact.sh @@ -25,6 +25,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/portable.sh" . "$ROOT/tests/lib/server_jobs.sh" AE="$ROOT/build/ae" [ -n "${EXE_EXT:-}" ] && AE="$AE$EXE_EXT" @@ -32,9 +33,7 @@ AE="$ROOT/build/ae" [ -x "$AE" ] || { echo " [SKIP] ae_add_release_artifact: ae not built"; exit 0; } command -v python3 >/dev/null 2>&1 || { echo " [SKIP] ae_add_release_artifact: python3 needed to serve fixtures"; exit 0; } command -v tar >/dev/null 2>&1 || { echo " [SKIP] ae_add_release_artifact: tar needed"; exit 0; } -if command -v sha256sum >/dev/null 2>&1; then SHA="sha256sum" -elif command -v shasum >/dev/null 2>&1; then SHA="shasum -a 256" -else echo " [SKIP] ae_add_release_artifact: no sha256 tool"; exit 0; fi +SHA=sha256_of # The artifact path is per-host-triple; on a host we publish no assets # for, ae_host_triple() returns NULL and everything correctly falls back diff --git a/tests/integration/ae_download_api_fallback/test_ae_download_api_fallback.sh b/tests/integration/ae_download_api_fallback/test_ae_download_api_fallback.sh index ef3a71fe0..f01de77bf 100755 --- a/tests/integration/ae_download_api_fallback/test_ae_download_api_fallback.sh +++ b/tests/integration/ae_download_api_fallback/test_ae_download_api_fallback.sh @@ -19,6 +19,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/portable.sh" . "$ROOT/tests/lib/server_jobs.sh" AE="$ROOT/build/ae" [ -n "${EXE_EXT:-}" ] && AE="$AE$EXE_EXT" @@ -40,6 +41,9 @@ fail() { PLAT="$("$AE" version 2>&1 | sed -n 's/^Platform:[[:space:]]*//p' | head -1)" [ -n "$PLAT" ] || { echo " [SKIP] ae_download_api_fallback: cannot read the platform from 'ae version'"; exit 0; } +# A host with no release build (OpenBSD, NetBSD, ...) has no asset to serve: +# `ae version` says "unsupported" and `ae version install` takes the source path. +[ "$PLAT" = unsupported ] && { echo " [SKIP] ae_download_api_fallback: no release build for this host ($(uname -s))"; exit 0; } case "$PLAT" in windows*) echo " [SKIP] ae_download_api_fallback: zip archives, not covered here"; exit 0 ;; esac # A release archive that passes ae's post-extract checks: flat layout, a @@ -49,8 +53,8 @@ ASSET="aether-9.9.9-$PLAT.tar.gz" mkdir -p "$TMP/pkg/bin" "$TMP/pkg/lib/aether" "$TMP/pkg/share/aether" "$TMP/rel" printf '#!/bin/sh\necho aetherc 9.9.9\n' > "$TMP/pkg/bin/aetherc" chmod 755 "$TMP/pkg/bin/aetherc"; cp "$TMP/pkg/bin/aetherc" "$TMP/pkg/bin/ae" -{ printf '!\n'; head -c 70000 /dev/urandom; } > "$TMP/pkg/lib/aether/libaether.a" -head -c 20000 /dev/urandom > "$TMP/pkg/share/aether/pad.bin" +{ printf '!\n'; head_c 70000 /dev/urandom; } > "$TMP/pkg/lib/aether/libaether.a" +head_c 20000 /dev/urandom > "$TMP/pkg/share/aether/pad.bin" echo 9.9.9 > "$TMP/pkg/VERSION" ( cd "$TMP/pkg" && COPYFILE_DISABLE=1 tar -czf "$TMP/rel/$ASSET" . ) diff --git a/tests/integration/ae_help/test_ae_help.sh b/tests/integration/ae_help/test_ae_help.sh index 714e014f8..1d928e3f8 100755 --- a/tests/integration/ae_help/test_ae_help.sh +++ b/tests/integration/ae_help/test_ae_help.sh @@ -6,6 +6,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/portable.sh" AE="$ROOT/build/ae" TMPDIR="$(mktemp -d)" @@ -140,7 +141,7 @@ main() { } EOF out=$("$AE" help "$TMPDIR/json.ae" --json 2>&1) -first=$(printf '%s' "$out" | head -c 1) +first=$(printf '%s' "$out" | head_c 1) last=$(printf '%s' "$out" | tr -d '\n' | tail -c 1) if [ "$first" = "{" ] && [ "$last" = "}" ]; then echo " [PASS] --json emits a single JSON object" diff --git a/tests/integration/aea_artifacts/test_aea_artifacts.sh b/tests/integration/aea_artifacts/test_aea_artifacts.sh index 4eadfeb2b..d348060eb 100755 --- a/tests/integration/aea_artifacts/test_aea_artifacts.sh +++ b/tests/integration/aea_artifacts/test_aea_artifacts.sh @@ -11,6 +11,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/portable.sh" AETHERC="$ROOT/build/aetherc" [ -f "$AETHERC.exe" ] && AETHERC="$AETHERC.exe" EXE="" @@ -187,7 +188,7 @@ if grep -q "not using .*/md2.aea: source changed since the artifact was built" " else bad "edited source not detected"; cat "$T/edit_trace.txt"; fi # 6. A damaged artifact is ignored, not trusted. -head -c 200 "$MODS/std/cryptography/sha3.aea" > "$T/short.aea" +head_c 200 "$MODS/std/cryptography/sha3.aea" > "$T/short.aea" cp "$T/short.aea" "$MODS/std/cryptography/sha3.aea" aec_trace app.ae "$T/damaged.c" 2>"$T/dmg_trace.txt" from_source app.ae "$T/damaged_src.c" 2>/dev/null diff --git a/tests/integration/aether_cfn_proc_address/test_aether_cfn_proc_address.sh b/tests/integration/aether_cfn_proc_address/test_aether_cfn_proc_address.sh index 6ff91bebd..4e407fac5 100755 --- a/tests/integration/aether_cfn_proc_address/test_aether_cfn_proc_address.sh +++ b/tests/integration/aether_cfn_proc_address/test_aether_cfn_proc_address.sh @@ -22,6 +22,7 @@ set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" AETHERC="$ROOT/build/aetherc" if [ ! -x "$AETHERC" ]; then @@ -142,7 +143,7 @@ for d in "$ROOT" "$ROOT/runtime" "$ROOT/runtime/actors" "$ROOT/runtime/scheduler done if ! ${CC:-cc} $INCLUDES -o "$tmpdir/app_bin" "$tmpdir/app.gen.c" "$tmpdir/glue.c" \ - "$ROOT/build/libaether.a" -lpthread -ldl -lm 2>"$tmpdir/cc.err"; then + "$ROOT/build/libaether.a" -lpthread $AE_DL_LIBS -lm 2>"$tmpdir/cc.err"; then echo " [FAIL] aether_cfn_proc_address: C compile returned non-zero" sed 's/^/ /' "$tmpdir/cc.err" exit 1 diff --git a/tests/integration/aether_extern_param_annotation/test_aether_extern_param_annotation.sh b/tests/integration/aether_extern_param_annotation/test_aether_extern_param_annotation.sh index 1b54a5ee6..51913ed04 100755 --- a/tests/integration/aether_extern_param_annotation/test_aether_extern_param_annotation.sh +++ b/tests/integration/aether_extern_param_annotation/test_aether_extern_param_annotation.sh @@ -14,6 +14,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" AETHERC="$ROOT/build/aetherc" if [ ! -x "$AETHERC" ]; then @@ -89,7 +90,7 @@ INC=$(find "$PREFIX/include/aether" -type d 2>/dev/null | sed 's|^|-I|' | tr '\n ${CC:-cc} -O2 -Wall -Wextra -Werror $INC \ "$tmpdir/positive.c" "$tmpdir/helper.c" \ -L"$PREFIX/lib/aether" -laether \ - -pthread -lm -ldl \ + -pthread -lm $AE_DL_LIBS \ -o "$tmpdir/positive" >/dev/null 2>"$tmpdir/link.err" || { echo " [FAIL] positive case link failed" head -3 "$tmpdir/link.err" diff --git a/tests/integration/aether_extern_struct/test_aether_extern_struct.sh b/tests/integration/aether_extern_struct/test_aether_extern_struct.sh index 932233b69..ca6aa2bad 100755 --- a/tests/integration/aether_extern_struct/test_aether_extern_struct.sh +++ b/tests/integration/aether_extern_struct/test_aether_extern_struct.sh @@ -21,6 +21,7 @@ set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" AETHERC="$ROOT/build/aetherc" AE="$ROOT/build/ae" @@ -292,7 +293,7 @@ if ! ${CC:-cc} -c -o "$tmpdir/case4_glue.o" "$tmpdir/case4_glue.c" \ exit 1 fi if ! ${CC:-cc} -o "$tmpdir/case4_bin" "$tmpdir/case4.gen.o" "$tmpdir/case4_glue.o" \ - "$ROOT/build/libaether.a" -lpthread -ldl -lm 2>"$tmpdir/case4.gcc.err"; then + "$ROOT/build/libaether.a" -lpthread $AE_DL_LIBS -lm 2>"$tmpdir/case4.gcc.err"; then echo " [FAIL] case 4: gcc failed linking" cat "$tmpdir/case4.gcc.err" | sed 's/^/ /' exit 1 diff --git a/tests/integration/aether_fnptr_call/test_aether_fnptr_call.sh b/tests/integration/aether_fnptr_call/test_aether_fnptr_call.sh index edc240281..29bc18608 100755 --- a/tests/integration/aether_fnptr_call/test_aether_fnptr_call.sh +++ b/tests/integration/aether_fnptr_call/test_aether_fnptr_call.sh @@ -18,6 +18,7 @@ set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" AETHERC="$ROOT/build/aetherc" if [ ! -x "$AETHERC" ]; then @@ -188,7 +189,7 @@ done if ! ${CC:-cc} $INCLUDES -o "$tmpdir/case4_bin" \ "$tmpdir/case4.gen.c" "$tmpdir/case4_glue.c" \ - "$ROOT/build/libaether.a" -lpthread -ldl -lm 2>"$tmpdir/case4.gcc.err"; then + "$ROOT/build/libaether.a" -lpthread $AE_DL_LIBS -lm 2>"$tmpdir/case4.gcc.err"; then echo " [FAIL] case 4: gcc returned non-zero" cat "$tmpdir/case4.gcc.err" | sed 's/^/ /' exit 1 diff --git a/tests/integration/array_field_length/test_array_field_length.sh b/tests/integration/array_field_length/test_array_field_length.sh index 5a7379fc8..2eb68582f 100644 --- a/tests/integration/array_field_length/test_array_field_length.sh +++ b/tests/integration/array_field_length/test_array_field_length.sh @@ -90,7 +90,7 @@ main() { println("${v.xs[3]} ${w.xs[1]} ${w.xs[3]} ${s.names[0]} ${string.length(s.names[2])} ${t.names[2]}") } AE -got="$("$AE" run "$tmp/ok.ae" 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$("$AE" run "$tmp/ok.ae" 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "4 6 0 x 0 c" ]; then echo " [FAIL] array_field_length: arrays of the field's length and shorter literals" printf 'got:\n%s\nwant:\n4 6 0 x 0 c\n' "$got" | sed 's/^/ /' diff --git a/tests/integration/assign_to_call_result/test_assign_to_call_result.sh b/tests/integration/assign_to_call_result/test_assign_to_call_result.sh index 580cd3c03..9186f5875 100644 --- a/tests/integration/assign_to_call_result/test_assign_to_call_result.sh +++ b/tests/integration/assign_to_call_result/test_assign_to_call_result.sh @@ -128,7 +128,7 @@ main() { println("${q.x} ${q.y} ${g.cells[1]}") } AE -got="$("$AE" run good.ae 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$("$AE" run good.ae 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "5 7 20" ]; then echo " [FAIL] assign_to_call_result: an assignment through a returned pointer stopped working" printf 'got:\n%s\nwant:\n5 7 20\n' "$got" | sed 's/^/ /' diff --git a/tests/integration/binary_import_ownership/test_binary_import_ownership.sh b/tests/integration/binary_import_ownership/test_binary_import_ownership.sh index 5ad24b6e8..5143e6ffe 100644 --- a/tests/integration/binary_import_ownership/test_binary_import_ownership.sh +++ b/tests/integration/binary_import_ownership/test_binary_import_ownership.sh @@ -17,6 +17,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" AE="$ROOT/build/ae" [ -x "$AE" ] || [ -x "$AE.exe" ] || { echo " [SKIP] binary_import_ownership: ae not built"; exit 0; } CC="${CC:-cc}" diff --git a/tests/integration/block_nesting_cc/test_block_nesting_cc.sh b/tests/integration/block_nesting_cc/test_block_nesting_cc.sh index 7c96acf88..b8e31b560 100755 --- a/tests/integration/block_nesting_cc/test_block_nesting_cc.sh +++ b/tests/integration/block_nesting_cc/test_block_nesting_cc.sh @@ -59,7 +59,7 @@ runs matches "matches 0" # The deepest ( [ { of the C of the matches, outside literals and comments. AETHER_HOME="$ROOT" "$AETHERC" "$WORK/matches.ae" "$WORK/matches.c" >/dev/null 2>&1 -d="$(awk 'BEGIN { d = 0; m = 0 } +d="$(LC_ALL=C awk 'BEGIN { d = 0; m = 0 } /^[ \t]*#/ { next } { n = length($0); str = 0 for (i = 1; i <= n; i++) { diff --git a/tests/integration/c_include_directive/test_c_include_directive.sh b/tests/integration/c_include_directive/test_c_include_directive.sh index d51d9f6de..ce24e830d 100644 --- a/tests/integration/c_include_directive/test_c_include_directive.sh +++ b/tests/integration/c_include_directive/test_c_include_directive.sh @@ -15,6 +15,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/portable.sh" AE="$ROOT/build/ae" AETHERC="$ROOT/build/aetherc" @@ -122,9 +123,11 @@ cross="cross build: zig not on PATH, skipped" if command -v zig >/dev/null 2>&1; then if (cd "$tmp/proj" && "$AE" build --lib "$tmp/lib" main.ae --target=x86_64-linux-musl -o "$tmp/cross_main" >"$tmp/cross.log" 2>&1) && [ -f "$tmp/cross_main" ]; then cross="the cross path finds it too" + elif zig_musl_unavailable "$tmp/cross.log"; then + cross="cross build skipped: zig cannot build musl libc.a on this host" else echo " [FAIL] c_include_directive: ae build --target did not find the module's header" - grep -i "error\|not found" "$tmp/cross.log" | head -3 | sed 's/^/ /' + grep -i -E "error|not found" "$tmp/cross.log" | head -3 | sed 's/^/ /' fail=1 fi fi diff --git a/tests/integration/cache_key_runtime_headers/test_cache_key_runtime_headers.sh b/tests/integration/cache_key_runtime_headers/test_cache_key_runtime_headers.sh index bd50f31d3..fc052ac5b 100644 --- a/tests/integration/cache_key_runtime_headers/test_cache_key_runtime_headers.sh +++ b/tests/integration/cache_key_runtime_headers/test_cache_key_runtime_headers.sh @@ -15,6 +15,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/portable.sh" fail() { echo " [FAIL] cache_key_runtime_headers: $1"; exit 1; } @@ -35,8 +36,8 @@ for f in "ae$EXE" "aetherc$EXE" libaether.a aether_manifest.o; do done # The headers, in one process (each process costs tens of milliseconds on # Windows). -(cd "$ROOT" && find runtime std include -name '*.h' 2>/dev/null | tar cf - -T -) | - (cd "$TREE" && tar xf -) || fail "could not copy the headers" +(cd "$ROOT" && find runtime std include -name '*.h' 2>/dev/null | copy_listed "$TREE") || + fail "could not copy the headers" AE="$TREE/build/ae$EXE" probe="$TREE/runtime/utils/zz_cache_key_probe.h" diff --git a/tests/integration/call_non_closure_reject/test_call_non_closure_reject.sh b/tests/integration/call_non_closure_reject/test_call_non_closure_reject.sh index 7c875f9e6..9325e6457 100644 --- a/tests/integration/call_non_closure_reject/test_call_non_closure_reject.sh +++ b/tests/integration/call_non_closure_reject/test_call_non_closure_reject.sh @@ -38,7 +38,7 @@ expect_reject() { grep -qF "$2" "$1.txt" || fail "$1: missing '$2'" "$1.txt" grep -qF -- "--> main.ae:5:10" "$1.txt" || fail "$1: not reported at the argument" "$1.txt" grep -qF "$3" "$1.txt" || fail "$1: missing help '$3'" "$1.txt" - if grep -q "has no member named\|not a structure or union" "$1.txt"; then + if grep -q -E "has no member named|not a structure or union" "$1.txt"; then fail "$1: still reported by the C compiler" "$1.txt" fi } @@ -105,7 +105,7 @@ main() { AE want="boxed 4 2 6 11 6 8" -got="$("$AE" run main.ae 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$("$AE" run main.ae 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "$want" ]; then echo " [FAIL] call_non_closure_reject: a closure callee stopped working" printf 'got:\n%s\nwant:\n%s\n' "$got" "$want" | sed 's/^/ /' diff --git a/tests/integration/canonical_repo_url/test_canonical_repo_url.sh b/tests/integration/canonical_repo_url/test_canonical_repo_url.sh index fed56aefb..e288f9a0b 100755 --- a/tests/integration/canonical_repo_url/test_canonical_repo_url.sh +++ b/tests/integration/canonical_repo_url/test_canonical_repo_url.sh @@ -33,13 +33,12 @@ STALE_OWNERS="nicolasmd87 aether-lang-${ORG_SUFFIX}" hits="" for owner in $STALE_OWNERS; do stale="$owner/aether" - found=$(grep -rn "$stale" \ - --exclude-dir=.git \ - --exclude-dir=build \ - --exclude-dir=target \ - --exclude=CHANGELOG.md \ - --exclude=CHANGELOG-archive.md \ - . 2>/dev/null | grep -Ev '^\.?/?benchmarks/json/corpus/') + # find, not `grep -r --exclude`: OpenBSD's grep matches --exclude against + # the whole path ("./CHANGELOG.md"), so the changelogs were not excluded. + found=$(find . \( -name .git -o -name build -o -name target \) -prune -o \ + -type f ! -name CHANGELOG.md ! -name CHANGELOG-archive.md \ + ! -path './benchmarks/json/corpus/*' \ + -exec grep -n "$stale" /dev/null {} + 2>/dev/null) [ -n "$found" ] && hits="${hits}${found} " done diff --git a/tests/integration/checksec_hardening/test_checksec_hardening.sh b/tests/integration/checksec_hardening/test_checksec_hardening.sh index b898d7a30..d1e1aa015 100755 --- a/tests/integration/checksec_hardening/test_checksec_hardening.sh +++ b/tests/integration/checksec_hardening/test_checksec_hardening.sh @@ -157,23 +157,34 @@ esac # Apple libc, all of them non-zero); an unfortified one writes past the object # and prints. Both halves are asserted, because a process that dies for some # other reason would otherwise look like a pass. -if ! "$AE" build "$TMP/overflow.ae" --extra "$TMP/probe_copy.c" -o "$TMP/overflow" \ - >"$TMP/overflow_build.log" 2>&1; then - sed 's/^/ /' "$TMP/overflow_build.log" | head -10 - fail "the overflow probe did not build" -fi -OVERFLOW="$(sed -n 's/^Built[^:]*: //p' "$TMP/overflow_build.log" | tail -1)" -[ -n "$OVERFLOW" ] || fail "the overflow build printed no artifact path" - -overflow_out="$("$OVERFLOW" 2>&1)" -overflow_rc=$? -[ "$overflow_rc" -ne 0 ] \ - || fail "the overflow completed with exit 0: FORTIFY did not catch a 32-byte write into a 16-byte object" -case "$overflow_out" in - *"overflow not caught"*) - fail "the overflow ran to completion (output: $overflow_out): FORTIFY is not in effect" ;; +# OpenBSD's libc has no __*_chk functions and its compiler no _FORTIFY_SOURCE +# lowering to them: there is nothing to fortify there, so the overflow below +# completes by design, and checksec must say so (a "no", not a "yes" or an +# "n/a") rather than the test pretending the protection exists. +case "$(uname -s)" in + OpenBSD) HAS_FORTIFY=no ;; + *) HAS_FORTIFY=yes ;; esac +if [ "$HAS_FORTIFY" = yes ]; then + if ! "$AE" build "$TMP/overflow.ae" --extra "$TMP/probe_copy.c" -o "$TMP/overflow" \ + >"$TMP/overflow_build.log" 2>&1; then + sed 's/^/ /' "$TMP/overflow_build.log" | head -10 + fail "the overflow probe did not build" + fi + OVERFLOW="$(sed -n 's/^Built[^:]*: //p' "$TMP/overflow_build.log" | tail -1)" + [ -n "$OVERFLOW" ] || fail "the overflow build printed no artifact path" + + overflow_out="$("$OVERFLOW" 2>&1)" + overflow_rc=$? + [ "$overflow_rc" -ne 0 ] \ + || fail "the overflow completed with exit 0: FORTIFY did not catch a 32-byte write into a 16-byte object" + case "$overflow_out" in + *"overflow not caught"*) + fail "the overflow ran to completion (output: $overflow_out): FORTIFY is not in effect" ;; + esac +fi + # And what checksec reads back agrees. glibc and Apple libc leave a __*_chk # call to find; mingw-w64 inlines the check, so a PE with no symbol table has # nothing to read and says n/a rather than guessing "no". The run above is @@ -184,13 +195,15 @@ case "$fmt" in yes|n/a) ;; *) fail "FORTIFY is '$fortify' on PE, expected yes or n/a" ;; esac ;; - *) [ "$fortify" = "yes" ] || fail "FORTIFY is '$fortify' on $fmt, expected yes" ;; + *) [ "$fortify" = "$HAS_FORTIFY" ] || fail "FORTIFY is '$fortify' on $fmt, expected $HAS_FORTIFY" ;; esac # The gate agrees with the report, and it is the same gate everywhere: a # property the format cannot express reports n/a and satisfies --require, so # `relro-full` is in this list on Mach-O and PE too. -"$AE" checksec --require "pie,nx,relro-full,canary,fortify" "$PROBE" >/dev/null 2>&1 \ +REQUIRE="pie,nx,relro-full,canary" +[ "$HAS_FORTIFY" = yes ] && REQUIRE="$REQUIRE,fortify" +"$AE" checksec --require "$REQUIRE" "$PROBE" >/dev/null 2>&1 \ || fail "--require rejected a binary the report says is hardened" # And the gate is a gate: something the report says the binary lacks must fail diff --git a/tests/integration/ci_coverage_smoke/test_ci_coverage_smoke.sh b/tests/integration/ci_coverage_smoke/test_ci_coverage_smoke.sh index 5b07c6ff5..4da4ad185 100755 --- a/tests/integration/ci_coverage_smoke/test_ci_coverage_smoke.sh +++ b/tests/integration/ci_coverage_smoke/test_ci_coverage_smoke.sh @@ -10,6 +10,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" AETHERC="$ROOT/build/aetherc" AE="$ROOT/build/ae" @@ -86,7 +87,7 @@ if ! (cd "$tmpdir" && gcc --coverage -O0 -g $INC_FLAGS \ fi if ! (cd "$tmpdir" && gcc --coverage cov_demo.o \ -L"$PREFIX/lib/aether" -laether \ - -lpthread -lm -ldl \ + -lpthread -lm $AE_DL_LIBS \ -o cov_demo 2>"$tmpdir/link.err"); then echo " [FAIL] ci_coverage_smoke: gcc --coverage link failed" head -5 "$tmpdir/link.err" diff --git a/tests/integration/closure_capture_compound_assign/test_closure_capture_compound_assign.sh b/tests/integration/closure_capture_compound_assign/test_closure_capture_compound_assign.sh index 9845b0dbc..f21d39acf 100644 --- a/tests/integration/closure_capture_compound_assign/test_closure_capture_compound_assign.sh +++ b/tests/integration/closure_capture_compound_assign/test_closure_capture_compound_assign.sh @@ -85,7 +85,7 @@ decl 212 float 2.5 count 5 try 5" -got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "$want" ]; then echo " [FAIL] closure_capture_compound_assign: wrong values" printf 'got:\n%s\nwant:\n%s\n' "$got" "$want" | sed 's/^/ /' diff --git a/tests/integration/closure_cell_borrowed_string/test_closure_cell_borrowed_string.sh b/tests/integration/closure_cell_borrowed_string/test_closure_cell_borrowed_string.sh index efbf36586..ef9c89b66 100644 --- a/tests/integration/closure_cell_borrowed_string/test_closure_cell_borrowed_string.sh +++ b/tests/integration/closure_cell_borrowed_string/test_closure_cell_borrowed_string.sh @@ -74,7 +74,7 @@ want="3 ab7 3 ab7 ab7 ab7? ab7 ab7/3 ab7" -got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "$want" ]; then echo " [FAIL] closure_cell_borrowed_string: the caller's string did not survive the closure's store" printf 'got:\n%s\nwant:\n%s\n' "$got" "$want" | sed 's/^/ /' diff --git a/tests/integration/closure_destructure_rebind_reject/test_closure_destructure_rebind_reject.sh b/tests/integration/closure_destructure_rebind_reject/test_closure_destructure_rebind_reject.sh index 5b1947b1d..07b93ec10 100644 --- a/tests/integration/closure_destructure_rebind_reject/test_closure_destructure_rebind_reject.sh +++ b/tests/integration/closure_destructure_rebind_reject/test_closure_destructure_rebind_reject.sh @@ -43,7 +43,7 @@ expect_reject() { tr -d '\r' < "$1.log" > "$1.txt" grep -qF "$2" "$1.txt" || fail "$1: missing '$2'" "$1.txt" grep -qF -- "--> main.ae:$3" "$1.txt" || fail "$1: not reported at the slot ($3)" "$1.txt" - if grep -q "makes pointer from integer\|incompatible types" "$1.txt"; then + if grep -q -E "makes pointer from integer|incompatible types" "$1.txt"; then fail "$1: still reported by the C compiler" "$1.txt" fi } diff --git a/tests/integration/closure_param_reassign/test_closure_param_reassign.sh b/tests/integration/closure_param_reassign/test_closure_param_reassign.sh index 99bd97707..dac0ee228 100644 --- a/tests/integration/closure_param_reassign/test_closure_param_reassign.sh +++ b/tests/integration/closure_param_reassign/test_closure_param_reassign.sh @@ -61,7 +61,7 @@ k 12 in! outer left 0" -got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "$want" ]; then echo " [FAIL] closure_param_reassign: wrong output" printf 'got:\n%s\nwant:\n%s\n' "$got" "$want" | sed 's/^/ /' diff --git a/tests/integration/closure_param_shadows_local_type/test_closure_param_shadows_local_type.sh b/tests/integration/closure_param_shadows_local_type/test_closure_param_shadows_local_type.sh index efd3eceb5..21cd1e569 100644 --- a/tests/integration/closure_param_shadows_local_type/test_closure_param_shadows_local_type.sh +++ b/tests/integration/closure_param_shadows_local_type/test_closure_param_shadows_local_type.sh @@ -62,7 +62,7 @@ string-shadow 33 outer text int-shadow 2.5 outer 7" -got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "$want" ]; then echo " [FAIL] closure_param_shadows_local_type: wrong output" printf 'got:\n%s\nwant:\n%s\n' "$got" "$want" | sed 's/^/ /' diff --git a/tests/integration/closure_volatile_array_capture/test_closure_volatile_array_capture.sh b/tests/integration/closure_volatile_array_capture/test_closure_volatile_array_capture.sh index eb35135c4..d4265c45a 100644 --- a/tests/integration/closure_volatile_array_capture/test_closure_volatile_array_capture.sh +++ b/tests/integration/closure_volatile_array_capture/test_closure_volatile_array_capture.sh @@ -86,8 +86,8 @@ for src in main.c main_nogcc.c; do sed 's/^/ /' "$tmp/cc.log" | head -20 fail "$src does not compile" fi - if grep -qi "discards\|volatile" "$tmp/cc.log"; then - grep -i "discards\|volatile" "$tmp/cc.log" | sed 's/^/ /' | head -10 + if grep -qi -E "discards|volatile" "$tmp/cc.log"; then + grep -i -E "discards|volatile" "$tmp/cc.log" | sed 's/^/ /' | head -10 fail "$src: a volatile array is passed where a plain one is expected" fi done diff --git a/tests/integration/closure_write_through_capture/test_closure_write_through_capture.sh b/tests/integration/closure_write_through_capture/test_closure_write_through_capture.sh index 048ac4c33..25471e31a 100644 --- a/tests/integration/closure_write_through_capture/test_closure_write_through_capture.sh +++ b/tests/integration/closure_write_through_capture/test_closure_write_through_capture.sh @@ -171,7 +171,7 @@ nested 6 100 loop 2 2 1 1 float 4 string a aab!!" -got="$("$AE" run "$tmp/arrays.ae" 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$("$AE" run "$tmp/arrays.ae" 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "$want_arrays" ]; then echo " [FAIL] closure_write_through_capture: a write to a captured array did not reach the caller" printf 'got:\n%s\nwant:\n%s\n' "$got" "$want_arrays" | sed 's/^/ /' @@ -212,7 +212,7 @@ main() { println("state ${n}") } AE -got="$("$AE" run "$tmp/actor.ae" 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$("$AE" run "$tmp/actor.ae" 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "state 704" ]; then echo " [FAIL] closure_write_through_capture: a handler-local array written by a closure" printf '%s\n' "$got" | head -5 | sed 's/^/ /' @@ -252,7 +252,7 @@ field 7 nested 15 element 9 rec ab!+ 2 box n2" -got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "$want" ]; then echo " [FAIL] closure_write_through_capture: wrong values" printf 'got:\n%s\nwant:\n%s\n' "$got" "$want" | sed 's/^/ /' diff --git a/tests/integration/condition_continuation_pipe/test_condition_continuation_pipe.sh b/tests/integration/condition_continuation_pipe/test_condition_continuation_pipe.sh index e4ce155e0..963f7cd8a 100755 --- a/tests/integration/condition_continuation_pipe/test_condition_continuation_pipe.sh +++ b/tests/integration/condition_continuation_pipe/test_condition_continuation_pipe.sh @@ -26,7 +26,7 @@ log="$tmpdir/cc.log" if ! grep -q '^error' "$log"; then echo " [FAIL] leading-|| condition reported no error" fail=1 -elif ! grep -qi "closure parameters" "$log" || ! grep -qi "previous line\|parenthesise\|wrapped condition" "$log"; then +elif ! grep -qi "closure parameters" "$log" || ! grep -Eqi "previous line|parenthesise|wrapped condition" "$log"; then echo " [FAIL] leading-|| error doesn't explain the wrapped-condition cause + fix" echo " got:"; sed 's/^/ /' "$log" | head -4 fail=1 diff --git a/tests/integration/const_dependency_order/test_const_dependency_order.sh b/tests/integration/const_dependency_order/test_const_dependency_order.sh index 0a31e3f8a..060c95913 100644 --- a/tests/integration/const_dependency_order/test_const_dependency_order.sh +++ b/tests/integration/const_dependency_order/test_const_dependency_order.sh @@ -31,7 +31,7 @@ fail() { # run run() { - got="$("$AE" run "$1" 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" + got="$("$AE" run "$1" 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "$2" ]; then printf ' [FAIL] const_dependency_order: %s\ngot:\n%s\nwant:\n%s\n' \ "$1" "$got" "$2" | sed '2,$s/^/ /' @@ -89,7 +89,7 @@ expect_cycle() { tr -d '\r' < "$1.log" > "$1.txt" grep -qF -- "--> $2" "$1.txt" || fail "$1: not reported at $2" "$1.txt" grep -qF "$3" "$1.txt" || fail "$1: missing '$3'" "$1.txt" - if grep -q "undeclared\|initializer element is not constant" "$1.txt"; then + if grep -q -E "undeclared|initializer element is not constant" "$1.txt"; then fail "$1: still reported by the C compiler" "$1.txt" fi } diff --git a/tests/integration/contrib_vulkan_portability/test_contrib_vulkan_portability.sh b/tests/integration/contrib_vulkan_portability/test_contrib_vulkan_portability.sh index 060f8241e..c516dd1f7 100755 --- a/tests/integration/contrib_vulkan_portability/test_contrib_vulkan_portability.sh +++ b/tests/integration/contrib_vulkan_portability/test_contrib_vulkan_portability.sh @@ -92,7 +92,7 @@ if ! "$NM_WIN" -u "$TMP/win.o" | grep -q 'LoadLibraryA'; then echo " the _WIN32 arm was compiled out" exit 1 fi -if "$NM_WIN" -u "$TMP/win.o" | grep -q '\bdlopen\b'; then +if "$NM_WIN" -u "$TMP/win.o" | grep -qE '(^|[^A-Za-z0-9_])dlopen([^A-Za-z0-9_]|$)'; then echo " [FAIL] contrib_vulkan_portability: the Windows object references dlopen" exit 1 fi diff --git a/tests/integration/cross_emit_lib/test_cross_emit_lib.sh b/tests/integration/cross_emit_lib/test_cross_emit_lib.sh index 2273b4882..7a7f48bb7 100755 --- a/tests/integration/cross_emit_lib/test_cross_emit_lib.sh +++ b/tests/integration/cross_emit_lib/test_cross_emit_lib.sh @@ -33,6 +33,7 @@ set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/portable.sh" AE="$ROOT/build/ae" if ! command -v zig >/dev/null 2>&1; then @@ -60,7 +61,7 @@ if [ ! -f "$TMPDIR_T/libgreet.so" ]; then exit 1 fi -DESC=$(file "$TMPDIR_T/libgreet.so" 2>/dev/null || echo "") +DESC=$(file_desc "$TMPDIR_T/libgreet.so" 2>/dev/null || echo "") case "$DESC" in *"shared object"*aarch64*) ;; *) @@ -73,9 +74,9 @@ esac # The symbol has to be there. A stripped-to-nothing .so links fine and is # useless to the consumer this feature exists for. if command -v nm >/dev/null 2>&1; then - if ! nm -D --defined-only "$TMPDIR_T/libgreet.so" 2>/dev/null | grep -q "aether_greet"; then + if ! dyn_syms "$TMPDIR_T/libgreet.so" | grep -q "aether_greet"; then echo " [FAIL] cross_emit_lib: aether_greet not exported from the .so" - nm -D --defined-only "$TMPDIR_T/libgreet.so" 2>/dev/null | head -10 | sed 's/^/ /' + dyn_syms "$TMPDIR_T/libgreet.so" | head -10 | sed 's/^/ /' exit 1 fi fi @@ -100,7 +101,7 @@ if [ ! -f "$TMPDIR_T/greet.dll" ]; then exit 1 fi -DESC=$(file "$TMPDIR_T/greet.dll" 2>/dev/null || echo "") +DESC=$(file_desc "$TMPDIR_T/greet.dll" 2>/dev/null || echo "") case "$DESC" in *DLL*) ;; *) @@ -186,8 +187,8 @@ fi # executable (e_type 2) beside a shared object (e_type 3), or a PE image # beside one with the IMAGE_FILE_DLL bit. if [ "$BOTH_KIND" = elf ]; then - et_exe=$(od -An -tu2 -j16 -N2 "$TMPDIR_T/$BOTH_EXE" | tr -d ' ') - et_lib=$(od -An -tu2 -j16 -N2 "$TMPDIR_T/$BOTH_LIB" | tr -d ' ') + et_exe=$(od_uint "$TMPDIR_T/$BOTH_EXE" 16 2) + et_lib=$(od_uint "$TMPDIR_T/$BOTH_LIB" 16 2) if [ "$et_exe" != 2 ] && [ "$et_exe" != 3 ]; then echo " [FAIL] cross_emit_lib: --emit=both's executable is not an ELF image (e_type $et_exe)"; exit 1 fi @@ -196,8 +197,8 @@ if [ "$BOTH_KIND" = elf ]; then fi else pe_chars() { - off=$(od -An -tu4 -j60 -N4 "$1" | tr -d ' ') - od -An -tu2 -j$((off + 22)) -N2 "$1" | tr -d ' ' + off=$(od_uint "$1" 60 4) + od_uint "$1" $((off + 22)) 2 } c_exe=$(pe_chars "$TMPDIR_T/$BOTH_EXE") c_lib=$(pe_chars "$TMPDIR_T/$BOTH_LIB") @@ -255,6 +256,14 @@ case "$(uname -s)/$(uname -m)" in SL_LIBS="-lws2_32 -lcrypt32 -lgdi32 -luser32 -ladvapi32 -lbcrypt -ldbghelp" ;; Linux/x86_64|*) SL_TARGET=x86_64-linux-musl ;; esac +# Nothing here can run a Linux, macOS or Windows binary: no triple to match. +case "$(uname -s)" in + OpenBSD|NetBSD) + echo " [PASS] cross_emit_lib: aarch64 .so with exports, PE .dll named correctly," + echo " --emit=both builds both, named for the target" + echo " (static archive link-and-run skipped: $(uname -s) has no target triple whose binary runs here)" + exit 0 ;; +esac if ! "$AE" build --target="$SL_TARGET" --emit=staticlib "$LIB" \ -o "$TMPDIR_T/libgreet.a" > "$TMPDIR_T/sl.log" 2>&1; then echo " [FAIL] cross_emit_lib: --emit=staticlib did not build" @@ -276,7 +285,7 @@ if grep -q 'install_name_tool' "$TMPDIR_T/elf.log"; then exit 1 fi -DESC=$(file "$TMPDIR_T/libgreet.a" 2>/dev/null || echo "") +DESC=$(file_desc "$TMPDIR_T/libgreet.a" 2>/dev/null || echo "") case "$DESC" in *archive*) ;; *) diff --git a/tests/integration/cross_linux_musl/test_cross_linux_musl.sh b/tests/integration/cross_linux_musl/test_cross_linux_musl.sh index 83d092558..bca1f3021 100755 --- a/tests/integration/cross_linux_musl/test_cross_linux_musl.sh +++ b/tests/integration/cross_linux_musl/test_cross_linux_musl.sh @@ -23,6 +23,7 @@ set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/portable.sh" AE="$ROOT/build/ae" [ -x "$AE" ] || { echo " [SKIP] cross_linux_musl: ae not built"; exit 0; } @@ -37,9 +38,13 @@ printf 'main() {\n println("musl-ok")\n return 0\n}\n' > "$TMP/hello.ae" # --- x86_64 musl: the full set of claims ----------------------------------- "$AE" build "$TMP/hello.ae" --target=x86_64-linux-musl -o "$TMP/x64" \ - >"$TMP/b1.log" 2>&1 || { sed -n '1,10p' "$TMP/b1.log"; fail "x86_64-linux-musl did not build"; } + >"$TMP/b1.log" 2>&1 || { + if zig_musl_unavailable "$TMP/b1.log"; then + echo " [SKIP] cross_linux_musl: zig cannot build musl libc.a on this host"; exit 0 + fi + sed -n '1,10p' "$TMP/b1.log"; fail "x86_64-linux-musl did not build"; } -desc=$(file -b "$TMP/x64" 2>/dev/null || echo "") +desc=$(file_desc "$TMP/x64" 2>/dev/null || echo "") case "$desc" in *"statically linked"*) ;; *) fail "x86_64-linux-musl is not statically linked: $desc" ;; @@ -58,7 +63,7 @@ fi # --- the gnu target must be unchanged -------------------------------------- "$AE" build "$TMP/hello.ae" --target=x86_64-linux -o "$TMP/gnu" \ >"$TMP/b2.log" 2>&1 || { sed -n '1,10p' "$TMP/b2.log"; fail "x86_64-linux did not build"; } -case "$(file -b "$TMP/gnu" 2>/dev/null)" in +case "$(file_desc "$TMP/gnu" 2>/dev/null)" in *"dynamically linked"*) ;; *) fail "x86_64-linux should still be dynamically linked (musl rows changed it)" ;; esac @@ -66,7 +71,7 @@ esac # --- aarch64 musl ---------------------------------------------------------- "$AE" build "$TMP/hello.ae" --target=aarch64-linux-musl -o "$TMP/a64" \ >"$TMP/b3.log" 2>&1 || { sed -n '1,10p' "$TMP/b3.log"; fail "aarch64-linux-musl did not build"; } -desc=$(file -b "$TMP/a64" 2>/dev/null || echo "") +desc=$(file_desc "$TMP/a64" 2>/dev/null || echo "") case "$desc" in *"statically linked"*) ;; *) fail "aarch64-linux-musl is not statically linked: $desc" ;; diff --git a/tests/integration/cross_riscv64_loongarch64/test_cross_riscv64_loongarch64.sh b/tests/integration/cross_riscv64_loongarch64/test_cross_riscv64_loongarch64.sh index 913e78340..06aa50b04 100755 --- a/tests/integration/cross_riscv64_loongarch64/test_cross_riscv64_loongarch64.sh +++ b/tests/integration/cross_riscv64_loongarch64/test_cross_riscv64_loongarch64.sh @@ -18,6 +18,7 @@ set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/portable.sh" AE="$ROOT/build/ae" [ -x "$AE" ] || { echo " [SKIP] cross_riscv64_loongarch64: ae not built"; exit 0; } @@ -64,9 +65,12 @@ ran="" skipped="" check() { # t="$1" - "$AE" build "$TMP/ping.ae" --target="$t" -o "$TMP/$t" > "$TMP/$t.log" 2>&1 || - { sed -n '1,10p' "$TMP/$t.log"; fail "$t did not build"; } - desc=$(file -b "$TMP/$t" 2>/dev/null || echo "") + "$AE" build "$TMP/ping.ae" --target="$t" -o "$TMP/$t" > "$TMP/$t.log" 2>&1 || { + if zig_musl_unavailable "$TMP/$t.log"; then + echo " [SKIP] cross_riscv64_loongarch64: zig cannot build musl libc.a on this host"; exit 0 + fi + sed -n '1,10p' "$TMP/$t.log"; fail "$t did not build"; } + desc=$(file_desc "$TMP/$t" 2>/dev/null || echo "") case "$desc" in *"statically linked"*) ;; *) fail "$t is not statically linked: $desc" ;; esac case "$desc" in *"$2"*) ;; *) fail "$t produced the wrong machine: $desc" ;; esac # The first of qemu--static / qemu- on PATH that is new enough: diff --git a/tests/integration/crypto_tls13_mtls_signature/test_tls13_mtls_signature.sh b/tests/integration/crypto_tls13_mtls_signature/test_tls13_mtls_signature.sh index d78579972..a58c1883b 100644 --- a/tests/integration/crypto_tls13_mtls_signature/test_tls13_mtls_signature.sh +++ b/tests/integration/crypto_tls13_mtls_signature/test_tls13_mtls_signature.sh @@ -53,7 +53,7 @@ ssl env MSYS2_ARG_CONV_EXCL='/CN=' openssl req -x509 -new -key "$TMP/root.key" - for k in srv cli; do ssl env MSYS2_ARG_CONV_EXCL='/CN=' openssl req -new -key "$TMP/$k.key" -out "$TMP/$k.csr" -subj "/CN=$k" ssl openssl x509 -req -in "$TMP/$k.csr" -CA "$TMP/root.pem" -CAkey "$TMP/root.key" -CAcreateserial \ - -days 2 -extfile "$TMP/$k.ext" -out "$TMP/$k.pem" + -days 2 -sha256 -extfile "$TMP/$k.ext" -out "$TMP/$k.pem" done "$AE" build "$SCRIPT_DIR/mtls.ae" -o "$TMP/mtls" >"$TMP/b_mtls.log" 2>&1 \ @@ -90,21 +90,21 @@ for round in 1 2; do serve "$TMP/cli.key" case "$got" in *"GOT HTTP/1.0 200 ok"*) echo " ok: its own key, round $round: the server took the signature and answered" ;; - *) echo " FAIL: its own key, round $round: client [$got], server [$(grep -i 'error\|alert\|verify' "$TMP/s.log" | tr '\n' '|')]"; fails=$((fails + 1)) ;; + *) echo " FAIL: its own key, round $round: client [$got], server [$(grep -i -E 'error|alert|verify' "$TMP/s.log" | tr '\n' '|')]"; fails=$((fails + 1)) ;; esac done serve "$TMP/other.key" case "$got" in *"REFUSED: "*"not sent"*) - if grep -qi "internal error\|alert number 80" "$TMP/s.log"; then + if grep -qi -E "internal error|alert number 80" "$TMP/s.log"; then echo " ok: another key: the client did not send the signature, and the server got internal_error" else - echo " FAIL: another key: the server's log has no internal_error: [$(grep -i 'error\|alert' "$TMP/s.log" | tr '\n' '|')]" + echo " FAIL: another key: the server's log has no internal_error: [$(grep -i -E 'error|alert' "$TMP/s.log" | tr '\n' '|')]" fails=$((fails + 1)) fi ;; - *) echo " FAIL: another key: client [$got], server [$(grep -i 'error\|alert\|verify' "$TMP/s.log" | tr '\n' '|')]"; fails=$((fails + 1)) ;; + *) echo " FAIL: another key: client [$got], server [$(grep -i -E 'error|alert|verify' "$TMP/s.log" | tr '\n' '|')]"; fails=$((fails + 1)) ;; esac [ "$fails" = 0 ] || fail "$fails case(s)" diff --git a/tests/integration/crypto_tls13_peer/kuserver.ae b/tests/integration/crypto_tls13_peer/kuserver.ae index eda6cfca0..42b82d522 100644 --- a/tests/integration/crypto_tls13_peer/kuserver.ae +++ b/tests/integration/crypto_tls13_peer/kuserver.ae @@ -32,7 +32,8 @@ main() { tls13_server.free_private_key(key) if err != "" { println("REFUSED: ${err}") - tcp.close(sock) + // Drained, as server.ae's: a reset could take the alert (#3465). + tcp.close_drained(sock) tls13_server.free_cert_chain(chain, lens) tcp.server_close(srv) exit(0) diff --git a/tests/integration/crypto_tls13_peer/server.ae b/tests/integration/crypto_tls13_peer/server.ae index feddfb247..65b0800a8 100644 --- a/tests/integration/crypto_tls13_peer/server.ae +++ b/tests/integration/crypto_tls13_peer/server.ae @@ -52,7 +52,12 @@ main() { tls13_server.free_private_key(key) if err != "" { println("REFUSED: ${err}") - tcp.close(sock) + // Drained, as the library's own entry closes a failed handshake + // (#3465): what the client sent after the record refused (a second + // change_cipher_spec, say) is still unread, and a plain close ends + // with a reset, which on Windows can take the alert from the client + // before it reads it ("HRR CLOSED" for "HRR ALERT 10"). + tcp.close_drained(sock) tls13_server.free_cert_chain(chain, lens) tcp.server_close(srv) exit(0) diff --git a/tests/integration/crypto_tls13_peer/test_tls13_peer.sh b/tests/integration/crypto_tls13_peer/test_tls13_peer.sh index a1dbabb36..45707f7b2 100644 --- a/tests/integration/crypto_tls13_peer/test_tls13_peer.sh +++ b/tests/integration/crypto_tls13_peer/test_tls13_peer.sh @@ -62,9 +62,26 @@ cleanup() { trap cleanup EXIT fail() { echo " [FAIL] $NAME: $1"; exit 1; } +# The seven programs build at once, and at -O0 (--quick): each is the whole +# pure TLS stack in one C file, which GCC on Windows took 18-23 s to compile +# at -O2 (2-4 s on macOS), so the builds alone came to 168 s of the test's +# 440 on the win11 lab host, and to past the sweep's 420 s limit under its +# load. At -O0 they take 7-9 s each there, and the cases, which check what +# goes over the wire, run in the same time (about 1 s a handshake). +bpids="" for p in peer client server rawclient peerclient kuclient kuserver; do - "$AE" build "$SCRIPT_DIR/$p.ae" -o "$TMP/$p" >"$TMP/b_$p.log" 2>&1 \ - || { sed -n '1,12p' "$TMP/b_$p.log"; fail "$p did not build"; } + "$AE" build --quick "$SCRIPT_DIR/$p.ae" -o "$TMP/$p" >"$TMP/b_$p.log" 2>&1 & + bpids="$bpids $p:$!" +done +# Every build is waited for before one that failed ends the test, so none +# is left writing into $TMP as the trap removes it. +bfail="" +for bp in $bpids; do + wait "${bp#*:}" || bfail="$bfail ${bp%%:*}" +done +for p in $bfail; do + sed -n '1,12p' "$TMP/b_$p.log" + fail "$p did not build" done fails=0 diff --git a/tests/integration/crypto_tls13_resume/test_tls13_resume.sh b/tests/integration/crypto_tls13_resume/test_tls13_resume.sh index a337c77f9..d8d469e28 100644 --- a/tests/integration/crypto_tls13_resume/test_tls13_resume.sh +++ b/tests/integration/crypto_tls13_resume/test_tls13_resume.sh @@ -246,7 +246,7 @@ if command -v openssl >/dev/null 2>&1 && openssl s_server -help 2>&1 | grep -q - grep -q ACCEPTED "$TMP/server.out"; then echo " ok: $sc_name" else - echo " FAIL: $sc_name: server said [$(tr '\n' '|' <"$TMP/server.out")], s_client [$(grep -i 'peer sig\|temp key\|alert\|error' "$TMP/c.log" | tr '\n' '|')]" + echo " FAIL: $sc_name: server said [$(tr '\n' '|' <"$TMP/server.out")], s_client [$(grep -i -E 'peer sig|temp key|alert|error' "$TMP/c.log" | tr '\n' '|')]" fails=$((fails + 1)) fi } diff --git a/tests/integration/crypto_tls13_signature_check/test_tls13_signature_check.sh b/tests/integration/crypto_tls13_signature_check/test_tls13_signature_check.sh index 53b134805..e21de650c 100644 --- a/tests/integration/crypto_tls13_signature_check/test_tls13_signature_check.sh +++ b/tests/integration/crypto_tls13_signature_check/test_tls13_signature_check.sh @@ -36,6 +36,9 @@ NAME=crypto_tls13_signature_check [ -x "$AE" ] || { echo " [SKIP] $NAME: ae not built"; exit 0; } command -v openssl >/dev/null 2>&1 || { echo " [SKIP] $NAME: no openssl"; exit 0; } unset SSL_CERT_FILE SSL_CERT_DIR || : +. "$ROOT/tests/lib/test_openssl_conf.sh" +openssl_has_option s_client -sigalgs || { + echo " [SKIP] $NAME: $(openssl version) s_client has no -sigalgs to pin one scheme"; exit 0; } TMP="$(mktemp -d)" . "$ROOT/tests/lib/test_openssl_conf.sh" @@ -105,14 +108,14 @@ check() { if signed_with "$2" "$3" "$4" && grep -q ACCEPTED "$TMP/server.out"; then echo " ok: $2: its own key's signature is sent" else - echo " FAIL: $2, its own key: server [$(tr '\n' '|' <"$TMP/server.out")], s_client [$(grep -i 'signature type\|signing digest\|alert\|error' "$TMP/c.log" | tr '\n' '|')]" + echo " FAIL: $2, its own key: server [$(tr '\n' '|' <"$TMP/server.out")], s_client [$(grep -i -E 'signature type|signing digest|alert|error' "$TMP/c.log" | tr '\n' '|')]" fails=$((fails + 1)) fi serve "$TMP/$1.pem" "$TMP/$1.other.key" "$2" - if grep -q "REFUSED: .*not sent" "$TMP/server.out" && grep -qi "alert internal error\|alert number 80" "$TMP/c.log"; then + if grep -q "REFUSED: .*not sent" "$TMP/server.out" && grep -qi -E "alert internal error|alert number 80" "$TMP/c.log"; then echo " ok: $2: a signature that does not verify is not sent (internal_error)" else - echo " FAIL: $2, another key: server [$(tr '\n' '|' <"$TMP/server.out")], s_client [$(grep -i 'alert\|error' "$TMP/c.log" | tr '\n' '|')]" + echo " FAIL: $2, another key: server [$(tr '\n' '|' <"$TMP/server.out")], s_client [$(grep -i -E 'alert|error' "$TMP/c.log" | tr '\n' '|')]" fails=$((fails + 1)) fi } @@ -135,10 +138,10 @@ for c in "p256 ecdsa_secp256r1_sha256" "ed25519 ed25519" "rsa rsa_pss_rsae_sha25 export SERVER_CORRUPT_KEY=1 serve "$TMP/$1.pem" "$TMP/$1.key" "$2" unset SERVER_CORRUPT_KEY - if grep -q "REFUSED: .*changed in memory" "$TMP/server.out" && grep -qi "alert internal error\|alert number 80" "$TMP/c.log"; then + if grep -q "REFUSED: .*changed in memory" "$TMP/server.out" && grep -qi -E "alert internal error|alert number 80" "$TMP/c.log"; then echo " ok: $1: a shielded key with a bit changed is not used (internal_error)" else - echo " FAIL: $1, corrupted shield: server [$(tr '\n' '|' <"$TMP/server.out")], s_client [$(grep -i 'alert\|error' "$TMP/c.log" | tr '\n' '|')]" + echo " FAIL: $1, corrupted shield: server [$(tr '\n' '|' <"$TMP/server.out")], s_client [$(grep -i -E 'alert|error' "$TMP/c.log" | tr '\n' '|')]" fails=$((fails + 1)) fi done diff --git a/tests/integration/crypto_tls13_socket_verify_mtls/test_tls13_socket_verify_mtls.sh b/tests/integration/crypto_tls13_socket_verify_mtls/test_tls13_socket_verify_mtls.sh index 2182a39ae..208c50d7d 100755 --- a/tests/integration/crypto_tls13_socket_verify_mtls/test_tls13_socket_verify_mtls.sh +++ b/tests/integration/crypto_tls13_socket_verify_mtls/test_tls13_socket_verify_mtls.sh @@ -57,7 +57,7 @@ ssl env MSYS2_ARG_CONV_EXCL='/CN=' openssl req -x509 -new -key "$TMP/root.key" - for k in srv cli; do ssl env MSYS2_ARG_CONV_EXCL='/CN=' openssl req -new -key "$TMP/$k.key" -out "$TMP/$k.csr" -subj "/CN=$k" ssl openssl x509 -req -in "$TMP/$k.csr" -CA "$TMP/root.pem" -CAkey "$TMP/root.key" -CAcreateserial \ - -days 2 -extfile "$TMP/$k.ext" -out "$TMP/$k.pem" + -days 2 -sha256 -extfile "$TMP/$k.ext" -out "$TMP/$k.pem" done # A self-signed localhost certificate nothing trusts. ssl env MSYS2_ARG_CONV_EXCL='/CN=' openssl req -x509 -new -key "$TMP/self.key" -out "$TMP/self.pem" -days 2 \ @@ -103,7 +103,7 @@ expect() { label=$1; pat=$2 case "$got" in $pat) echo " ok: $label" ;; - *) echo " FAIL: $label: client [$got], server [$(grep -i 'error\|alert\|verify' "$TMP/s.log" | tr '\n' '|')]"; fails=$((fails + 1)) ;; + *) echo " FAIL: $label: client [$got], server [$(grep -i -E 'error|alert|verify' "$TMP/s.log" | tr '\n' '|')]"; fails=$((fails + 1)) ;; esac } diff --git a/tests/integration/deep_constants/test_deep_constants.sh b/tests/integration/deep_constants/test_deep_constants.sh index 65a25fc72..6d2d17439 100755 --- a/tests/integration/deep_constants/test_deep_constants.sh +++ b/tests/integration/deep_constants/test_deep_constants.sh @@ -70,14 +70,22 @@ case "$got" in esac AETHER_HOME="$ROOT" "$AETHERC" "$WORK/consts.ae" "$WORK/consts.c" >/dev/null 2>&1 -d="$(awk 'BEGIN { d = 0; m = 0 } +# LC_ALL=C: under a UTF-8 locale gawk's substr counts characters from the +# start of the line on every call, so this walk is quadratic in a line, and +# consts.c has lines of 245 KB: 361 s of the test on the win11 lab host and +# 292 s on CI's Windows runner, against 6 s in the C locale. The walk only +# compares ASCII, which a UTF-8 multibyte character never contains. +d="$(LC_ALL=C awk 'BEGIN { d = 0; m = 0 } /^[ \t]*#/ { next } { gsub(/_AE_SEQ_OPEN/, "({"); gsub(/_AE_SEQ_CLOSE/, "; })") - n = length($0); str = 0 + # One split, not a substr($0, i, 1) per character: the awk of OpenBSD + # rescans the line from its start on every substr, so a long line + # (these hold 1000-link chains) took six minutes to scan there. + n = split($0, ch, ""); str = 0 for (i = 1; i <= n; i++) { - c = substr($0, i, 1) + c = ch[i] if (str) { if (c == "\\") { i++; continue } if (c == q) str = 0; continue } - if (c == "/" && substr($0, i + 1, 1) == "/") break + if (c == "/" && ch[i + 1] == "/") break if (c == "\"" || c == "\047") { str = 1; q = c; continue } if (c == "(" || c == "[" || c == "{") { d++; if (d > m) m = d } else if (c == ")" || c == "]" || c == "}") d-- diff --git a/tests/integration/deep_nesting/test_deep_nesting.sh b/tests/integration/deep_nesting/test_deep_nesting.sh index ba2b272ba..afffecbcb 100755 --- a/tests/integration/deep_nesting/test_deep_nesting.sh +++ b/tests/integration/deep_nesting/test_deep_nesting.sh @@ -39,7 +39,7 @@ depths="" # The deepest ( [ { of a C file outside literals, comments and directives, # with the sequence macros spelled as a GCC-compatible compiler has them. c_depth() { - awk 'BEGIN { d = 0; m = 0; com = 0 } + LC_ALL=C awk 'BEGIN { d = 0; m = 0; com = 0 } /^[ \t]*#/ { next } { gsub(/_AE_SEQ_OPEN/, "({"); gsub(/_AE_SEQ_CLOSE/, "; })") n = length($0); str = 0 diff --git a/tests/integration/emit_csrc_cross/test_emit_csrc_cross.sh b/tests/integration/emit_csrc_cross/test_emit_csrc_cross.sh index 3f5cebbdf..6777e0868 100755 --- a/tests/integration/emit_csrc_cross/test_emit_csrc_cross.sh +++ b/tests/integration/emit_csrc_cross/test_emit_csrc_cross.sh @@ -21,6 +21,7 @@ set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/portable.sh" AE="$ROOT/build/ae" SRC="$SCRIPT_DIR/mylib.ae" @@ -86,7 +87,7 @@ if command -v zig >/dev/null 2>&1; then fail "--target=$t --emit=obj was rejected" fi [ -s "$TMP/o_$t.o" ] || fail "no object emitted for $t" - desc="$(file "$TMP/o_$t.o")" + desc="$(file_desc "$TMP/o_$t.o")" case "$desc" in *"$want"*) ;; *) fail "$t object is not $want format: $desc" ;; @@ -121,9 +122,9 @@ if command -v zig >/dev/null 2>&1; then || fail "wasm32-wasi csrc differs; csrc is meant to be target-neutral" "$AE" build --target=wasm32-wasi --emit=obj "$SRC" -o "$TMP/wasi.o" >"$TMP/wasiobj" 2>&1 || { cat "$TMP/wasiobj"; fail "wasm32-wasi --emit=obj was rejected"; } - case "$(file "$TMP/wasi.o")" in + case "$(file_desc "$TMP/wasi.o")" in *WebAssembly*) ;; - *) fail "wasm32-wasi object is not a wasm module: $(file "$TMP/wasi.o")" ;; + *) fail "wasm32-wasi object is not a wasm module: $(file_desc "$TMP/wasi.o")" ;; esac # A full executable link now WORKS for wasm32-wasi (#1655). It was rejected @@ -135,9 +136,9 @@ if command -v zig >/dev/null 2>&1; then cat "$TMP/wasiexe" fail "wasm32-wasi executable link failed" fi - case "$(file "$TMP/app.wasm")" in + case "$(file_desc "$TMP/app.wasm")" in *WebAssembly*) ;; - *) fail "wasi exe is not a wasm module: $(file "$TMP/app.wasm")" ;; + *) fail "wasi exe is not a wasm module: $(file_desc "$TMP/app.wasm")" ;; esac # An ACTOR program must link too. This is the case that caught the real diff --git a/tests/integration/emit_lib/test_emit_lib.sh b/tests/integration/emit_lib/test_emit_lib.sh index e85f5927a..cc95023b2 100755 --- a/tests/integration/emit_lib/test_emit_lib.sh +++ b/tests/integration/emit_lib/test_emit_lib.sh @@ -5,12 +5,13 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" pass=0 fail=0 # Platform: Linux uses .so, macOS .dylib, Windows .dll (which needs no -ldl) -LDL="-ldl" +LDL="$AE_DL_LIBS" case "$(uname -s 2>/dev/null)" in Darwin) LIB_EXT=".dylib" ;; MINGW*|MSYS*|CYGWIN*|Windows_NT) LIB_EXT=".dll"; LDL="" ;; diff --git a/tests/integration/emit_lib_accounts/test_emit_lib_accounts.sh b/tests/integration/emit_lib_accounts/test_emit_lib_accounts.sh index cbd4e4fe2..c93ea5d0a 100644 --- a/tests/integration/emit_lib_accounts/test_emit_lib_accounts.sh +++ b/tests/integration/emit_lib_accounts/test_emit_lib_accounts.sh @@ -20,6 +20,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" AE="$ROOT/build/ae" AETHERC="$ROOT/build/aetherc" CC="${CC:-cc}" diff --git a/tests/integration/emit_lib_clause_sets/test_emit_lib_clause_sets.sh b/tests/integration/emit_lib_clause_sets/test_emit_lib_clause_sets.sh index 121c6f326..d6d85b044 100644 --- a/tests/integration/emit_lib_clause_sets/test_emit_lib_clause_sets.sh +++ b/tests/integration/emit_lib_clause_sets/test_emit_lib_clause_sets.sh @@ -27,6 +27,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" AE="$ROOT/build/ae" if [ ! -x "$AE" ] && [ ! -x "$AE.exe" ]; then @@ -34,7 +35,7 @@ if [ ! -x "$AE" ] && [ ! -x "$AE.exe" ]; then exit 0 fi -LDL="-ldl" +LDL="$AE_DL_LIBS" case "$(uname -s 2>/dev/null)" in Darwin) LIB_EXT=".dylib" ;; MINGW*|MSYS*|CYGWIN*|Windows_NT) LIB_EXT=".dll"; LDL="" ;; diff --git a/tests/integration/emit_lib_composite/test_emit_lib_composite.sh b/tests/integration/emit_lib_composite/test_emit_lib_composite.sh index 6c3815060..6774b7083 100755 --- a/tests/integration/emit_lib_composite/test_emit_lib_composite.sh +++ b/tests/integration/emit_lib_composite/test_emit_lib_composite.sh @@ -9,6 +9,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" LINK_FLAGS="" case "$(uname -s 2>/dev/null)" in @@ -51,7 +52,7 @@ fi # Windows the DLL is found beside the consumer; elsewhere through the rpath. case "$LIB_EXT" in .dll) ;; - *) LINK_FLAGS="-Wl,-rpath,$TMPDIR -ldl" ;; + *) LINK_FLAGS="-Wl,-rpath,$TMPDIR $AE_DL_LIBS" ;; esac # shellcheck disable=SC2086 if ! ${CC:-cc} \ diff --git a/tests/integration/emit_lib_execution_frame/test_emit_lib_execution_frame.sh b/tests/integration/emit_lib_execution_frame/test_emit_lib_execution_frame.sh index 36d21ef2e..d371b5585 100644 --- a/tests/integration/emit_lib_execution_frame/test_emit_lib_execution_frame.sh +++ b/tests/integration/emit_lib_execution_frame/test_emit_lib_execution_frame.sh @@ -19,6 +19,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" AE="$ROOT/build/ae" AETHERC="$ROOT/build/aetherc" CC="${CC:-cc}" diff --git a/tests/integration/emit_lib_keeps_main/test_emit_lib_keeps_main.sh b/tests/integration/emit_lib_keeps_main/test_emit_lib_keeps_main.sh index 65bff3abe..e76a7206d 100755 --- a/tests/integration/emit_lib_keeps_main/test_emit_lib_keeps_main.sh +++ b/tests/integration/emit_lib_keeps_main/test_emit_lib_keeps_main.sh @@ -44,6 +44,8 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/portable.sh" +. "$ROOT/tests/lib/syslibs.sh" AE="$ROOT/build/ae" AETHERC="$ROOT/build/aetherc" @@ -60,17 +62,15 @@ pass=0; fail=0 ok() { echo " [PASS] $1"; pass=$((pass + 1)); } bad() { echo " [FAIL] $1"; fail=$((fail + 1)); } -# Dynamic symbols, portably: a DLL's export table (objdump -p), GNU nm -D, -# else macOS nm -gU (leading '_'). +# Dynamic symbols, portably: a DLL's export table (objdump -p), else +# dyn_syms (tests/lib/portable.sh: GNU nm -D, macOS nm -gU, OpenBSD nm -g). syms() { case "$1" in *.dll) objdump -p "$1" 2>/dev/null | sed -n '/\[Ordinal\/Name Pointer\] Table/,/^$/p' return ;; esac - s="$(nm -D --defined-only "$1" 2>/dev/null || true)" - [ -n "$s" ] || s="$(nm -gU "$1" 2>/dev/null || true)" - printf '%s\n' "$s" + dyn_syms "$1" } exports() { # exports syms "$1" | grep -E "[[:space:]]_?$2\$" >/dev/null diff --git a/tests/integration/emit_lib_kind_safe/test_emit_lib_kind_safe.sh b/tests/integration/emit_lib_kind_safe/test_emit_lib_kind_safe.sh index 8d89279fa..a8e1fada2 100755 --- a/tests/integration/emit_lib_kind_safe/test_emit_lib_kind_safe.sh +++ b/tests/integration/emit_lib_kind_safe/test_emit_lib_kind_safe.sh @@ -20,6 +20,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" # The host loads the library by path (dlopen, LoadLibrary on Windows) and # links it for the aether_value_* accessors. Windows finds the DLL beside @@ -33,7 +34,7 @@ esac TMPDIR="$(mktemp -d)" trap 'rm -rf "$TMPDIR"' EXIT LINK_FLAGS="" -[ "$LIB_EXT" = .dll ] || LINK_FLAGS="-Wl,-rpath,$TMPDIR -ldl" +[ "$LIB_EXT" = .dll ] || LINK_FLAGS="-Wl,-rpath,$TMPDIR $AE_DL_LIBS" pass=0 fail=0 diff --git a/tests/integration/emit_lib_lists/test_emit_lib_lists.sh b/tests/integration/emit_lib_lists/test_emit_lib_lists.sh index 8b3edd340..4ec666be4 100755 --- a/tests/integration/emit_lib_lists/test_emit_lib_lists.sh +++ b/tests/integration/emit_lib_lists/test_emit_lib_lists.sh @@ -2,6 +2,7 @@ # List-return test for --emit=lib. SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" # The host loads the library by path (dlopen, LoadLibrary on Windows) and # links it; Windows finds the DLL beside the host, with no rpath or -ldl. @@ -13,7 +14,7 @@ esac TMPDIR="$(mktemp -d)"; trap 'rm -rf "$TMPDIR"' EXIT LINK_FLAGS="" -[ "$LIB_EXT" = .dll ] || LINK_FLAGS="-Wl,-rpath,$TMPDIR -ldl" +[ "$LIB_EXT" = .dll ] || LINK_FLAGS="-Wl,-rpath,$TMPDIR $AE_DL_LIBS" pass=0; fail=0 cd "$SCRIPT_DIR" diff --git a/tests/integration/emit_lib_net/test_emit_lib_net.sh b/tests/integration/emit_lib_net/test_emit_lib_net.sh index 9bf280054..939d0a309 100755 --- a/tests/integration/emit_lib_net/test_emit_lib_net.sh +++ b/tests/integration/emit_lib_net/test_emit_lib_net.sh @@ -7,6 +7,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" # The driver loads the library by path: dlopen (-ldl on Linux), or # LoadLibrary on Windows. @@ -14,7 +15,7 @@ LDL="" case "$(uname -s 2>/dev/null)" in MINGW*|MSYS*|CYGWIN*|Windows_NT) LIB_EXT=".dll" ;; Darwin) LIB_EXT=".dylib" ;; - *) LIB_EXT=".so"; LDL="-ldl" ;; + *) LIB_EXT=".so"; LDL="$AE_DL_LIBS" ;; esac TMPDIR="$(mktemp -d)" diff --git a/tests/integration/emit_lib_primitives/test_emit_lib_primitives.sh b/tests/integration/emit_lib_primitives/test_emit_lib_primitives.sh index a1854d8bc..6f5eeaad3 100755 --- a/tests/integration/emit_lib_primitives/test_emit_lib_primitives.sh +++ b/tests/integration/emit_lib_primitives/test_emit_lib_primitives.sh @@ -2,6 +2,7 @@ # int64/bool/float primitive round-trip for --emit=lib. SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" # The host loads the library by path (dlopen, LoadLibrary on Windows) and # links it; Windows finds the DLL beside the host, with no rpath or -ldl. @@ -13,7 +14,7 @@ esac TMPDIR="$(mktemp -d)"; trap 'rm -rf "$TMPDIR"' EXIT LINK_FLAGS="" -[ "$LIB_EXT" = .dll ] || LINK_FLAGS="-Wl,-rpath,$TMPDIR -ldl" +[ "$LIB_EXT" = .dll ] || LINK_FLAGS="-Wl,-rpath,$TMPDIR $AE_DL_LIBS" pass=0; fail=0 cd "$SCRIPT_DIR" diff --git a/tests/integration/emit_lib_typed_ptr/test_emit_lib_typed_ptr.sh b/tests/integration/emit_lib_typed_ptr/test_emit_lib_typed_ptr.sh index 4f42a5694..22b04b27e 100755 --- a/tests/integration/emit_lib_typed_ptr/test_emit_lib_typed_ptr.sh +++ b/tests/integration/emit_lib_typed_ptr/test_emit_lib_typed_ptr.sh @@ -8,6 +8,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" # The host loads the library by path: dlopen (-ldl on Linux), or # LoadLibrary on Windows. @@ -15,7 +16,7 @@ LDL="" case "$(uname -s 2>/dev/null)" in MINGW*|MSYS*|CYGWIN*|Windows_NT) LIB_EXT=".dll" ;; Darwin) LIB_EXT=".dylib" ;; - *) LIB_EXT=".so"; LDL="-ldl" ;; + *) LIB_EXT=".so"; LDL="$AE_DL_LIBS" ;; esac TMPDIR="$(mktemp -d)" diff --git a/tests/integration/emit_lib_unsupported/test_emit_lib_unsupported.sh b/tests/integration/emit_lib_unsupported/test_emit_lib_unsupported.sh index 15b17c2f3..d0de2cd4a 100755 --- a/tests/integration/emit_lib_unsupported/test_emit_lib_unsupported.sh +++ b/tests/integration/emit_lib_unsupported/test_emit_lib_unsupported.sh @@ -42,7 +42,7 @@ else # Check (c) — a warning mentions the skipped function. if grep -q "takes_optional" "$TMPDIR/stderr.log" && \ - grep -q "skipping alias stub\|representable in the" "$TMPDIR/stderr.log"; then + grep -q -E "skipping alias stub|representable in the" "$TMPDIR/stderr.log"; then echo " [PASS] warning emitted for unsupported param type" pass=$((pass + 1)) else diff --git a/tests/integration/emit_lib_windows_dll/test_emit_lib_windows_dll.sh b/tests/integration/emit_lib_windows_dll/test_emit_lib_windows_dll.sh index ba7adcbc9..dff79da50 100644 --- a/tests/integration/emit_lib_windows_dll/test_emit_lib_windows_dll.sh +++ b/tests/integration/emit_lib_windows_dll/test_emit_lib_windows_dll.sh @@ -38,7 +38,7 @@ AE # /build, so name it) if ! (cd "$tmp" && "$AE" build --emit=lib mathlib.ae -o "$tmp/libmathlib.dll" > "$tmp/build.log" 2>&1); then echo " [FAIL] emit_lib_windows_dll: ae build --emit=lib failed" - grep -i "undefined\|error" "$tmp/build.log" | head -3 | sed 's/^/ /' + grep -i -E "undefined|error" "$tmp/build.log" | head -3 | sed 's/^/ /' exit 1 fi dll="$(find "$tmp" -name 'libmathlib.dll' | head -1)" @@ -101,7 +101,7 @@ mkdir -p "$tmp/ns" cp "$ROOT/tests/integration/namespace_basic/manifest.ae" "$ROOT/tests/integration/namespace_basic/hello.ae" "$tmp/ns/" if ! (cd "$tmp/ns" && AETHER_HOME="$ROOT" "$AE" build --namespace . > "$tmp/ns.log" 2>&1); then echo " [FAIL] emit_lib_windows_dll: ae build --namespace . failed" - grep -iv "^\s*$" "$tmp/ns.log" | head -4 | sed 's/^/ /' + grep -iv "^[[:space:]]*$" "$tmp/ns.log" | head -4 | sed 's/^/ /' fail=1 elif [ ! -f "$tmp/ns/libgreet.dll" ]; then echo " [FAIL] emit_lib_windows_dll: --namespace did not produce libgreet.dll (named from the manifest)" diff --git a/tests/integration/emit_lib_with_capability/test_emit_lib_with_capability.sh b/tests/integration/emit_lib_with_capability/test_emit_lib_with_capability.sh index 5aedf3ddd..1a6ea6836 100755 --- a/tests/integration/emit_lib_with_capability/test_emit_lib_with_capability.sh +++ b/tests/integration/emit_lib_with_capability/test_emit_lib_with_capability.sh @@ -40,7 +40,7 @@ run_case() { fail=$((fail + 1)) fi else - if [ "$rc" -ne 0 ] && grep -q "capability-empty\|--with=" "$TMPDIR/stderr"; then + if [ "$rc" -ne 0 ] && grep -q -E "capability-empty|--with=" "$TMPDIR/stderr"; then echo " [PASS] $label"; pass=$((pass + 1)) else echo " [FAIL] $label — expected reject (rc=$rc, stderr:)" diff --git a/tests/integration/eval_order_left_to_right/test_eval_order_left_to_right.sh b/tests/integration/eval_order_left_to_right/test_eval_order_left_to_right.sh index 4f485359d..537108d64 100644 --- a/tests/integration/eval_order_left_to_right/test_eval_order_left_to_right.sh +++ b/tests/integration/eval_order_left_to_right/test_eval_order_left_to_right.sh @@ -129,7 +129,7 @@ global 11 23 closure 55 pointer 22 message 12 2" -got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "$want" ]; then echo " [FAIL] eval_order_left_to_right: operands not evaluated left to right" printf 'got:\n%s\nwant:\n%s\n' "$got" "$want" | sed 's/^/ /' diff --git a/tests/integration/f32_arithmetic/test_f32_arithmetic.sh b/tests/integration/f32_arithmetic/test_f32_arithmetic.sh index 0fa160e97..ac0e1b490 100644 --- a/tests/integration/f32_arithmetic/test_f32_arithmetic.sh +++ b/tests/integration/f32_arithmetic/test_f32_arithmetic.sh @@ -52,7 +52,7 @@ main() { } AE -got="$("$AE" run "$tmp/main.ae" 2>&1 | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$" | tr '\n' ' ')" +got="$("$AE" run "$tmp/main.ae" 2>&1 | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$" | tr '\n' ' ')" if [ "$got" != "float-exact true 14 1 0.05 0.2 2.15 0.4 0.05 -0.05 0.05 -0.05 true " ]; then echo " [FAIL] f32_arithmetic: program output (got '$got')" fail=1 diff --git a/tests/integration/fixed_array_capture_and_state/test_fixed_array_capture_and_state.sh b/tests/integration/fixed_array_capture_and_state/test_fixed_array_capture_and_state.sh index 2c1c43b1c..3beff01e7 100644 --- a/tests/integration/fixed_array_capture_and_state/test_fixed_array_capture_and_state.sh +++ b/tests/integration/fixed_array_capture_and_state/test_fixed_array_capture_and_state.sh @@ -72,7 +72,7 @@ h 4 pick bo outer 1 state 970" -got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "$want" ]; then echo " [FAIL] fixed_array_capture_and_state: wrong output" printf 'got:\n%s\nwant:\n%s\n' "$got" "$want" | sed 's/^/ /' diff --git a/tests/integration/fmt_gate/test_fmt_gate.sh b/tests/integration/fmt_gate/test_fmt_gate.sh index 706e9aa27..fba666ee6 100755 --- a/tests/integration/fmt_gate/test_fmt_gate.sh +++ b/tests/integration/fmt_gate/test_fmt_gate.sh @@ -28,6 +28,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" . "$ROOT/tests/lib/same_bytes.sh" +. "$ROOT/tests/lib/portable.sh" AE="$ROOT/build/ae" AETHERC="$ROOT/build/aetherc" @@ -157,7 +158,7 @@ find examples tests -name '*.ae' -type f 2>/dev/null | LC_ALL=C sort | \ # custom_lib_dir/.mylib; and what --check lists is then the sample's path. mkdir "$TMPDIR/idem" TOTAL=$(awk 'END { print NR }' "$TMPDIR/sample.txt") -if ! tar cf - -T "$TMPDIR/sample.txt" | (cd "$TMPDIR/idem" && tar xf -) || +if ! copy_listed "$TMPDIR/idem" < "$TMPDIR/sample.txt" || [ "$(find "$TMPDIR/idem" -type f | wc -l | tr -d ' ')" != "$TOTAL" ]; then echo " [FAIL] fmt_gate: could not copy the $TOTAL sampled files for the idempotence tier" exit 1 diff --git a/tests/integration/fn_param_call_dispatch/test_fn_param_call_dispatch.sh b/tests/integration/fn_param_call_dispatch/test_fn_param_call_dispatch.sh index 8b046ca03..e0b955a6c 100644 --- a/tests/integration/fn_param_call_dispatch/test_fn_param_call_dispatch.sh +++ b/tests/integration/fn_param_call_dispatch/test_fn_param_call_dispatch.sh @@ -63,7 +63,7 @@ twice 3 twice h 14 maker's f 5 k 7" -got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "$want" ]; then echo " [FAIL] fn_param_call_dispatch: a call on an fn parameter did not go through its value" printf 'got:\n%s\nwant:\n%s\n' "$got" "$want" | sed 's/^/ /' diff --git a/tests/integration/fs_open_binary_mode/test_fs_open_binary_mode.sh b/tests/integration/fs_open_binary_mode/test_fs_open_binary_mode.sh index bf976449c..e72a2bb62 100644 --- a/tests/integration/fs_open_binary_mode/test_fs_open_binary_mode.sh +++ b/tests/integration/fs_open_binary_mode/test_fs_open_binary_mode.sh @@ -72,7 +72,7 @@ pread 6 true crlf 6 true append size 8 wt true ' -got="$(AETHER_HOME="$ROOT" "$AE" run "$tmp/main.ae" -- "$tmp" 2>&1 | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$(AETHER_HOME="$ROOT" "$AE" run "$tmp/main.ae" -- "$tmp" 2>&1 | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "$want" ]; then echo " [FAIL] fs_open_binary_mode: output differs" printf '%s\n' "$got" | head -8 | sed 's/^/ /' diff --git a/tests/integration/function_clause_string_pattern/test_function_clause_string_pattern.sh b/tests/integration/function_clause_string_pattern/test_function_clause_string_pattern.sh index 6759415a4..85026805c 100644 --- a/tests/integration/function_clause_string_pattern/test_function_clause_string_pattern.sh +++ b/tests/integration/function_clause_string_pattern/test_function_clause_string_pattern.sh @@ -45,7 +45,7 @@ hello amy quoted hi bob! 1 2 3' -got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v "^warning\|^ *-->\|^ *[0-9]* |\|^ *|\|^Type checking\|^$")" +got="$("$AE" run "$tmp/main.ae" 2>&1 | tr -d '\r' | grep -v -E "^warning|^ *-->|^ *[0-9]* [|]|^ *[|]|^Type checking|^$")" if [ "$got" != "$want" ]; then echo " [FAIL] function_clause_string_pattern: wrong output" printf 'got:\n%s\nwant:\n%s\n' "$got" "$want" | sed 's/^/ /' diff --git a/tests/integration/host_bridge_import_link/test_host_bridge_import_link.sh b/tests/integration/host_bridge_import_link/test_host_bridge_import_link.sh index adb91d092..cd1afd657 100755 --- a/tests/integration/host_bridge_import_link/test_host_bridge_import_link.sh +++ b/tests/integration/host_bridge_import_link/test_host_bridge_import_link.sh @@ -103,7 +103,7 @@ elif ! grep -q "libaether_host_definitelynothere.a" "$tmpdir/c.log"; then echo " [FAIL] host_bridge_import_link C: error did not name the missing .a" sed 's/^/ /' "$tmpdir/c.log" | head -5 fail=1 -elif ! grep -q "make contrib\|install-contrib" "$tmpdir/c.log"; then +elif ! grep -q -E "make contrib|install-contrib" "$tmpdir/c.log"; then echo " [FAIL] host_bridge_import_link C: error did not point at remediation" sed 's/^/ /' "$tmpdir/c.log" | head -5 fail=1 diff --git a/tests/integration/host_child_signal_safe/test_host_child_signal_safe.sh b/tests/integration/host_child_signal_safe/test_host_child_signal_safe.sh index 028d9a632..e60fb39f3 100755 --- a/tests/integration/host_child_signal_safe/test_host_child_signal_safe.sh +++ b/tests/integration/host_child_signal_safe/test_host_child_signal_safe.sh @@ -9,6 +9,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/syslibs.sh" case "$(uname -s 2>/dev/null)" in Linux) ;; @@ -42,7 +43,7 @@ if ! ${CC:-cc} $INC -I"$ROOT/contrib/host/aether" \ # shellcheck disable=SC2086 if ! ${CC:-cc} $INC -I"$ROOT/contrib/host/aether" \ -o "$TMP/harness" $SRC "$ROOT/build/libaether.a" \ - -lpthread -ldl -lm -lrt -lz -lssl -lcrypto 2>"$TMP/cc.err"; then + -lpthread $AE_DL_LIBS -lm $AE_RT_LIBS -lz -lssl -lcrypto 2>"$TMP/cc.err"; then echo " [FAIL] could not link the harness against the host and libaether.a" sed 's/^/ /' "$TMP/cc.err" | head -8 exit 1 diff --git a/tests/integration/http_client_custom_ca/test_http_client_custom_ca.sh b/tests/integration/http_client_custom_ca/test_http_client_custom_ca.sh index 0fc233876..ca4f13cd6 100755 --- a/tests/integration/http_client_custom_ca/test_http_client_custom_ca.sh +++ b/tests/integration/http_client_custom_ca/test_http_client_custom_ca.sh @@ -63,7 +63,7 @@ if ! openssl req -newkey rsa:2048 -keyout "$KEY" -out "$TMPDIR/leaf.csr" \ -nodes -subj "/CN=127.0.0.1" 2>"$TMPDIR/o2.err" \ || ! printf 'subjectAltName=IP:127.0.0.1\n' > "$TMPDIR/ext.cnf" \ || ! openssl x509 -req -in "$TMPDIR/leaf.csr" -CA "$CA" -CAkey "$CAKEY" \ - -CAcreateserial -out "$CERT" -days 1 -extfile "$TMPDIR/ext.cnf" \ + -CAcreateserial -out "$CERT" -days 1 -sha256 -extfile "$TMPDIR/ext.cnf" \ 2>"$TMPDIR/o3.err"; then echo " [SKIP] openssl (sign leaf) failed:"; head -5 "$TMPDIR/o3.err"; exit 0 fi diff --git a/tests/integration/http_h2_concurrent_dispatch/test_http_h2_concurrent_dispatch.sh b/tests/integration/http_h2_concurrent_dispatch/test_http_h2_concurrent_dispatch.sh index c4e8c2644..d8429fcec 100755 --- a/tests/integration/http_h2_concurrent_dispatch/test_http_h2_concurrent_dispatch.sh +++ b/tests/integration/http_h2_concurrent_dispatch/test_http_h2_concurrent_dispatch.sh @@ -35,6 +35,7 @@ set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/portable.sh" . "$ROOT/tests/lib/wait_port.sh" . "$ROOT/tests/lib/server_jobs.sh" AE="$ROOT/build/ae" @@ -147,7 +148,7 @@ run_parallel() { while [ $j -lt "$n" ]; do if ! grep -q "^slow:s=$j\$" "$PAR_DIR/n$j" 2>/dev/null; then echo " [WARN] parallel x$n stream $j body mismatch on attempt $label:" >&2 - head -c 200 "$PAR_DIR/n$j" >&2 + head_c 200 "$PAR_DIR/n$j" >&2 echo >&2 return 1 fi diff --git a/tests/integration/http_ipv6/test_http_ipv6.sh b/tests/integration/http_ipv6/test_http_ipv6.sh index f17501cb0..1455f1bde 100755 --- a/tests/integration/http_ipv6/test_http_ipv6.sh +++ b/tests/integration/http_ipv6/test_http_ipv6.sh @@ -67,7 +67,7 @@ start() { SRV_PID=$! _i=0 while [ "$_i" -lt 100 ]; do - grep -q "READY\|BIND FAILED" "$_log" 2>/dev/null && break + grep -q -E "READY|BIND FAILED" "$_log" 2>/dev/null && break sleep 0.1 _i=$((_i + 1)) done @@ -130,9 +130,13 @@ stop_server "$SRV_PID"; SRV_PID="" # The "::" wildcard is what dual-stack means, and on Windows each new # executable listening on it opens a firewall prompt (#2639). There the -# dual-stack parts are left out and TLS runs over ::1 alone. +# dual-stack parts are left out and TLS runs over ::1 alone. OpenBSD has no +# dual-stack at all (inet6(4): an AF_INET6 socket is IPv6-only, and +# IPV6_V6ONLY cannot be cleared), so "::" there answers [::1] and never +# 127.0.0.1: the same parts are left out, for that reason. case "$(uname -s)" in - MINGW*|MSYS*|CYGWIN*) WILDCARD=no ;; + MINGW*|MSYS*|CYGWIN*) WILDCARD=no; WILDCARD_WHY="Windows (wildcard listener)" ;; + OpenBSD) WILDCARD=no; WILDCARD_WHY="OpenBSD (no dual-stack sockets)" ;; *) WILDCARD=yes ;; esac @@ -144,7 +148,7 @@ if [ "$WILDCARD" = yes ]; then stop_server "$SRV_PID"; SRV_PID="" DUAL_NOTE="dual-stack ::" else - DUAL_NOTE="dual-stack skipped on Windows (wildcard listener)" + DUAL_NOTE="dual-stack skipped on $WILDCARD_WHY" fi # --- 3. "localhost" still means 127.0.0.1 --- @@ -181,7 +185,7 @@ fi # --- 5. TinyWeb WebSocket, SSE and long poll --- if [ "$WILDCARD" = no ]; then - STREAMS_NOTE="TinyWeb streams skipped on Windows (wildcard listener)" + STREAMS_NOTE="TinyWeb streams skipped on $WILDCARD_WHY" else streams "plain HTTP" STREAMS_NOTE="ws/SSE/long poll over HTTP" diff --git a/tests/integration/http_proxy_direct_equivalence/test_http_proxy_direct_equivalence.sh b/tests/integration/http_proxy_direct_equivalence/test_http_proxy_direct_equivalence.sh index ac2292653..df5b407a0 100755 --- a/tests/integration/http_proxy_direct_equivalence/test_http_proxy_direct_equivalence.sh +++ b/tests/integration/http_proxy_direct_equivalence/test_http_proxy_direct_equivalence.sh @@ -14,6 +14,7 @@ set -e SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +. "$ROOT/tests/lib/portable.sh" AE="$ROOT/build/ae" SRC="$ROOT/tests/integration/http_reverse_proxy/server.ae" @@ -35,7 +36,7 @@ if ! AETHER_HOME="$ROOT" "$AE" build "$SRC" -o "$TMPDIR/server" >"$TMPDIR/build. echo " [FAIL] build:"; head -20 "$TMPDIR/build.log"; exit 1 fi -head -c 2048 /dev/urandom > "$TMPDIR/post.in" 2>/dev/null || \ +head_c 2048 /dev/urandom > "$TMPDIR/post.in" 2>/dev/null || \ { i=0; while [ $i -lt 64 ]; do printf 'payload-0123456789abcdef'; i=$((i+1)); done > "$TMPDIR/post.in"; } wait_ready() { @@ -57,6 +58,14 @@ UP_PID=$! wait_ready "$UP_PID" "$TMPDIR/up.log" upstream UP_PORT=$(read_ready_port "$TMPDIR/up.log") || exit 1 +# fix_length : add delta to the file's Content-Length. +fix_length() { + LC_ALL=C awk -v d="$2" ' + tolower(substr($0, 1, 15)) == "content-length:" { + printf "%s %d\r\n", substr($0, 1, 15), substr($0, 16) + d; next } + { print }' "$1" > "$1.norm" && mv "$1.norm" "$1" +} + # capture