diff --git a/CMakeLists.txt b/CMakeLists.txt index 7de9776..bf82d92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,4 +92,7 @@ if(BUILD_TESTING) target_include_directories(tux-dock-stop-sequence-tests PRIVATE src) target_compile_options(tux-dock-stop-sequence-tests PRIVATE -Wall -Wextra -Wpedantic) add_test(NAME tux-dock-stop-sequence-tests COMMAND tux-dock-stop-sequence-tests) + + add_test(NAME tux-dock-docker-integration-tests + COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_docker_integration.sh) endif() diff --git a/README.md b/README.md index 1aba8a1..051c28e 100644 --- a/README.md +++ b/README.md @@ -47,10 +47,15 @@ sudo ./build/tux-dock # Build without running tests ./compile.sh --no-test + +# Run tests and serve an HTML 3.2 report on port 8095 +./compile.sh --web-test-view + +# Override the report server port +./compile.sh --web-test-view 9000 ``` -Prefer a prebuilt binary? CI artifacts are published at: -https://mentalnet.xyz/forgejo/markmental/tuxdock/actions +The web report is generated under `/tmp` and includes exact CTest output, test summaries, Docker integration output, and a text rendition of the TUI flow. It requires `nc` or `netcat`; press `Ctrl-C` to stop the server. The default port is `8095`; pass a port after `--web-test-view` to override it. `--web-test-view --no-test` is invalid. Normal test runs include a Docker integration test using `debian:forky`, so Docker must be available. --- @@ -161,7 +166,7 @@ ctest --test-dir build --output-on-failure - Version: `0.1-beta` - Created by: `markmental` - GitHub: https://github.com/MARKMENTAL/tuxdock -- Forgejo: https://mentalnet.xyz/forgejo/markmental/tuxdock +- Forgejo: https://mentalnet.xyz/forgejo-v2/markmental/tuxdock --- diff --git a/compile.sh b/compile.sh index ae041fa..29ab3a7 100755 --- a/compile.sh +++ b/compile.sh @@ -2,14 +2,37 @@ set -eu -if [ "$#" -gt 1 ] || { [ "$#" -eq 1 ] && [ "$1" != "--no-test" ]; }; then - printf '%s\n' "Usage: $0 [--no-test]" >&2 +run_tests=1 +web_view=0 +web_port=8095 + +while [ "$#" -gt 0 ]; do + argument=$1 + shift + case "$argument" in + --no-test) run_tests=0 ;; + --web-test-view) + web_view=1 + if [ "$#" -gt 0 ] && printf '%s' "$1" | grep -Eq '^[0-9]+$'; then + web_port=$1 + shift + fi + ;; + *) + printf '%s\n' "Usage: $0 [--no-test] [--web-test-view [port]]" >&2 + exit 2 + ;; + esac +done + +if [ "$web_port" -lt 1 ] || [ "$web_port" -gt 65535 ]; then + printf '%s\n' "Web test view port must be between 1 and 65535" >&2 exit 2 fi -run_tests=1 -if [ "$#" -eq 1 ]; then - run_tests=0 +if [ "$web_view" -eq 1 ] && [ "$run_tests" -eq 0 ]; then + printf '%s\n' "--web-test-view cannot be combined with --no-test" >&2 + exit 2 fi if [ "$run_tests" -eq 1 ]; then @@ -21,7 +44,106 @@ fi cmake --build build -j if [ "$run_tests" -eq 1 ]; then - ctest --test-dir build --output-on-failure + report_dir=$(mktemp -d /tmp/tux-dock-test.XXXXXX) + test_output="$report_dir/ctest-output.txt" + test_status=0 + ctest --test-dir build --output-on-failure >"$test_output" 2>&1 || test_status=$? + + if [ "$web_view" -eq 1 ]; then + report="$report_dir/report.html" + escaped_output=$(mktemp "$report_dir/output.XXXXXX") + sed 's/&/\&/g; s/\</g; s/>/\>/g' "$test_output" >"$escaped_output" + total=$(awk '/tests passed, [0-9]+ tests failed out of [0-9]+/ { print $(NF); exit }' "$test_output" 2>/dev/null || true) + failed=$(awk '/tests passed, [0-9]+ tests failed out of [0-9]+/ { print $4; exit }' "$test_output" 2>/dev/null || true) + total=${total:-unknown} + passed=${passed:-0} + failed=${failed:-0} + if [ "$total" != unknown ]; then + passed=$((total - failed)) + fi + duration=$(awk '/Total Test time/ { print $(NF - 1); exit }' "$test_output" 2>/dev/null || true) + duration=${duration:-unknown} + command_line='ctest --test-dir build --output-on-failure' + test_rows=$(mktemp "$report_dir/test-rows.XXXXXX") + awk ' + match($0, /^[[:space:]]*([0-9]+)\/([0-9]+) Test #[0-9]+: ([^ ]+)[[:space:]]+\.+[[:space:]]+(Passed|Failed|Skipped)[[:space:]]+([0-9.]+) sec/, m) { + printf "
Generated: %s
\n' "$(date)" + printf '%s\n' '| Metric | Value | Graph | |
|---|---|---|---|
| Total tests | %s | ||
| Passed | %s | ||
| Failed | %s | ||
| Duration | %s seconds |
| # | Test | Status | Duration |
|---|
' + printf '%s\n' "$command_line" + printf '%s\n' '
' + cat "$escaped_output" + printf '%s\n' '
' + cat "$escaped_raw" + printf '%s\n' '
' + cat "$escaped_tui" + printf '%s\n' '' + } >"$report" + + response="$report_dir/http-response.txt" + report_size=$(wc -c <"$report" | tr -d ' ') + { + printf 'HTTP/1.1 200 OK\r\n' + printf 'Content-Type: text/html; charset=utf-8\r\n' + printf 'Content-Length: %s\r\n' "$report_size" + printf 'Connection: close\r\n' + printf '\r\n' + cat "$report" + } >"$response" + + if command -v nc >/dev/null 2>&1; then + nc_command=nc + elif command -v netcat >/dev/null 2>&1; then + nc_command=netcat + else + printf '%s\n' "Web report generated at $report, but nc/netcat is unavailable." >&2 + exit "$test_status" + fi + + printf '%s\n' "Test report: http://0.0.0.0:$web_port/" + printf '%s\n' "Report file: $report" + printf '%s\n' "Press Ctrl-C to stop the server." + while :; do + "$nc_command" -l -N -s 0.0.0.0 -p "$web_port" <"$response" + done + fi + exit "$test_status" fi printf '%s\n' "tux-dock successfully compiled!" diff --git a/tests/test_docker_integration.sh b/tests/test_docker_integration.sh new file mode 100755 index 0000000..60a2aba --- /dev/null +++ b/tests/test_docker_integration.sh @@ -0,0 +1,76 @@ +#!/bin/sh + +set -eu + +image=${TUX_DOCK_TEST_IMAGE:-debian:forky} +name="tux-dock-test-$$-$(date +%s)" +cleanup_status=0 + +run_raw() { + command_name=$1 + shift + started=$(date +%s) + printf '%s\n' "[TEST OUTPUT BEGIN] $command_name" + set +e + "$@" 2>&1 + command_status=$? + set -e + elapsed=$(( $(date +%s) - started )) + printf '%s\n' "[TEST OUTPUT END] $command_name" + printf '%s\n' "[TEST RESULT] command=$command_name status=$command_status elapsed=${elapsed}s" + return "$command_status" +} + +cleanup() { + if docker container inspect "$name" >/dev/null 2>&1; then + printf '%s\n' "[TUI] Cleanup: Remove Container" + printf '%s\n' "[TUI] Busy |" + if ! docker rm -f "$name"; then + cleanup_status=1 + fi + printf '%s\n' "[TUI] Complete: Container removed" + fi +} +trap cleanup EXIT INT TERM + +printf '%s\n' "[TUI] Actions > Run/Create Interactive Container" +printf '%s\n' "[TUI] Busy |" +printf '%s\n' "[TUI] docker run --name $name $image sh -c '