Skip to content

Commit

Permalink
Add initial unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tstirrat committed Jul 31, 2024
1 parent 8fba11b commit c05f0b3
Show file tree
Hide file tree
Showing 19 changed files with 476 additions and 4 deletions.
7 changes: 3 additions & 4 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ insert_final_newline = true
[Makefile]
indent_style = tab
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.py]
generated_code = true
83 changes: 83 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: Pytest

on: [push]

jobs:
build:
runs-on: ubuntu-latest
container:
image: scottyhardy/docker-wine:latest
env:
# USER_NAME: runner
# USER_UID: ${USER_UID:-1010}
# USER_GID: ${USER_GID:-"${USER_UID}"}
# USER_HOME: ${USER_HOME:-/home/"${USER_NAME}"}
# USER_PASSWD: ${USER_PASSWD:-"$(openssl passwd -1 -salt "$(openssl rand -base64 6)" "${USER_NAME}")"}
# USER_SUDO: ${USER_SUDO:-yes}
RDP_SERVER: yes
# RUN_AS_ROOT: ${RUN_AS_ROOT:-no}
# FORCED_OWNERSHIP: ${FORCED_OWNERSHIP:-no}
# TZ: ${TZ:-UTC}
# USE_XVFB: ${USE_XVFB:-no}
DUMMY_PULSEAUDIO: yes # ${DUMMY_PULSEAUDIO:-no}

steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: robinraju/release-downloader@v1
with:
repository: "gbdk-2020/gbdk-2020"
tag: "4.3.0"
fileName: "gbdk-linux64.tar.gz"
extract: true
# - uses: pyvista/setup-headless-display-action@v2
# - name: Install wine
# run: |
# sudo dpkg --add-architecture i386
# sudo mkdir -pm755 /etc/apt/keyrings
# sudo wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key
# sudo wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/ubuntu/dists/jammy/winehq-jammy.sources
# sudo apt update
# sudo apt install -y --install-recommends winehq-stable winbind
# wine --version
# - name: Install wine-mono
# run: |
# wget https://github.com/madewokherd/wine-mono/releases/download/wine-mono-9.0.0/wine-mono-9.0.0-x86.msi
# WINEDEBUG=fixme-all WINEDLLOVERRIDES=mscoree=d wine wine-mono-9.0.0-x86.msi
- name: pip install
shell: bash
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
working-directory: ./tests
- name: make roms
shell: bash
run: |
make
ls build
working-directory: ./tests
- name: test single rom (python)
shell: bash
run: |
python test.py
working-directory: ./tests
continue-on-error: true
- run: |
ls -alF
shell: bash
working-directory: ./tests/build
- name: test single rom (bash)
run: |
wine ../bgb/bgb.exe -autoexit -hf -stateonexit -screenonexit build/wav_test_load_and_play.bmp -rom build/wav_test_load_and_play.gb
working-directory: ./tests
- run: |
ls -alF
shell: bash
working-directory: ./tests/build
# run: |
# pytest -v -s
# working-directory: ./tests
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.vscode/settings.json
.venv/
gbdk/
Source/obj
Source/out
mgb.gb
bgb
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "tests/gbdk_unit_test"]
path = tests/gbdk_unit_test
url = git@github.com:untoxa/gbdk_unit_test.git
10 changes: 10 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@
},
"dependsOn": "make-debug",
"problemMatcher": []
},
{
"label": "make-test-roms",
"type": "shell",
"command": "make",
"args": [],
"options": {
"cwd": "tests/"
},
"problemMatcher": []
}
]
}
4 changes: 4 additions & 0 deletions tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/build
/unused
__pycache__
.pytest_cache
39 changes: 39 additions & 0 deletions tests/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
MYDIR = .
BLDDIR = $(MYDIR)/build
FW = $(MYDIR)/framework
CC = ../gbdk/bin/lcc -tempdir=$(BLDDIR) -Wl-j -Wl-m -Wl-w -Wl-yt2 -Wl-yo4 -Wl-ya4

TESTS = $(wildcard *.c)
OBJS = $(TESTS:%.c=$(BLDDIR)/%.o)
TESTROMS = $(TESTS:%.c=$(BLDDIR)/%.gb)

BGBDIR = ../bgb
# GBDKDIR = ../gbdk

all: clean mkdirs $(BGBDIR) build-all

.PHONY: clean
clean:
rm -rf $(BLDDIR)

.PHONY: mkdirs
mkdirs:
mkdir -p $(BLDDIR)

$(BGBDIR):
wget https://bgb.bircd.org/bgb.zip
unzip -jo -qq bgb.zip -d ../bgb
rm bgb.zip

# gbdk-linux64.tar.gz:
# wget https://github.com/gbdk-2020/gbdk-2020/releases/download/4.3.0/gbdk-linux64.tar.gz

# $(GBDKDIR):
# tar -zxvf gbdk-linux64.tar.gz -C $(GBDKDIR)
# # rm gbdk-linux64.tar.gz

$(BLDDIR)/%.gb: $(MYDIR)/%.c mkdirs $(BGBDIR)
$(CC) -o $@ $<

.PHONY: build-all
build-all: $(TESTROMS)
152 changes: 152 additions & 0 deletions tests/bgb_get_snapshot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import subprocess
from array import array
import os
from pathlib import Path
from PIL import Image, ImageChops

from gbdk_unit_test.framework.BGB_toolkit import load_noi, read_bgb_snspshot

base_dir = os.path.dirname(os.path.realpath(__file__))
base_path = Path(base_dir)


def load_rom_snapshot(rom_relative):

make_and_run(rom_relative)

snapshot_file = base_path.joinpath(rom_relative).with_suffix('.sna')

if not snapshot_file.is_file():
raise Exception("Cannot load snapshot: " + snapshot_file)

noi_file = base_path.joinpath(rom_relative).with_suffix('.noi')
if not snapshot_file.is_file():
raise Exception("Cannot load symbols: " + noi_file)

screenshot = base_path.joinpath(rom_relative).with_suffix('.bmp')

snapshot = read_bgb_snspshot(snapshot_file)
symbols = load_noi(noi_file)
symbols = {value: key for key, value in symbols.items()}

snapshot['symbols'] = symbols
snapshot['screenshot'] = screenshot

return snapshot


def make_and_run(rom_relative):
make_rom(rom_relative)

rom_path_full = base_path.joinpath(rom_relative)
screenshot_path = rom_path_full.with_suffix('.bmp')

bgb_exe = ["wine", "../bgb/bgb64.exe"]

# windows
if os.name == 'nt':
bgb_exe = ["../bgb/bgb64.exe"]

cmd = [
*bgb_exe,
"-set \"DebugSrcBrk=1\"",
"-autoexit",
"-hf",
"-stateonexit",
# "-screenonexit", screenshot_path.absolute().as_posix(),
rom_relative,
]

print("executing bgb: ", cmd)
my_env = os.environ.copy()
my_env["WINEDEBUG"] = "fixme-all"

subprocess.call(cmd,
cwd=base_dir,
env=my_env,
# stdout=subprocess.DEVNULL,
# stderr=subprocess.DEVNULL,
)

print("bgb: finished")

if not rom_path_full.with_suffix(".sna").is_file():
raise Exception("Tried to run rom, failed " + rom_relative)


def make_rom(rom_relative):
print("calling make", rom_relative)
result = subprocess.call(
["make", rom_relative],
cwd=base_dir,
# stdout=subprocess.DEVNULL
)

print("make: finished", rom_relative)

if result != 0:
raise Exception("Rom make failed " + rom_relative)


# The following code is repurposed from unit_checker.py by untoxa (MIT License)

# WRAM = 49152
mem_map = {
'VRAM': 0x8000,
'WRAM': 0xC000,
'OAM': 0xFE00,
'IO_REG': 0xFF00,
'HRAM': 0xFF80,
}


def symbol_addr(snapshot, symbol, base):
if type(base) is str:
base = mem_map[base.upper()]
return snapshot['symbols'].get(symbol) - base


def get(snapshot, section, address, len=0):
if isinstance(address, str):
address = symbol_addr(snapshot, address, section)

if len > 1:
return snapshot[section][address:address + len]
else:
return snapshot[section][address]


def ASCIIZ(snapshot, section, address):
ofs = address
data = snapshot[section]
fin = ofs
while data[fin] != 0:
fin += 1
return str(data[ofs:fin], 'ascii') if fin - ofs > 0 else ''


def CHECKSCREEN(snapshot, file_name):
image_one = Image.open(base_path.joinpath(file_name)).convert('RGB')
image_two = Image.open(snapshot['screenshot']).convert('RGB')

diff = ImageChops.difference(image_one, image_two)

return (diff.getbbox() is None)


def find(input: dict | array, val: str | int, parent_key: str | None):
if isinstance(input, array):
for i, v in enumerate(input):
if v == val:
print("found", [parent_key, i])
return [parent_key, i]
elif isinstance(v, array):
find(v, val, i)
return

for k, v in input.items():
if v == val:
print("found", [parent_key, k])
return [parent_key, k]
elif isinstance(v, array) or isinstance(v, dict):
find(v, val, k)
8 changes: 8 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import pytest

from bgb_get_snapshot import load_rom_snapshot


@pytest.fixture
def snapshot(request):
return load_rom_snapshot(request.param)
1 change: 1 addition & 0 deletions tests/gbdk_unit_test
Submodule gbdk_unit_test added at 101355
35 changes: 35 additions & 0 deletions tests/pu1_plays_note.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include <gbdk/emu_debug.h>
#include <gbdk/platform.h>

#include "../Source/io/midi.h"

#include "../Source/mGB.h"
#include "../Source/synth/common.c"
#include "../Source/synth/pulse.c"
#include "../Source/synth/wav.c"

bool systemIdle = true;

uint8_t statusByte;
uint8_t addressByte;
uint8_t valueByte;
uint8_t capturedAddress;

uint8_t result[2] = {0U, 0U};

void main(void) {
rAUDENA = AUDENA_ON;
rAUDVOL = AUDVOL_VOL_LEFT(7U) | AUDVOL_VOL_RIGHT(7U);

setOutputPan(PU1, 64U);

addressByte = 64U; // MIDI note
valueByte = 127U; // MIDI velocity

playNotePu1();
updatePu1();

delay(100);

EMU_BREAKPOINT;
}
18 changes: 18 additions & 0 deletions tests/pu1_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import pytest

from bgb_get_snapshot import get


def describe_pu1():

@pytest.mark.parametrize('snapshot', ['build/pu1_plays_note.gb'], indirect=True)
def it_plays_a_note(snapshot):
rAUD1LOW = get(snapshot, 'IO_REG', '_NR13_REG')
rAUD1HIGH = get(snapshot, 'IO_REG', '_NR14_REG')

# noteIndex = 64U - 36U == 28U
# f = freq[noteIndex] == 1650U == 0x0672

assert rAUD1LOW == 0x72
retrig = 0x80
assert rAUD1HIGH == 0x06 | retrig
4 changes: 4 additions & 0 deletions tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pillow ~= 10.4.0
pytest ~= 8.3.0
pytest-describe ~= 2.2.0
pytest-sugar ~= 1.0.0
Loading

0 comments on commit c05f0b3

Please sign in to comment.