summaryrefslogtreecommitdiff
path: root/corebinutils/pwd/tests
diff options
context:
space:
mode:
authorMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 18:28:16 +0300
committerMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 18:28:16 +0300
commit155dc8e70a7bf008c3cb1a2b7d968373da7aeed8 (patch)
tree30b09216ab9e0a38a559e24f1de3f0ed0db609f0 /corebinutils/pwd/tests
parent681e61b5ab23760db91aea6b30cd38ef3526e308 (diff)
parente19ed3fe0f247b74692c23b07d02ee3779ab5fc9 (diff)
downloadProject-Tick-155dc8e70a7bf008c3cb1a2b7d968373da7aeed8.tar.gz
Project-Tick-155dc8e70a7bf008c3cb1a2b7d968373da7aeed8.zip
Add 'corebinutils/pwd/' from commit 'e19ed3fe0f247b74692c23b07d02ee3779ab5fc9'
git-subtree-dir: corebinutils/pwd git-subtree-mainline: 681e61b5ab23760db91aea6b30cd38ef3526e308 git-subtree-split: e19ed3fe0f247b74692c23b07d02ee3779ab5fc9
Diffstat (limited to 'corebinutils/pwd/tests')
-rw-r--r--corebinutils/pwd/tests/deep_helper.c105
-rw-r--r--corebinutils/pwd/tests/test.sh466
2 files changed, 571 insertions, 0 deletions
diff --git a/corebinutils/pwd/tests/deep_helper.c b/corebinutils/pwd/tests/deep_helper.c
new file mode 100644
index 0000000000..d413cf4750
--- /dev/null
+++ b/corebinutils/pwd/tests/deep_helper.c
@@ -0,0 +1,105 @@
+/*
+ * deep_helper.c – test helper for pwd's deep-path test
+ *
+ * Usage: deep_helper <depth> <segment> <pwd_binary>
+ *
+ * Creates `depth` nested directories each named `segment`, navigates
+ * into the deepest one using chdir(2) (not the shell's cd builtin),
+ * then exec(2)s `pwd_binary` as a child process. The output of
+ * pwd_binary goes to stdout.
+ *
+ * Exit codes:
+ * 0 – pwd_binary exited 0
+ * 1 – setup failure (mkdir/chdir/exec)
+ * N – whatever pwd_binary exited with
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#define _GNU_SOURCE 1
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+int
+main(int argc, char *argv[])
+{
+ long depth;
+ char *end;
+ const char *segment;
+ const char *pwd_bin;
+ int i, status;
+ pid_t child;
+
+ if (argc != 4) {
+ fprintf(stderr, "usage: deep_helper <depth> <segment> <pwd_binary>\n");
+ return 1;
+ }
+
+ errno = 0;
+ depth = strtol(argv[1], &end, 10);
+ if (end == argv[1] || *end != '\0' || errno != 0 || depth < 0) {
+ fprintf(stderr, "deep_helper: invalid depth: %s\n", argv[1]);
+ return 1;
+ }
+ segment = argv[2];
+ pwd_bin = argv[3];
+
+ for (i = 0; i < (int)depth; i++) {
+ if (mkdir(segment, 0755) != 0 && errno != EEXIST) {
+ fprintf(stderr, "deep_helper: mkdir(%s) at depth %d: %s\n",
+ segment, i, strerror(errno));
+ return 1;
+ }
+ if (chdir(segment) != 0) {
+ /*
+ * chdir(2) with a single short component should not fail
+ * ENAMETOOLONG on Linux even when the cumulative path
+ * exceeds PATH_MAX. If it does, report it clearly.
+ */
+ fprintf(stderr, "deep_helper: chdir(%s) at depth %d: %s\n",
+ segment, i, strerror(errno));
+ return 1;
+ }
+ }
+
+ /* Unset PWD so pwd(1) falls back to getcwd(NULL, 0). */
+ if (unsetenv("PWD") != 0) {
+ fprintf(stderr, "deep_helper: unsetenv(PWD): %s\n",
+ strerror(errno));
+ return 1;
+ }
+
+ /* Fork so we can capture the exit status of pwd_bin. */
+ child = fork();
+ if (child == -1) {
+ fprintf(stderr, "deep_helper: fork: %s\n", strerror(errno));
+ return 1;
+ }
+ if (child == 0) {
+ /* Child: exec pwd_bin with no arguments (default -L) */
+ char *args[] = { (char *)pwd_bin, "-P", NULL };
+ execv(pwd_bin, args);
+ fprintf(stderr, "deep_helper: execv(%s): %s\n",
+ pwd_bin, strerror(errno));
+ _exit(1);
+ }
+
+ /* Parent: wait for child. */
+ if (waitpid(child, &status, 0) == -1) {
+ fprintf(stderr, "deep_helper: waitpid: %s\n", strerror(errno));
+ return 1;
+ }
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+
+ fprintf(stderr, "deep_helper: pwd_bin killed by signal %d\n",
+ WTERMSIG(status));
+ return 1;
+}
diff --git a/corebinutils/pwd/tests/test.sh b/corebinutils/pwd/tests/test.sh
new file mode 100644
index 0000000000..28114c1339
--- /dev/null
+++ b/corebinutils/pwd/tests/test.sh
@@ -0,0 +1,466 @@
+#!/bin/sh
+#
+# tests/test.sh — pwd(1) Linux port test suite
+#
+# Environment:
+# PWD_BIN path to the pwd binary under test
+# LC_ALL must be C (set below)
+#
+# Contract per test:
+# Every test asserts exit status + stdout + stderr.
+# "It ran" alone is never sufficient.
+#
+# Robustness:
+# - Works inside containers and PID namespaces
+# - No assumption about the runner's $PWD value
+# - TZ and LC_ALL forced to avoid locale/timezone noise
+# - TMPDIR-based isolation; cleaned up on exit
+
+set -eu
+
+export LC_ALL=C
+export TZ=UTC
+
+ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd -P)
+PWD_BIN="${PWD_BIN:-$ROOT/out/pwd}"
+
+TMPDIR="${TMPDIR:-/tmp}"
+WORKDIR=$(mktemp -d "$TMPDIR/pwd-test.XXXXXX")
+STDOUT_FILE="$WORKDIR/stdout"
+STDERR_FILE="$WORKDIR/stderr"
+trap 'rm -rf "$WORKDIR"' EXIT INT TERM
+
+# ---------------------------------------------------------------------------
+# Helpers
+# ---------------------------------------------------------------------------
+
+fail() {
+ printf 'FAIL: %s\n' "$1" >&2
+ exit 1
+}
+
+pass() {
+ printf 'PASS: %s\n' "$1"
+}
+
+# run_capture cmd [args...]
+# Captures stdout → STDOUT_FILE, stderr → STDERR_FILE, status → LAST_STATUS.
+# Never aborts due to nonzero exit before capture.
+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")
+}
+
+assert_status() {
+ name=$1
+ expected=$2
+ actual=$3
+ if [ "$expected" -ne "$actual" ]; then
+ printf 'FAIL: %s\n' "$name" >&2
+ printf ' expected status: %d\n' "$expected" >&2
+ printf ' actual status: %d\n' "$actual" >&2
+ exit 1
+ fi
+}
+
+assert_eq() {
+ name=$1
+ expected=$2
+ actual=$3
+ if [ "$expected" != "$actual" ]; then
+ printf 'FAIL: %s\n' "$name" >&2
+ printf ' expected: %s\n' "$expected" >&2
+ printf ' actual: %s\n' "$actual" >&2
+ exit 1
+ fi
+}
+
+assert_empty() {
+ name=$1
+ text=$2
+ if [ -n "$text" ]; then
+ printf 'FAIL: %s (expected empty, got: %s)\n' "$name" "$text" >&2
+ exit 1
+ fi
+}
+
+assert_contains() {
+ name=$1
+ text=$2
+ pattern=$3
+ case $text in
+ *"$pattern"*) ;;
+ *) fail "$name: expected '$pattern' in '$text'" ;;
+ esac
+}
+
+# ---------------------------------------------------------------------------
+# Prerequisite
+# ---------------------------------------------------------------------------
+
+[ -x "$PWD_BIN" ] || fail "missing or non-executable binary: $PWD_BIN"
+
+# ---------------------------------------------------------------------------
+# 1. Basic invocation — no flags
+# ---------------------------------------------------------------------------
+
+(
+ cd "$WORKDIR"
+ run_capture "$PWD_BIN"
+ assert_status "basic no-flag status" 0 "$LAST_STATUS"
+ assert_eq "basic no-flag stdout" "$WORKDIR" "$LAST_STDOUT"
+ assert_empty "basic no-flag stderr" "$LAST_STDERR"
+)
+pass "basic no-flag"
+
+# ---------------------------------------------------------------------------
+# 2. Explicit -L (logical, $PWD matches physical — simplest case)
+# ---------------------------------------------------------------------------
+
+(
+ cd "$WORKDIR"
+ run_capture "$PWD_BIN" -L
+ assert_status "explicit -L status" 0 "$LAST_STATUS"
+ assert_eq "explicit -L stdout" "$WORKDIR" "$LAST_STDOUT"
+ assert_empty "explicit -L stderr" "$LAST_STDERR"
+)
+pass "explicit -L"
+
+# ---------------------------------------------------------------------------
+# 3. Explicit -P (physical, no symlinks → same as logical in WORKDIR)
+# ---------------------------------------------------------------------------
+
+(
+ cd "$WORKDIR"
+ run_capture "$PWD_BIN" -P
+ assert_status "explicit -P status" 0 "$LAST_STATUS"
+ assert_eq "explicit -P stdout" "$WORKDIR" "$LAST_STDOUT"
+ assert_empty "explicit -P stderr" "$LAST_STDERR"
+)
+pass "explicit -P"
+
+# ---------------------------------------------------------------------------
+# 4. Symlink: -L shows logical path, -P shows physical path
+# ---------------------------------------------------------------------------
+
+PHYBASE="$WORKDIR/phy"
+LOGBASE="$WORKDIR/log"
+PHYDIR="$PHYBASE/sub"
+mkdir -p "$PHYDIR"
+ln -s "$PHYBASE" "$LOGBASE"
+
+(
+ cd "$LOGBASE/sub"
+ export PWD="$LOGBASE/sub"
+ run_capture "$PWD_BIN" -L
+ assert_status "symlink -L status" 0 "$LAST_STATUS"
+ assert_eq "symlink -L stdout" "$LOGBASE/sub" "$LAST_STDOUT"
+ assert_empty "symlink -L stderr" "$LAST_STDERR"
+)
+pass "symlink -L"
+
+(
+ cd "$LOGBASE/sub"
+ export PWD="$LOGBASE/sub"
+ run_capture "$PWD_BIN" -P
+ assert_status "symlink -P status" 0 "$LAST_STATUS"
+ assert_eq "symlink -P stdout" "$PHYDIR" "$LAST_STDOUT"
+ assert_empty "symlink -P stderr" "$LAST_STDERR"
+)
+pass "symlink -P"
+
+# ---------------------------------------------------------------------------
+# 5. Last flag wins: -L -P should behave as -P
+# ---------------------------------------------------------------------------
+
+(
+ cd "$LOGBASE/sub"
+ export PWD="$LOGBASE/sub"
+ run_capture "$PWD_BIN" -L -P
+ assert_status "last-flag -L -P status" 0 "$LAST_STATUS"
+ assert_eq "last-flag -L -P stdout" "$PHYDIR" "$LAST_STDOUT"
+ assert_empty "last-flag -L -P stderr" "$LAST_STDERR"
+)
+pass "last-flag -L -P is physical"
+
+(
+ cd "$LOGBASE/sub"
+ export PWD="$LOGBASE/sub"
+ run_capture "$PWD_BIN" -P -L
+ assert_status "last-flag -P -L status" 0 "$LAST_STATUS"
+ assert_eq "last-flag -P -L stdout" "$LOGBASE/sub" "$LAST_STDOUT"
+ assert_empty "last-flag -P -L stderr" "$LAST_STDERR"
+)
+pass "last-flag -P -L is logical"
+
+# ---------------------------------------------------------------------------
+# 6. Relative $PWD → logical fallback to physical
+# ---------------------------------------------------------------------------
+
+(
+ cd "$LOGBASE/sub"
+ export PWD="log/sub" # relative — must be rejected
+ run_capture "$PWD_BIN" -L
+ assert_status "relative PWD -L status" 0 "$LAST_STATUS"
+ # Must fall back to physical
+ assert_eq "relative PWD -L stdout" "$PHYDIR" "$LAST_STDOUT"
+ assert_empty "relative PWD -L stderr" "$LAST_STDERR"
+)
+pass "relative PWD fallback to physical"
+
+# ---------------------------------------------------------------------------
+# 7. $PWD contains /./ → must reject it (POSIX 4.13)
+# ---------------------------------------------------------------------------
+
+(
+ cd "$PHYDIR"
+ export PWD="$PHYBASE/./sub"
+ run_capture "$PWD_BIN" -L
+ assert_status "dot-component PWD -L status" 0 "$LAST_STATUS"
+ assert_eq "dot-component PWD -L stdout" "$PHYDIR" "$LAST_STDOUT"
+ assert_empty "dot-component PWD -L stderr" "$LAST_STDERR"
+)
+pass "PWD with /./ rejected, fallback to physical"
+
+# ---------------------------------------------------------------------------
+# 8. $PWD contains /../ → must reject it
+# ---------------------------------------------------------------------------
+
+(
+ cd "$PHYDIR"
+ export PWD="$PHYBASE/../phy/sub"
+ run_capture "$PWD_BIN" -L
+ assert_status "dotdot-component PWD -L status" 0 "$LAST_STATUS"
+ assert_eq "dotdot-component PWD -L stdout" "$PHYDIR" "$LAST_STDOUT"
+ assert_empty "dotdot-component PWD -L stderr" "$LAST_STDERR"
+)
+pass "PWD with /../ rejected, fallback to physical"
+
+# ---------------------------------------------------------------------------
+# 9. $PWD points to wrong inode → fallback to physical
+# ---------------------------------------------------------------------------
+
+(
+ cd "$PHYDIR"
+ export PWD="$PHYBASE" # $PWD is a parent dir, not the same inode
+ run_capture "$PWD_BIN" -L
+ assert_status "wrong-inode PWD -L status" 0 "$LAST_STATUS"
+ assert_eq "wrong-inode PWD -L stdout" "$PHYDIR" "$LAST_STDOUT"
+ assert_empty "wrong-inode PWD -L stderr" "$LAST_STDERR"
+)
+pass "PWD inode mismatch fallback to physical"
+
+# ---------------------------------------------------------------------------
+# 10. $PWD does not exist → fallback to physical
+# ---------------------------------------------------------------------------
+
+(
+ cd "$PHYDIR"
+ export PWD="$WORKDIR/does-not-exist"
+ run_capture "$PWD_BIN" -L
+ assert_status "nonexistent PWD -L status" 0 "$LAST_STATUS"
+ assert_eq "nonexistent PWD -L stdout" "$PHYDIR" "$LAST_STDOUT"
+ assert_empty "nonexistent PWD -L stderr" "$LAST_STDERR"
+)
+pass "nonexistent PWD fallback to physical"
+
+# ---------------------------------------------------------------------------
+# 11. Unset $PWD → logical falls back to physical
+# ---------------------------------------------------------------------------
+
+(
+ cd "$PHYDIR"
+ unset PWD
+ run_capture "$PWD_BIN" -L
+ assert_status "unset PWD -L status" 0 "$LAST_STATUS"
+ assert_eq "unset PWD -L stdout" "$PHYDIR" "$LAST_STDOUT"
+ assert_empty "unset PWD -L stderr" "$LAST_STDERR"
+)
+pass "unset PWD fallback to physical"
+
+# ---------------------------------------------------------------------------
+# 12. Bad flag → usage written to stderr, exit 1
+# ---------------------------------------------------------------------------
+
+(
+ cd "$WORKDIR"
+ run_capture "$PWD_BIN" -x
+ assert_status "bad-flag status" 1 "$LAST_STATUS"
+ assert_empty "bad-flag stdout" "$LAST_STDOUT"
+ assert_contains "bad-flag stderr has usage" "$LAST_STDERR" "usage:"
+ assert_contains "bad-flag stderr has -L" "$LAST_STDERR" "-L"
+ assert_contains "bad-flag stderr has -P" "$LAST_STDERR" "-P"
+)
+pass "bad flag exits 1 with usage on stderr"
+
+# ---------------------------------------------------------------------------
+# 13. Extra operand → usage on stderr, exit 1
+# ---------------------------------------------------------------------------
+
+(
+ cd "$WORKDIR"
+ run_capture "$PWD_BIN" extra-arg
+ assert_status "extra-arg status" 1 "$LAST_STATUS"
+ assert_empty "extra-arg stdout" "$LAST_STDOUT"
+ assert_contains "extra-arg stderr has usage" "$LAST_STDERR" "usage:"
+)
+pass "extra operand exits 1 with usage"
+
+# ---------------------------------------------------------------------------
+# 14. stdout failure → exit non-zero, error on stderr
+# ---------------------------------------------------------------------------
+# Pipe into a program that exits immediately, causing SIGPIPE / EPIPE.
+# We explicitly ignore PIPE so the subshell stays alive long enough to
+# capture the exit status from $pwd.
+#
+# This test is done in a subshell; the outer set -e is irrelevant inside it.
+
+(
+ RESULT_FILE="$WORKDIR/pipe-result"
+ ERR_FILE="$WORKDIR/pipe-err"
+ # We run a pipeline in a subshell that traps PIPE so pwd can finish writing.
+ # - The left side: sleep 0 && "$PWD_BIN" (so shell sets up pipe before exec)
+ # - The right side: true immediately exits → EPIPE on next write
+ sh -c '
+ trap "" PIPE
+ sleep 0
+ "$1" 2>"$2"
+ echo $? >"$3"
+ ' -- "$PWD_BIN" "$ERR_FILE" "$RESULT_FILE" | true || true
+
+ # Give the subshell a moment to finish writing result file
+ # (In practice this is instantaneous on any real system)
+ i=0
+ while [ $i -lt 20 ] && [ ! -s "$RESULT_FILE" ]; do
+ sleep 0.1
+ i=$((i + 1))
+ done
+
+ if [ ! -s "$RESULT_FILE" ]; then
+ # PIPE delivery timing is platform-dependent; skip rather than
+ # produce a false negative in constrained environments.
+ printf 'SKIP: stdout-failure test (PIPE timing uncertainty)\n' >&2
+ else
+ result=$(cat "$RESULT_FILE")
+ err=$(cat "$ERR_FILE" 2>/dev/null || true)
+ if [ "$result" -eq 0 ]; then
+ # Some musl/kernel combinations silently succeed when output
+ # is consumed before close. Accept skip here as well.
+ printf 'SKIP: stdout-failure test (PIPE not delivered)\n' >&2
+ else
+ assert_contains "stdout-failure stderr" "$err" "stdout"
+ pass "stdout failure exits non-zero with stderr message"
+ fi
+ fi
+)
+
+# ---------------------------------------------------------------------------
+# 15. Deep path — no static PATH_MAX limit
+# ---------------------------------------------------------------------------
+#
+# Verify that pwd -P handles deep directory trees correctly via the dynamic
+# getcwd(NULL, 0) interface rather than a fixed-size stack buffer.
+#
+# We use a C helper (deep_helper) that calls chdir(2) directly, bypassing
+# the bash/dash shell builtin cd which enforces PATH_MAX even for relative
+# single-component changes. Only the C layer's getcwd(3) call inside pwd
+# is asked to assemble the full path.
+#
+# The helper creates 90 levels × 50-char segment = 4500+ bytes of path
+# (plus the WORKDIR prefix), then exec(2)s "$PWD_BIN" -P from inside.
+
+DEEP_HELPER_BIN="${DEEP_HELPER_BIN:-}"
+SEGMENT="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" # 50 chars
+
+if [ -z "$DEEP_HELPER_BIN" ] || [ ! -x "$DEEP_HELPER_BIN" ]; then
+ printf 'SKIP: deep-path test (DEEP_HELPER_BIN not set or not executable)\n' >&2
+else
+ DEEPBASE="$WORKDIR/deep"
+ mkdir -p "$DEEPBASE"
+ # run_capture is called inside a subshell (cd changes dir), so
+ # LAST_* variables won't propagate. Instead we rely on $STDOUT_FILE
+ # and $STDERR_FILE (persistent files in $WORKDIR) and the subshell
+ # exit status to carry the result back.
+ set +e
+ (cd "$DEEPBASE" && "$DEEP_HELPER_BIN" 70 "$SEGMENT" "$PWD_BIN" \
+ >"$STDOUT_FILE" 2>"$STDERR_FILE")
+ deep_exit=$?
+ set -e
+ deep_stdout=$(cat "$STDOUT_FILE")
+ deep_stderr=$(cat "$STDERR_FILE")
+
+ if [ "$deep_exit" -ne 0 ]; then
+ fail "deep-path: helper/pwd exited $deep_exit; stderr: $deep_stderr"
+ fi
+
+ case "$deep_stdout" in
+ /*)
+ ;;
+ *)
+ fail "deep-path: output is not absolute: $deep_stdout"
+ ;;
+ esac
+
+ deeplength=$(printf '%s' "$deep_stdout" | wc -c)
+ if [ "$deeplength" -lt 3500 ]; then
+ fail "deep-path: path not long enough to test getcwd(NULL,0) ($deeplength < 3500)"
+ fi
+
+ assert_empty "deep-path stderr" "$deep_stderr"
+ pass "deep path (${deeplength} bytes) with -P via getcwd(NULL,0)"
+fi
+
+# ---------------------------------------------------------------------------
+# 16. Path output ends with newline (no trailing garbage)
+# ---------------------------------------------------------------------------
+
+(
+ cd "$WORKDIR"
+ run_capture "$PWD_BIN"
+ # Ensure the captured stdout is exactly WORKDIR (shell strips trailing newlines
+ # from $(...), but our assert_eq checks LAST_STDOUT which is set via $(...),
+ # so both sides will have trailing newlines stripped identically).
+ assert_eq "newline-terminated stdout" "$WORKDIR" "$LAST_STDOUT"
+ assert_empty "newline-terminated stderr" "$LAST_STDERR"
+)
+pass "output is newline-terminated"
+
+# ---------------------------------------------------------------------------
+# 17. Symlink traversal depth: $PWD via multiple layers
+# ---------------------------------------------------------------------------
+
+LAYERED1="$WORKDIR/layer1"
+LAYERED2="$WORKDIR/layer2"
+LAYERED_PHY="$WORKDIR/layerphy/deep"
+mkdir -p "$LAYERED_PHY"
+ln -s "$LAYERED_PHY" "$LAYERED1"
+ln -s "$LAYERED1" "$LAYERED2" # layer2 → layer1 → layerphy/deep
+
+(
+ cd "$LAYERED2"
+ export PWD="$LAYERED2"
+ run_capture "$PWD_BIN" -L
+ assert_status "multilayer -L status" 0 "$LAST_STATUS"
+ assert_eq "multilayer -L stdout" "$LAYERED2" "$LAST_STDOUT"
+ assert_empty "multilayer -L stderr" "$LAST_STDERR"
+)
+pass "multilayer symlink -L"
+
+(
+ cd "$LAYERED2"
+ export PWD="$LAYERED2"
+ run_capture "$PWD_BIN" -P
+ assert_status "multilayer -P status" 0 "$LAST_STATUS"
+ assert_eq "multilayer -P stdout" "$LAYERED_PHY" "$LAST_STDOUT"
+ assert_empty "multilayer -P stderr" "$LAST_STDERR"
+)
+pass "multilayer symlink -P"
+
+# ---------------------------------------------------------------------------
+printf '\nALL TESTS PASSED.\n'
+exit 0