RustForge

foundry

Foundry OS: the operating system built on the Rustbelt kernel

foundry / root / scripts / sbase-test.sh
10746 bytes ยท raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# sbase-test.sh: checks suckless sbase (milestone M4), run by dash.
# The self-test runs it as `dash /scripts/sbase-test.sh`. sbase lives in
# /usr/lib/sbase, OFF the default PATH, so every tool is called by its full
# path ($S/sort, ...); the Foundry OS Rust tools stay the default.
#
# Each check prints one line:
#   sbase: ok NAME            it worked
#   sbase: FAIL NAME          it did not (the test fails)
#   sbase: xfail NAME (WHY)   a known gap, failing as expected; WHY names the
#                             kernel request in docs/kernel-requests.md
#   sbase: XPASS NAME (WHY)   a known gap that suddenly works: fixed? Move it
#                             to the plain checks (counts as a failure)
#   sbase: absent NAME        a tool that sbase 0.1 doesn't have at all
# The exit status is the number of failures (FAIL + XPASS).
#
# To try it on a Linux host against a host-built sbase:
#   SBASE=/path/to/sbase WORK=/tmp/sbase-work dash root/scripts/sbase-test.sh
# (there `uname`, `du` block sizes and the known gaps differ, of course).
S=${SBASE:-/usr/lib/sbase}
W=${WORK:-/sbase-work}
fails=0 passed=0 gaps=0 absent=0
check() { # check NAME COMMAND...  (passes when COMMAND succeeds)
    name=$1; shift
    if "$@"; then echo "sbase: ok $name"; passed=$((passed + 1))
    else echo "sbase: FAIL $name"; fails=$((fails + 1)); fi
}
xfail() { # xfail NAME WHY COMMAND...  (a known gap: COMMAND should fail)
    name=$1 why=$2; shift 2
    if "$@" > /dev/null 2>&1; then echo "sbase: XPASS $name ($why)"; fails=$((fails + 1))
    else echo "sbase: xfail $name ($why)"; gaps=$((gaps + 1)); fi
}
notin() { # notin NAME WHERE: a tool sbase doesn't have
    if [ -e "$S/$1" ]; then echo "sbase: FAIL $1 is in sbase now: test it"; fails=$((fails + 1))
    else echo "sbase: absent $1 (not part of sbase 0.1; $2)"; absent=$((absent + 1)); fi
}
eq() { [ "$1" = "$2" ] || { echo "  got '$1', want '$2'"; return 1; }; }
# j: join standard input's lines with single spaces (plain sh, no tools).
j() { r=; while IFS= read -r l; do r="$r${r:+ }$l"; done; printf '%s' "$r"; }
# lsl FILE: `ls -l FILE` split into words: $1 mode, $2 links, $3 owner,
# $4 group, $5 size, $6-$8 date. Result in $L (e.g. "$L" = "-rw-r--r-- 1 ...").
lsl() { L=$($S/ls -l "$1") || return 1; }

$S/rm -rf "$W" && $S/mkdir -p "$W" && cd "$W" || { echo "sbase: FAIL cannot use $W"; exit 1; }
printf '%s\n' 1 2 3 > nums                       # 6 bytes
printf 'hello\n' > hello                          # 6 bytes

# ---- text ----
check sort        eq "$(printf 'b\na\nc\nb\n' | $S/sort | j)" "a b b c"
check sort-n      eq "$(printf '10\n9\n100\n' | $S/sort -n | j)" "9 10 100"
check sort-u-r    eq "$(printf 'b\na\nc\nb\n' | $S/sort -u -r | j)" "c b a"
check sort-k      eq "$($S/sort -t: -k3 -n /etc/passwd | $S/cut -d: -f1 | j)" "root tyler"
check uniq-c      eq "$(printf 'a\na\nb\n' | $S/uniq -c | $S/sed 's/^ *//' | j)" "2 a 1 b"
check uniq-d      eq "$(printf 'a\na\nb\n' | $S/uniq -d)" "a"
check head        eq "$($S/seq 10 | $S/head -n 3 | j)" "1 2 3"
check tail        eq "$($S/seq 10 | $S/tail -n 2 | j)" "9 10"
check tail-plus   eq "$($S/seq 10 | $S/tail -n +9 | j)" "9 10"
check cut-f       eq "$($S/cut -d: -f1 /etc/passwd | j)" "root tyler"
check cut-c       eq "$(echo hello | $S/cut -c2-4)" "ell"
check tr          eq "$(echo hello | $S/tr a-z A-Z)" "HELLO"
check tr-d-s      eq "$(echo hello | $S/tr -d e)/$(echo hello | $S/tr -s l)" "hllo/helo"
check sed-s       eq "$(echo a:b:c | $S/sed 's/:/-/g')" "a-b-c"
check sed-d       eq "$($S/seq 3 | $S/sed 2d | j)" "1 3"
check sed-n-p     eq "$($S/sed -n '/^t/p' /etc/passwd | $S/cut -d: -f1)" "tyler"
check sed-y       eq "$(echo abc | $S/sed 'y/abc/xyz/')" "xyz"
check grep        eq "$($S/grep -c '^[rt]' /etc/passwd)/$($S/grep -v root /etc/passwd | $S/cut -d: -f1)" "2/tyler"
check wc          eq "$(set -- $(printf 'a b\nc d e\n' | $S/wc); printf '%s' "$*")" "2 5 10"
check od          eq "$(set -- $(printf 'AB\n' | $S/od -t c); printf '%s' "$*")" '0000000 A B \n 0000003'
check printf      eq "$($S/printf '%05d|%s|%x|%c|%5.2f' 42 hi 255 A 3.14159)" "00042|hi|ff|A| 3.14"
check expr        eq "$($S/expr 6 \* 7)/$($S/expr abc : 'a\(.\)')/$($S/expr 5 \> 3)" "42/b/1"
check seq         eq "$($S/seq 2 2 8 | j)/$($S/seq -s, 1 3)/$($S/seq -w 8 10 | j)" "2 4 6 8/1,2,3/08 09 10"
check basename    eq "$($S/basename /a/b/c.txt .txt)" "c"
check dirname     eq "$($S/dirname /a/b/c.txt)" "/a/b"
check paste-comm  eq "$($S/paste -s -d, nums)/$(printf 'a\nb\n' > c1; printf 'b\nc\n' > c2; $S/comm -12 c1 c2)" "1,2,3/b"
check rev-fold-nl eq "$(echo abc | $S/rev)/$(echo abcdef | $S/fold -w 2 | j)/$($S/nl -b a nums | $S/sed -n 1p | $S/tr -d ' \t')" "cba/ab cd ef/11"
check cat         eq "$($S/cat nums hello | j)" "1 2 3 hello"

# ---- checksums ----
check cksum       eq "$($S/cksum < hello)" "3015617425 6"
check sha256sum   eq "$($S/sha256sum hello)" "5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03  hello"
check sha256sum-c eval '$S/sha256sum hello > sums && $S/sha256sum -c sums > /dev/null'
check md5sum      eq "$($S/md5sum hello)" "b1946ac92492d2347c6235b4d2611184  hello"
check sha1sum     eq "$($S/sha1sum hello)" "f572d396fae9206628714fb2ce00f72e94f2258f  hello"

# ---- files, modes, links, times (A:, ext2) ----
check ls-l        eval 'lsl nums && set -- $L && eq "$1 $2 $3 $4 $5" "-rw-r--r-- 1 root root 6"'
check ls-l-owner  eval 'lsl /etc/passwd && set -- $L && eq "$3:$4" "root:root"'
check ls-ld       eval 'set -- $($S/ls -ld /etc) && eq "$1" drwxr-xr-x'
check chmod       eval '$S/chmod 640 nums && lsl nums && set -- $L && eq "$1" -rw-r-----'
check chmod-sym   eval '$S/chmod u+x,g-r nums && lsl nums && set -- $L && eq "$1" -rwx------ && $S/chmod 644 nums'
check ln-s        eval '$S/ln -s nums sym && eq "$($S/readlink sym)" nums && eq "$($S/cat sym | j)" "1 2 3" && lsl sym && case $L in l*) ;; *) false;; esac'
check ln-hard     eval '$S/ln nums hard && lsl nums && set -- $L && eq "$2" 2 && eq "$($S/ls -i nums | $S/cut -d" " -f1)" "$($S/ls -i hard | $S/cut -d" " -f1)"'
check touch-new   eval '$S/touch empty && [ -e empty ] && [ ! -s empty ]'
check touch-t     eval '$S/touch -t 200001020304 nums && lsl nums && set -- $L && eq "$6 $7 $8" "Jan 02 2000"'
check touch-d     eval '$S/touch -d 2001-02-03T04:05:06 hello && lsl hello && set -- $L && eq "$6 $7 $8" "Feb 03 2001"'
check cp-p        eval '$S/chmod 600 nums && $S/cp -p nums keep && lsl keep && set -- $L && eq "$1 $6 $7 $8" "-rw------- Jan 02 2000"'
check cp-plain    eval '$S/cp nums fresh && lsl fresh && set -- $L && [ "$8" != 2000 ]'
check cp-R-mv-rm  eval '$S/mkdir -p d/e && $S/cp nums d/e/ && $S/cp -R d d2 && $S/mv d2 d3 && [ -f d3/e/nums ] && $S/rm -r d d3 && [ ! -e d3 ]'
check rmdir       eval '$S/mkdir -p r/s && ! $S/rmdir r 2>/dev/null && $S/rmdir r/s r && [ ! -e r ]'
check dd          eval '$S/dd if=/dev/zero of=zeros bs=512 count=6 2> dd.err && eq "$($S/wc -c < zeros)" 3072 && $S/grep -q "6+0 records in" dd.err'
check dd-skip     eq "$($S/dd if=/etc/passwd bs=1 skip=5 count=4 2>/dev/null)" "x:0:"
check du          eval 'set -- $($S/du -k zeros) && eq "$1" 3'
check find        eval '$S/mkdir -p tree/a/b && $S/touch tree/x.txt tree/a/y.txt && eq "$($S/find tree -name "*.txt" | $S/sort | j)" "tree/a/y.txt tree/x.txt"'
check find-type   eq "$($S/find tree -type d | $S/sort | j)" "tree tree/a tree/a/b"
check find-newer  eval '$S/touch -t 200001010000 tree/x.txt && eq "$($S/find tree -name "*.txt" -newer tree/x.txt)" tree/a/y.txt'
check xargs       eq "$(printf 'a b\nc\n' | $S/xargs $S/echo)" "a b c"
check xargs-n     eq "$(printf 'a b c\n' | $S/xargs -n 1 $S/echo | $S/wc -l)" "3"
check tee         eval 'echo hi | $S/tee t1 > t2 && echo ho | $S/tee -a t1 > /dev/null && eq "$($S/cat t1 | j)/$($S/cat t2)" "hi ho/hi"'
check test        eval '$S/test -d / && $S/test -f /etc/passwd && ! $S/test -e /nope && $S/test 3 -lt 5 && $S/test -L sym && $S/test -s nums && ! $S/test -s empty && $S/test abc = abc'
check tar         eval '$S/tar -cf t.tar tree && $S/rm -r tree && $S/tar -xf t.tar && [ -f tree/a/y.txt ] && eq "$($S/tar -tf t.tar | $S/grep -c txt)" 2'
check cmp-split   eval '$S/cmp -s nums keep && ! $S/cmp -s nums hello && $S/split -l 1 nums part. && eq "$($S/cat part.ab)" 2'
check readlink-f  eq "$($S/readlink -f sym)" "$W/nums"
check mktemp      eval 'f=$($S/mktemp "$W/tmp.XXXXXX") && [ -f "$f" ] && d=$($S/mktemp -d "$W/dir.XXXXXX") && [ -d "$d" ]'

# ---- environment, processes, system ----
check env-i       eq "$($S/env -i FOO=bar $S/env)" "FOO=bar"
check env-u       eval '! $S/env -u PATH $S/printenv PATH > /dev/null'
check date        eq "$($S/date -u -d 0 '+%Y-%m-%d %H:%M:%S')" "1970-01-01 00:00:00"
check date-now    eval '[ "$($S/date +%Y)" -ge 2025 ]'
check yes-head    eq "$($S/yes | $S/head -n 3 | j)/$($S/yes ab | $S/head -n 2 | j)" "y y y/ab ab"
check sleep       eval 'a=$($S/date +%s); $S/sleep 1; b=$($S/date +%s); [ $((b - a)) -ge 1 ] && [ $((b - a)) -le 3 ]'
check kill-TERM   eval '$S/sleep 30 & p=$!; $S/kill $p; wait $p; eq $? 143'
check kill-9      eval '$S/sleep 30 & p=$!; $S/kill -9 $p; wait $p; eq $? 137'
check kill-l      eq "$($S/kill -l 15)" "TERM"
check nice        eq "$($S/nice -n 5 $S/echo niced)" "niced"
check renice      eval '$S/renice -n 2 -p $$ > /dev/null'
check uname       eq "$($S/uname -s)/$($S/uname -m)" "Rustbelt/x86_64"
echo "sbase: uname -sr: $($S/uname -sr)"
check whoami      eq "$($S/whoami)" "root"
check hostname    eval '[ -n "$($S/hostname)" ]'
check tail-f      eval '$S/tail -f nums > tailed & p=$!; $S/sleep 1; $S/kill $p; wait $p; eq "$? $($S/cat tailed | j)" "143 1 2 3"'
check true-false  eval '$S/true && ! $S/false'
check getconf     eq "$($S/getconf PAGESIZE)" "4096"

# ---- known gaps: the kernel (docs/kernel-requests.md K14) ----
xfail mkfifo "K14: mknodat 259 is ENOSYS" $S/mkfifo fifo
xfail mknod-p "K14: mknodat 259 is ENOSYS" $S/mknod fifo2 p
xfail chroot "K14: chroot 161 is ENOSYS" $S/chroot / $S/true
xfail ls-a-dots "K14: getdents64 has no . and .. entries" eval '$S/ls -a "$W" | $S/grep -qx "\.\."'
xfail touch-on-FAT "K14: FAT32 and exFAT keep no file times" eval '$S/touch -t 200001020304 /mnt/disk0/sbase-t && lsl /mnt/disk0/sbase-t && set -- $L && $S/rm -f /mnt/disk0/sbase-t && eq "$8" 2000'
$S/rm -f /mnt/disk0/sbase-t
# ---- tools sbase 0.1 doesn't have (its sister project ubase does) ----
notin df "ubase; busybox at M5"
notin id "ubase; busybox at M5"

cd / && $S/rm -r "$W"
check cleanup [ ! -e "$W" ]
if [ $fails = 0 ]; then
    echo "sbase-test: all $((passed + gaps + absent)) checks as expected ($passed passed, $gaps known gaps, $absent tools not in sbase)"
else
    echo "sbase-test: $fails unexpected result(s) ($passed passed, $gaps known gaps)"
fi
exit $fails