summaryrefslogtreecommitdiff
path: root/corebinutils/sync/tests
diff options
context:
space:
mode:
Diffstat (limited to 'corebinutils/sync/tests')
-rw-r--r--corebinutils/sync/tests/test.sh188
1 files changed, 188 insertions, 0 deletions
diff --git a/corebinutils/sync/tests/test.sh b/corebinutils/sync/tests/test.sh
new file mode 100644
index 0000000000..b38f16fa34
--- /dev/null
+++ b/corebinutils/sync/tests/test.sh
@@ -0,0 +1,188 @@
+#!/bin/sh
+set -eu
+
+ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
+SYNC_BIN=${SYNC_BIN:-"$ROOT/out/sync"}
+CC=${CC:-cc}
+TMPDIR=${TMPDIR:-/tmp}
+WORKDIR=$(mktemp -d "$TMPDIR/sync-test.XXXXXX")
+HOOK_C="$WORKDIR/sync-hook.c"
+HOOK_SO="$WORKDIR/sync-hook.so"
+STDOUT_FILE="$WORKDIR/stdout"
+STDERR_FILE="$WORKDIR/stderr"
+SYNC_MARKER="$WORKDIR/sync-marker"
+trap 'rm -rf "$WORKDIR"' EXIT INT TERM
+
+export LC_ALL=C
+
+fail() {
+ printf '%s\n' "FAIL: $1" >&2
+ exit 1
+}
+
+assert_eq() {
+ name=$1
+ expected=$2
+ actual=$3
+ if [ "$expected" != "$actual" ]; then
+ printf '%s\n' "FAIL: $name" >&2
+ printf '%s\n' "--- expected ---" >&2
+ printf '%s' "$expected" >&2
+ printf '\n%s\n' "--- actual ---" >&2
+ printf '%s' "$actual" >&2
+ printf '\n' >&2
+ exit 1
+ fi
+}
+
+assert_empty() {
+ name=$1
+ text=$2
+ if [ -n "$text" ]; then
+ printf '%s\n' "FAIL: $name" >&2
+ printf '%s\n' "--- expected empty ---" >&2
+ printf '%s\n' "--- actual ---" >&2
+ printf '%s' "$text" >&2
+ printf '\n' >&2
+ exit 1
+ fi
+}
+
+assert_status() {
+ name=$1
+ expected=$2
+ actual=$3
+ if [ "$expected" -ne "$actual" ]; then
+ printf '%s\n' "FAIL: $name" >&2
+ printf '%s\n' "expected status: $expected" >&2
+ printf '%s\n' "actual status: $actual" >&2
+ exit 1
+ fi
+}
+
+run_capture() {
+ if "$@" >"$STDOUT_FILE" 2>"$STDERR_FILE"; then
+ LAST_STATUS=0
+ else
+ LAST_STATUS=$?
+ fi
+
+ LAST_STDOUT=$(cat "$STDOUT_FILE")
+ LAST_STDERR=$(cat "$STDERR_FILE")
+}
+
+build_sync_hook() {
+ cat >"$HOOK_C" <<'EOF'
+#define _GNU_SOURCE 1
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+typedef void (*sync_fn_t)(void);
+
+static void
+write_marker(const char *path)
+{
+ static const char marker[] = "sync-called\n";
+ size_t offset;
+ int fd;
+
+ if (path == NULL || path[0] == '\0')
+ return;
+
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0600);
+ if (fd < 0)
+ return;
+
+ offset = 0;
+ while (offset < sizeof(marker) - 1) {
+ ssize_t written;
+
+ written = write(fd, marker + offset, sizeof(marker) - 1 - offset);
+ if (written < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ offset += (size_t)written;
+ }
+
+ (void)close(fd);
+}
+
+void
+sync(void)
+{
+ static sync_fn_t real_sync;
+ static int resolved;
+
+ if (!resolved) {
+ real_sync = (sync_fn_t)dlsym(RTLD_NEXT, "sync");
+ resolved = 1;
+ }
+
+ write_marker(getenv("SYNC_HOOK_OUTPUT"));
+ if (real_sync != NULL)
+ real_sync();
+}
+EOF
+
+ "$CC" -shared -fPIC -O2 -std=c17 -Wall -Wextra -Werror \
+ "$HOOK_C" -o "$HOOK_SO" -ldl || fail "failed to build sync hook with $CC"
+}
+
+[ -x "$SYNC_BIN" ] || fail "missing binary: $SYNC_BIN"
+build_sync_hook
+
+run_capture "$SYNC_BIN"
+assert_status "sync status" 0 "$LAST_STATUS"
+assert_empty "sync stdout" "$LAST_STDOUT"
+assert_empty "sync stderr" "$LAST_STDERR"
+
+run_capture "$SYNC_BIN" unexpected
+assert_status "operand status" 1 "$LAST_STATUS"
+assert_empty "operand stdout" "$LAST_STDOUT"
+assert_eq "operand stderr" \
+ "sync: unexpected argument: unexpected
+usage: sync" \
+ "$LAST_STDERR"
+
+run_capture "$SYNC_BIN" --
+assert_status "double dash status" 1 "$LAST_STATUS"
+assert_empty "double dash stdout" "$LAST_STDOUT"
+assert_eq "double dash stderr" \
+ "sync: unexpected argument: --
+usage: sync" \
+ "$LAST_STDERR"
+
+run_capture "$SYNC_BIN" first second
+assert_status "too many args status" 1 "$LAST_STATUS"
+assert_empty "too many args stdout" "$LAST_STDOUT"
+assert_eq "too many args stderr" \
+ "sync: unexpected argument: first
+usage: sync" \
+ "$LAST_STDERR"
+
+rm -f "$SYNC_MARKER"
+run_capture env LD_PRELOAD="$HOOK_SO" SYNC_HOOK_OUTPUT="$SYNC_MARKER" "$SYNC_BIN"
+assert_status "hooked sync status" 0 "$LAST_STATUS"
+assert_empty "hooked sync stdout" "$LAST_STDOUT"
+assert_empty "hooked sync stderr" "$LAST_STDERR"
+[ -f "$SYNC_MARKER" ] || fail "sync() was not invoked"
+assert_eq "hook marker" "sync-called" "$(cat "$SYNC_MARKER")"
+
+rm -f "$SYNC_MARKER"
+run_capture env LD_PRELOAD="$HOOK_SO" SYNC_HOOK_OUTPUT="$SYNC_MARKER" \
+ "$SYNC_BIN" unexpected
+assert_status "hooked invalid status" 1 "$LAST_STATUS"
+assert_empty "hooked invalid stdout" "$LAST_STDOUT"
+assert_eq "hooked invalid stderr" \
+ "sync: unexpected argument: unexpected
+usage: sync" \
+ "$LAST_STDERR"
+[ ! -e "$SYNC_MARKER" ] || fail "sync() ran on invalid arguments"
+
+printf '%s\n' "PASS"