summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--etc/defaults/shell/test/TARGETS1
-rw-r--r--rules/shell/test/EXPRESSIONS116
-rw-r--r--rules/shell/test/RULES50
-rwxr-xr-xrules/shell/test/test_runner.sh44
4 files changed, 211 insertions, 0 deletions
diff --git a/etc/defaults/shell/test/TARGETS b/etc/defaults/shell/test/TARGETS
new file mode 100644
index 00000000..0967ef42
--- /dev/null
+++ b/etc/defaults/shell/test/TARGETS
@@ -0,0 +1 @@
+{}
diff --git a/rules/shell/test/EXPRESSIONS b/rules/shell/test/EXPRESSIONS
new file mode 100644
index 00000000..429cd475
--- /dev/null
+++ b/rules/shell/test/EXPRESSIONS
@@ -0,0 +1,116 @@
+{ "test-result":
+ { "vars": ["name", "test.sh"]
+ , "expression":
+ { "type": "let*"
+ , "bindings":
+ [ [ "runner"
+ , { "type": "map_union"
+ , "$1":
+ { "type": "foreach"
+ , "var": "runner"
+ , "range": {"type": "FIELD", "name": "runner"}
+ , "body":
+ { "type": "map_union"
+ , "$1":
+ { "type": "foreach"
+ , "var": "runner"
+ , "range":
+ { "type": "values"
+ , "$1":
+ { "type": "DEP_ARTIFACTS"
+ , "dep": {"type": "var", "name": "runner"}
+ }
+ }
+ , "body":
+ { "type": "singleton_map"
+ , "key": "runner"
+ , "value": {"type": "var", "name": "runner"}
+ }
+ }
+ }
+ }
+ }
+ ]
+ , [ "deps"
+ , { "type": "TREE"
+ , "$1":
+ { "type": "disjoint_map_union"
+ , "msg": "Field 'deps' has to stage in a conflict free way"
+ , "$1":
+ { "type": "++"
+ , "$1":
+ [ { "type": "foreach"
+ , "var": "dep"
+ , "range": {"type": "FIELD", "name": "deps"}
+ , "body":
+ { "type": "DEP_RUNFILES"
+ , "dep": {"type": "var", "name": "dep"}
+ }
+ }
+ , { "type": "foreach"
+ , "var": "dep"
+ , "range": {"type": "FIELD", "name": "deps"}
+ , "body":
+ { "type": "DEP_ARTIFACTS"
+ , "dep": {"type": "var", "name": "dep"}
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ , [ "test-results"
+ , { "type": "ACTION"
+ , "outs":
+ { "type": "++"
+ , "$1":
+ [ ["result", "stdout", "stderr", "time-start", "time-stop"]
+ , { "type": "foreach"
+ , "var": "filename"
+ , "range": {"type": "FIELD", "name": "keep"}
+ , "body":
+ { "type": "join"
+ , "$1": ["work/", {"type": "var", "name": "filename"}]
+ }
+ }
+ ]
+ }
+ , "inputs":
+ { "type": "map_union"
+ , "$1":
+ [ { "type": "singleton_map"
+ , "key": "work"
+ , "value": {"type": "var", "name": "deps"}
+ }
+ , {"type": "var", "name": "runner"}
+ , {"type": "var", "name": "test.sh"}
+ ]
+ }
+ , "cmd":
+ { "type": "++"
+ , "$1": [["./runner"], {"type": "FIELD", "name": "keep"}]
+ }
+ , "may_fail": ["test"]
+ , "fail_message":
+ { "type": "join"
+ , "$1": ["shell test ", {"type": "var", "name": "name"}, " failed"]
+ }
+ }
+ ]
+ , [ "runfiles"
+ , { "type": "singleton_map"
+ , "key": {"type": "var", "name": "name"}
+ , "value":
+ {"type": "TREE", "$1": {"type": "var", "name": "test-results"}}
+ }
+ ]
+ ]
+ , "body":
+ { "type": "RESULT"
+ , "artifacts": {"type": "var", "name": "test-results"}
+ , "runfiles": {"type": "var", "name": "runfiles"}
+ }
+ }
+ }
+}
diff --git a/rules/shell/test/RULES b/rules/shell/test/RULES
new file mode 100644
index 00000000..ca5ddbcf
--- /dev/null
+++ b/rules/shell/test/RULES
@@ -0,0 +1,50 @@
+{ "script":
+ { "doc": ["Shell test, given by a test script"]
+ , "target_fields": ["deps", "test"]
+ , "string_fields": ["keep", "name"]
+ , "field_doc":
+ { "test": ["The shell script for the test, launched with sh"]
+ , "name":
+ [ "A name for the test, used in reporting, as well as for staging"
+ , "the test result tree in the runfiles"
+ ]
+ , "keep":
+ [ "List of names (relativ to the test working directory) of files that"
+ , "the test might generate that should be kept as part of the output."
+ , "This might be useful for further analysis of the test"
+ ]
+ , "deps":
+ [ "Any targets that should be staged (with artifacts and runfiles) into"
+ , "the tests working directory"
+ ]
+ }
+ , "tainted": ["test"]
+ , "implicit": {"runner": ["test_runner.sh"]}
+ , "imports":
+ { "test-result": "test-result"
+ , "stage": ["./", "../..", "stage_singleton_field"]
+ }
+ , "expression":
+ { "type": "let*"
+ , "bindings":
+ [ [ "test.sh"
+ , { "type": "context"
+ , "msg": "Expecting 'test' to specify precisely one file containing a shell script"
+ , "$1":
+ { "type": "let*"
+ , "bindings": [["fieldname", "test"], ["location", "test.sh"]]
+ , "body": {"type": "CALL_EXPRESSION", "name": "stage"}
+ }
+ }
+ ]
+ , [ "name"
+ , { "type": "assert_non_empty"
+ , "msg": "Have to provide a non-empty name for the test (e.g., for result staging)"
+ , "$1": {"type": "join", "$1": {"type": "FIELD", "name": "name"}}
+ }
+ ]
+ ]
+ , "body": {"type": "CALL_EXPRESSION", "name": "test-result"}
+ }
+ }
+}
diff --git a/rules/shell/test/test_runner.sh b/rules/shell/test/test_runner.sh
new file mode 100755
index 00000000..969b80ad
--- /dev/null
+++ b/rules/shell/test/test_runner.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# ensure all required outputs are present
+touch stdout
+touch stderr
+RESULT=UNKNOWN
+echo "${RESULT}" > result
+echo UNKNOWN > time-start
+echo UNKNOWN > time-stop
+
+mkdir scratch
+export TEST_TMPDIR=$(realpath scratch)
+export TMPDIR="${TEST_TMPDIR}"
+
+# Change to the working directory; note: while unlikely, the test
+# might not have test data, so we have to ensure the presence of
+# the work directory.
+mkdir -p work
+cd work
+
+date +%s > ../time-start
+# TODO:
+# - proper wrapping with timeout
+if sh ../test.sh > ../stdout 2> ../stderr
+then
+ RESULT=PASS
+else
+ RESULT=FAIL
+fi
+date +%s > ../time-stop
+
+# Ensure all the promissed output files in the work directory
+# are present, even if the test failed to create them.
+for f in "$@"
+do
+ touch "./${f}"
+done
+
+echo "${RESULT}" > ../result
+
+if [ "${RESULT}" '!=' PASS ]
+then
+ exit 1;
+fi