#!/usr/bin/env bash # Checks that what the machine records is what it plays back. # # --record-pad writes what a controller held, one byte a frame, in exactly the format --pad # reads. That symmetry is the whole feature and it is the whole of what is checked here: a # recording is only worth having if playing it back does what was recorded. # # ---- Why it exists ---- # # Some inputs cannot sensibly be written by hand. Flying a lander from one base to another is # a few hundred frames of steering that has to arrive somewhere eight cells wide, and several # attempts at hand-authoring one got within two columns and no closer. That is a piloting # exercise rather than a test. Playing it once and keeping what happened is the answer, and # this is what says the keeping works. # # Written by Anachronaut set -u ROOT="$(cd "$(dirname "$0")/.." && pwd)" BUILD="$ROOT/Tests/build/replay" ASM="$ROOT/Assembler" EMU="$ROOT/SplitBit" for tool in "$ASM" "$EMU"; do [ -x "$tool" ] || { echo "$(basename "$tool") is not built."; exit 1; } done rm -rf "$BUILD"; mkdir -p "$BUILD" PASS=0 FAIL=0 FAILED_NAMES=() GREEN=$'\033[32m'; RED=$'\033[31m'; RESET=$'\033[0m' [ -t 1 ] || { GREEN=""; RED=""; RESET=""; } result() { if [ "$1" = "ok" ]; then PASS=$((PASS + 1)); printf " [%sok %s] %-40s %s\n" "$GREEN" "$RESET" "$2" "$3" else FAIL=$((FAIL + 1)); FAILED_NAMES+=("$2") printf " [%sFAIL%s] %-40s %s\n" "$RED" "$RESET" "$2" "$3" fi } echo "Checking that a recording plays back as itself." # A program that reads a pad every frame and stops. It is the one in testPrograms rather than # a new one, because what is being checked is the recording and not the reading. "$ASM" "$ROOT/Programs/testPrograms/padTest.asm" -o "$BUILD/padTest.bin" > "$BUILD/asm.log" 2>&1 \ || { echo "padTest did not assemble."; exit 1; } # ---- Played and recorded at the same time ---- # # The strongest form of the claim: the recording is made OF a playback, so if the two formats # ever drift apart the bytes coming out stop matching the bytes going in. "$EMU" --fast --pad "$ROOT/Tests/input/padTest.pad" \ --record-pad "$BUILD/roundtrip.pad" "$BUILD/padTest.bin" > "$BUILD/roundtrip.out" 2>&1 SAME="$(python3 -c " first = open('$ROOT/Tests/input/padTest.pad', 'rb').read() again = open('$BUILD/roundtrip.pad', 'rb').read() print('yes' if again[:len(first)] == first else 'no') " 2>/dev/null || echo error)" [ "$SAME" = "yes" ] \ && result ok "a recording of a playback is the playback" "byte for byte, in and out" \ || result no "a recording of a playback is the playback" "the bytes differ" # ---- One byte a frame, and the frame is the machine's ---- # # A recording is a TIMELINE. If it were a byte a read, a program that polled twice in one # frame would record twice as fast as it flew; if it skipped frames nobody looked at, it would # play back faster than it was flown. So the count is the number of frames the machine ran, # which at 16,667 cycles a frame is arithmetic rather than a guess. "$EMU" --fast --cycles 500000 --record-pad "$BUILD/timeline.pad" \ "$BUILD/padTest.bin" > "$BUILD/timeline.out" 2>&1 WROTE="$(wc -c < "$BUILD/timeline.pad" | tr -d ' ')" # The machine halts of its own accord well before the limit, so what is checked is that the # count is frames-of-something rather than bytes-of-nothing: more than none, and fewer than # the limit could possibly hold. [ "$WROTE" -gt 0 ] && [ "$WROTE" -le 30 ] \ && result ok "a byte for every frame and no more" "$WROTE frames of it" \ || result no "a byte for every frame and no more" "$WROTE bytes, which is not a frame count" # ---- And a machine with no controller records that honestly ---- # # Nothing held is nought, which is what a pad nobody is touching reports and what a machine # with no pad at all reports. A recorder that wrote something else would make a fixture that # pressed buttons nobody pressed. QUIET="$(python3 -c " data = open('$BUILD/timeline.pad', 'rb').read() print('yes' if set(data) <= {0} else 'no') " 2>/dev/null || echo error)" [ "$QUIET" = "yes" ] \ && result ok "and nothing held is written as nothing" "no buttons nobody pressed" \ || result no "and nothing held is written as nothing" "the recording holds something" echo if [ "$FAIL" -eq 0 ]; then echo "All $PASS replay checks passed." exit 0 fi echo "$PASS passed, $FAIL failed: ${FAILED_NAMES[*]}" exit 1