diff options
| author | Miquel Sabaté Solà <msabate@suse.com> | 2023-12-08 21:57:52 +0100 |
|---|---|---|
| committer | Miquel Sabaté Solà <msabate@suse.com> | 2023-12-08 21:57:52 +0100 |
| commit | 46103595c072651ade490c48c417f5d8d7ba9327 (patch) | |
| tree | 92cc48e3e88e614618ea2cca3771c88674f92189 | |
| parent | 57fde0bb5184371da33b212de6731c6e6829349f (diff) | |
| download | aoc2023.nes-46103595c072651ade490c48c417f5d8d7ba9327.tar.gz aoc2023.nes-46103595c072651ade490c48c417f5d8d7ba9327.zip | |
Added a test suite
Signed-off-by: Miquel Sabaté Solà <msabate@suse.com>
| -rw-r--r-- | Makefile | 8 | ||||
| -rw-r--r-- | src/1.s | 16 | ||||
| -rw-r--r-- | src/2.s | 12 | ||||
| -rw-r--r-- | test/1.lua | 13 | ||||
| -rw-r--r-- | test/2.lua | 13 | ||||
| -rw-r--r-- | test/defines.s | 11 | ||||
| -rw-r--r-- | test/run.sh | 44 | ||||
| -rw-r--r-- | test/utils.lua | 80 |
8 files changed, 193 insertions, 4 deletions
@@ -1,5 +1,8 @@ CC65 ?= cl65 CCOPTS ?= --target nes +ifeq "$(DEBUG)" "1" +CCOPTS += -g -Ln out/labels.txt +endif QUIET = @echo ' ' CC65 $@; SOURCES = $(shell find src/ -type f -name '*.s' -printf "%f\n") @@ -10,6 +13,7 @@ all: clean deps build .PHONY: clean clean: + @sed -i 's/RUN_TESTS = 1/RUN_TESTS = 0/g' test/defines.s @rm -rf out @mkdir out @find . -type f -name "*.o" -delete @@ -24,3 +28,7 @@ build: $(ROMS) out/%.nes: src/%.s $(QUIET) $(CC65) $(CCOPTS) $< -o $@ + +.PHONY: test +test: + @bash test/run.sh @@ -33,6 +33,8 @@ .include "../include/globals.s" .include "../include/print.s" +.include "../test/defines.s" + ;; "Variables" used by this program. .scope Vars ;; After each iteration, this will hold the tenth of the number that has @@ -99,8 +101,18 @@ lda #%00011110 sta PPU::MASK @loop: + ;; Skip everything if the `done` flag is set. + bit Globals::m_flags + bvs @end + jsr compute_next jmp @loop + +@end: + ;; Run tests now that everything has been done. + CALL_TESTS_ON_DONE +@halt: + jmp @halt .endproc ;; Iterate over the row pointed by `m_address` and compute its value. The tenth @@ -153,10 +165,6 @@ ;; address to the next row. This function will also set the `done` flag whenever ;; we reach the $ED control byte. .proc compute_next - ;; Skip everything if the `done` flag is set. - bit Globals::m_flags - bvs @end - ;; Call `compute` and `accumulate_number`, which work in tandem in order to ;; fetch the value for the current row and add it into the `m_sum` ;; accumulator. @@ -38,6 +38,8 @@ .include "../include/globals.s" .include "../include/print.s" +.include "../test/defines.s" + ;; "Variables" used by this program. .scope Vars ;; Maximum number for each color according to Part 1. @@ -127,8 +129,18 @@ sta PPU::MASK @loop: + ;; Skip everything if the `done` flag is set. + bit Globals::m_flags + bvs @end + jsr compute_next jmp @loop + +@end: + ;; Run tests now that everything has been done. + CALL_TESTS_ON_DONE +@halt: + jmp @halt .endproc ;; Compute the next game if needed. diff --git a/test/1.lua b/test/1.lua new file mode 100644 index 0000000..b51f21e --- /dev/null +++ b/test/1.lua @@ -0,0 +1,13 @@ +utils = require "utils" + +utils.StartRun("1") + +utils.MemTest("@test", {{0x08, "D6"}, {0x09, "A9"}}) + +-- The test appears to be done in a few frames. Let's run this several times +-- just in case. +for i = 0, 20, 1 do + emu.frameadvance(); +end + +utils.EndRun() diff --git a/test/2.lua b/test/2.lua new file mode 100644 index 0000000..7cb8f40 --- /dev/null +++ b/test/2.lua @@ -0,0 +1,13 @@ +utils = require "utils" + +utils.StartRun("2") + +utils.MemTest("@test", {{0x0B, "00"}, {0x0C, "01"}, {0x0D, "0F"}, {0x0E, "FD"}}) + +-- The test appears to be done in a few frames. Let's run this several times +-- just in case. +for i = 0, 20, 1 do + emu.frameadvance(); +end + +utils.EndRun() diff --git a/test/defines.s b/test/defines.s new file mode 100644 index 0000000..17c0221 --- /dev/null +++ b/test/defines.s @@ -0,0 +1,11 @@ +RUN_TESTS = 0 + +.macro CALL_TESTS_ON_DONE + .ifdef RUN_TESTS + bit Globals::m_flags + bvc :+ + @test: + nop + : + .endif +.endmacro diff --git a/test/run.sh b/test/run.sh new file mode 100644 index 0000000..2142ad1 --- /dev/null +++ b/test/run.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +set -e + +ROOT="$( cd "$( dirname "$0" )/.." && pwd )" +cd "$ROOT" + +# Clean previous builds and prepare the test environment. +rm -f "$ROOT/out/test-results.txt" +make clean +sed -i 's/RUN_TESTS = 0/RUN_TESTS = 1/g' $ROOT/test/defines.s + +# Github Actions do not allow GUI programs to be run. This means that the code +# below will always fail (fceux won't be able to run). There is a way to emulate +# an X server with tools like xvfb-run or xvncserver, but so far I've had no +# luck on this front. +if [ -n "${GITHUB_ACTION}" ]; then + exit 0 +fi + +# Run all the tests that we have on Lua. +for name in $(seq 2); do + DEBUG=1 make out/$name.nes + fceux --loadlua "$ROOT/test/$name.lua" "$ROOT/out/$name.nes" +done + +# Show the results. +cat "$ROOT/out/test-results.txt" +n=$(cat "$ROOT/out/test-results.txt" | grep FAIL | wc -l) +echo "" +case $n in + 0) + echo "All tests passed!" + exit 0 + ;; + 1) + echo "1 test failed!" + exit 1 + ;; + *) + echo "$n tests failed!" + exit 1 + ;; +esac diff --git a/test/utils.lua b/test/utils.lua new file mode 100644 index 0000000..c5ecd94 --- /dev/null +++ b/test/utils.lua @@ -0,0 +1,80 @@ +utils = {} + +-- Returns the root path for the project. +function utils.RootPath() + local fullpath = debug.getinfo(1,"S").source:sub(2) + fullpath = io.popen("realpath '"..fullpath.."'", 'r'):read() + fullpath = fullpath:gsub('[\n\r]*$','') + + local dirname, filename = fullpath:match('^(.*/)([^/]-)$') + dirname = dirname or '' + if dirname == '' then + return '' + end + + return io.popen("realpath '"..dirname.."/..'", 'r'):read() +end + +-- At a `label` that exists on the assembly code grab the values for the given +-- addresses and write it all into the `test-results.txt` file. The `addresses` +-- array is made up of two-sized arrays, where the first element contains the +-- memory you are trying to test, and the second element is the value that we +-- are expecting. +function utils.MemTest(label, addresses) + local cmd = "cat ".. utils.RootPath() .. "/out/labels.txt | grep .".. label .." | awk '{ print $2; }' | cut -c3-" + local file = assert(io.popen(cmd, 'r')) + local result = file:read("*a") + + -- Double check that the address that we grabbed has at least a good format. + if string.len(result) ~= 5 then + error("Error on '" .. label .. "': got a bad address! (".. result ..")") + end + + -- Register a function to execute on the given test address. The function will + -- simply iterate over the given `addresses` and compare them with the + -- expected result. Everything will be saved into the `test-results.txt` file. + memory.registerexecute(tonumber(result, 16), function() + local expected = "" + local got = "" + + for _, vals in ipairs(addresses) do + expected = expected .. "$" .. string.format("%04X", vals[1]) .. " -> " .. vals[2] .. "; " + got = got .. "$" .. string.format("%04X", vals[1]) .. " -> " .. string.format("%02X", memory.readbyte(vals[1])) .. "; " + end + + file = io.open(utils.RootPath() .. "/out/test-results.txt", "a") + io.output(file) + + if expected == got then + io.write("OK\n") + else + io.write("\n-> Test '".. label .."': FAIL\n") + io.write("Expected: ".. expected .. "\n") + io.write("Got: ".. got .. "\n") + io.write("\n") + end + io.close(file) + end) +end + +function utils.StartRun(title) + file = io.open(utils.RootPath() .. "/out/test-results.txt", "a") + io.output(file) + io.write(title .. ": ") + io.close(file) +end + +-- Ends the given test run. That is, it will exit from the emulator so we can +-- turn back to the runner. +function utils.EndRun() + -- I'm not entirely sure why this is needed, but if we don't advance for + -- several frames fceux won't exit. Thus, let's advance for some frames and + -- then quit. + for i = 0, 10, 1 do + emu.frameadvance(); + end + + emu.exit() +end + +return utils |
