Skip to content

Commit

Permalink
Initial implementation
Browse files Browse the repository at this point in the history
TAP is so good.  Converting Cram's output to TAP makes it possible to
use `prove`, e.g.

    prove --exec=cram-tap tests/

and being able to use `prove` (or any other TAP runner/harness!) opens
up features like parallelization with smart start-slowest-first
behaviour, e.g.

    prove --exec=cram-tap -j9 --state=slow,save tests/

This produces TAP by converting Cram's xUnit output.  It would be better
(and I suspect very much not difficult) to add TAP output directly to
Cram.  I'll leave that for another time.
  • Loading branch information
tsibley committed Nov 14, 2024
0 parents commit 122f351
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 0 deletions.
14 changes: 14 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Usage: cram-tap [OPTIONS] TESTS...

A TAP-emitting Cram.

cram-tap options:
-h, --help show this help message and exit
-V, --version show version information and exit

cram options:
-E, --preserve-env don't reset common environment variables
--keep-tmpdir keep temporary directories
--shell=PATH shell to use for running tests (default: /bin/sh)
--shell-opts=OPTS arguments to invoke shell with
--indent=NUM number of spaces to use for indentation (default: 2)
62 changes: 62 additions & 0 deletions cram-tap
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/bin/bash
set -euo pipefail

main() {
if [[ $# -eq 0 ]]; then
exit-with-help
fi

# Validate arguments
for arg; do
case "$arg" in
--help|-h)
exit-with-help;;

--version|-V)
exit-with-version;;

--interactive|-i|--yes|-y|--no|-n)
echo "cram's interactivity options (e.g. $1) are not supported under cram-tap" >&2
exit 1;;

--quiet|-q|--verbose|-v|--debug|-d|--xunit-file)
echo "cram's output control options (e.g. $1) are not supported under cram-tap" >&2
exit 1;;
esac
done

# XXX TODO: Maybe unmask cram's exit status depending on if it matches
# failed test count?
# -trs, 13 Nov 2024
cram "$@" --xunit-file=>("$(dirname "$0")"/cram-xunit-to-tap) >/dev/null || true
}

exit-with-help() {
cram --help | sed -Ee '
1s/^Usage: cram /Usage: cram-tap /
/^Options:/ {
i A TAP-emitting Cram.
i
i cram-tap options:
i \ \ -h, --help show this help message and exit
i \ \ -V, --version show version information and exit
i
c cram options:
}
/\s--(help|version)\s/d # takeover help and version
/\s--(quiet|verbose|debug|xunit-file=PATH)\s/d # no output control
/\s--(interactive|yes|no)\s/d # no interactivity
'
exit
}

exit-with-version() {
echo "cram-tap (version 0)"
echo "Copyright 2024 Thomas Sibley <[email protected]>"
echo
cram --version
exit
}

main "$@"
35 changes: 35 additions & 0 deletions cram-xunit-to-tap
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
import xml.etree.ElementTree as ET
from sys import stdin, stdout
from textwrap import indent

doc = ET.parse(stdin)
suite = doc.getroot()

assert suite.tag == "testsuite" and suite.get("name") == "cram"

count = int(suite.get("tests", 0))

print("TAP version 13")
print(f"1..{count:d}")

escape = lambda text: text.replace("\\", "\\\\").replace("#", "\\#")
comment = lambda text: indent(text, "# ", lambda line: True)

for i, test in enumerate(suite.findall("./testcase"), 1):
if file := test.get("classname"):
description = "- " + escape(file)
else:
description = ""

if (failure := test.find("failure")) is not None:
print("not ok", i, description)
print(comment(failure.text))

elif test.find("skipped") is not None:
# Skipped tests in TAP may be ok or not ok, but Cram doesn't tell us.
print("ok", i, description, "# SKIP")

else:
assert len(test) == 0
print("ok", i, description)

0 comments on commit 122f351

Please sign in to comment.