#!/usr/bin/env shelly
# All assertions and orchestration run in Shelly. Native children preserve CLI and PTY boundaries.
fn run_case($file: String, $library: String, $suite: String): Boolean
{
let $work: String
mktemp -d /tmp/shelly-suite.XXXXXXXXXX -> $work
$work = $work.trim_end
# Darwin exposes /tmp through /private/tmp. Use the physical spelling for
# fixtures, HOME, cwd, and evidence normalization so their paths agree.
if $os == "macos"
{
let $physical: String
let $result = run_process ["/bin/pwd", "-P"] ["cwd": $work, "timeout_ms": 1000] -> $physical
if ($result["error"] != ()) || ($result["exit_code"] != 0)
{
rm -rf -- $work
return false
}
$work = $physical.trim_end
}
if !(cp -r -- "${suite}/tests/fixtures/." $work)
{
rm -rf -- $work
return false
}
mkdir -- "${work}/driver-home" "${work}/tmp"
ln -s -- `$shelly "${work}/bin/shelly"
let $common: String
let $source: String
$library -> $common
$file -> $source
printf "%s\n%s" $common $source -> "${work}/driver.shy"
let $case_id = ""
if ($file.starts_with "tests/cases/")
{
let $parts = $file.split "/"
$case_id = $parts[$parts.count - 1].replace ".shy" ""
}
let $result = run_process [`$shelly, "-m", "${work}/driver.shy", $work, $suite, $case_id] [
"cwd": $suite,
"env": ["HOME": "${work}/driver-home", "PATH": "${work}/bin:/usr/bin:/bin",
"LANG": "C.UTF-8", "LC_ALL": "C.UTF-8", "TERM": "dumb", "TMPDIR": "${work}/tmp"],
"stdin_file": "/dev/null",
"stdout_file": "${work}/driver.out", "stderr_file": "${work}/driver.err",
"timeout_ms": 30000,
]
let $passed = ($result["exit_code"] == 0) && ($result["signal"] == ())
&& (!$result["timed_out"]) && ($result["error"] == ())
if $passed { echo "PASS" $file }
else
{
echo "FAIL" $file $result
cat "${work}/driver.out" "${work}/driver.err"
}
rm -rf -- $work
$passed
}
fn invalid_inventory($message: String)
{
echo "Invalid inventory: $message"
exit 1
}
fn validate_inventory($files: Array, $suite: String)
{
if !(test -f "${suite}/tests/INVENTORY.tsv")
{
if $files.count != 0 { invalid_inventory "missing inventory" }
return
}
let $text: String
"${suite}/tests/INVENTORY.tsv" -> $text
let $paths = []
let $ids = []
for $line in $text.split "\n"
{
if ($line == "") || ($line == "id\tgroup\tname\tfile") { continue }
let $fields = $line.split "\t"
if $fields.count != 4 { invalid_inventory "malformed row" }
let $id = $fields[0]
let $group = $fields[1]
if ($id == "") || (($group != "positive") && ($group != "negative"))
{ invalid_inventory "invalid ID or group" }
for $character in $id.chars
{
if !("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-".contains $character)
{ invalid_inventory "invalid ID" }
}
if $fields[3] != "tests/cases/${group}/${id}.shy"
{ invalid_inventory "case path does not match ID" }
$paths = [$paths..., $fields[3]]
$ids = [$ids..., $id]
}
let $previous = ""
for $id in $ids.sort
{
if $id == $previous { invalid_inventory "duplicate ID" }
$previous = $id
}
if $paths.sort != $files.sort { invalid_inventory "missing or orphan case file" }
}
let $suite = $pwd
let $filter = if $args.count == 0 { "" } else { $args[0] }
let $passed = 0
let $total = 0
for ($pattern, $library) in [
["process", "tests/support/process.shy"],
["repl", "tests/support/terminal.shy"],
["native", "tests/support/process.shy"],
["harness", "tests/support/process.shy"],
]
{
if (($filter == "process") || ($filter == "repl") || ($filter == "native") || ($filter == "harness")) && ($filter != $pattern)
{ continue }
let $directories = ["tests/${pattern}"]
if $pattern == "process" { $directories = ["tests/cases/positive", "tests/cases/negative"] }
let $files = []
for $directory in $directories
{
if !(test -d $directory) { continue }
let $found: String
find $directory -maxdepth 1 -name "\x2a.shy" -type f -print -quit -> $found
if $found != "" { $files = [$files..., ($directory/*.shy)...] }
}
if ($pattern == "process") && (($filter == "") || ($filter == "process"))
{ validate_inventory $files $suite }
for $file in $files
{
if ($filter == "") || ($filter == $pattern) || ($file.contains $filter)
{
$total = $total + 1
if (run_case $file $library $suite) { $passed = $passed + 1 }
}
}
}
echo $passed "/" $total "tests passed."
if ($total == 0) || ($passed != $total) { exit 1 }