Basic testing

This commit is contained in:
Matt Borgerson 2022-02-06 20:07:13 -07:00
parent dbdbc1424a
commit 06452f4d88
10 changed files with 328 additions and 0 deletions

View File

@ -0,0 +1,46 @@
name: Build Docker image
on:
push:
branches: master
paths:
- 'test-container/**'
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
main:
name: Build and Publish Image
if: github.repository_owner == 'mborgerson'
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Clone Tree
uses: actions/checkout@v2
- name: Extract image metadata (tags, labels)
id: meta
uses: docker/metadata-action@v3
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
if: github.event_name != 'pull_request'
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push image
uses: docker/build-push-action@v2
with:
context: test-container
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
results
bios.bin
mcpx.bin
xemu.ini
xemu.deb

49
test-container/Dockerfile Normal file
View File

@ -0,0 +1,49 @@
FROM ubuntu:20.04
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -qy fuse build-essential pkg-config libfuse-dev cmake git
RUN mkdir -p /usr/src \
&& git clone https://github.com/mborgerson/fatx.git /usr/src/fatx
WORKDIR /usr/src/fatx
RUN mkdir build \
&& cd build \
&& cmake .. \
&& make DESTDIR=/fatx install
FROM ubuntu:20.04
RUN set -xe; \
apt-get -qy update \
&& DEBIAN_FRONTEND=noninteractive \
apt-get -qy install \
xvfb \
x11-utils \
x11vnc \
xinit \
ffmpeg \
i3 \
fuse \
qemu-utils \
libc6 \
libepoxy0 \
libgcc-s1 \
libglib2.0-0 \
libgtk-3-0 \
libpcap0.8 \
libpixman-1-0 \
libpulse0 \
libsamplerate0 \
libsdl2-2.0-0 \
libssl1.1 \
libstdc++6 \
zlib1g \
cpu-checker \
;
COPY --from=0 /fatx /fatx
RUN cp -ruT /fatx / && rm -rf /fatx
ENV SDL_AUDIODRIVER=disk
ENV SDL_DISKAUDIOFILE=/dev/null
EXPOSE 5900
COPY ./docker_entry.sh /usr/local/bin/docker_entry.sh
ENTRYPOINT ["/usr/local/bin/docker_entry.sh"]

34
test-container/docker_entry.sh Executable file
View File

@ -0,0 +1,34 @@
#!/bin/bash
XVFB_WHD=640x480x24
DISPLAY=:99
if [ $# -eq 0 ]; then
echo "No launch command provided"
exit 1
fi
set -e
echo "[*] Installing xemu package"
apt-get -qy install /work/xemu.deb
echo "exec i3" >> ~/.xinitrc
chmod +x ~/.xinitrc
mkdir -p ~/.config/i3
echo "border none" >> ~/.config/i3/config
echo "[*] Starting Xvfb"
xinit -- /usr/bin/Xvfb $DISPLAY -ac -screen 0 "$XVFB_WHD" -nolisten tcp +extension GLX +render -noreset &
Xvfb_pid="$!"
echo "[~] Waiting for Xvfb (PID: $Xvfb_pid) to be ready..."
set +e
while ! xdpyinfo -display "${DISPLAY}" 1>/dev/null 2>&1; do
sleep 0.1
done
set -e
export DISPLAY
echo "[*] Starting VNC server"
x11vnc -forever 1>/dev/null 2>&1 &
echo "[*] Running target command"
exec "$@"

5
test-xbe/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
bin
*.d
*.exe
*.obj
*.iso

6
test-xbe/Makefile Normal file
View File

@ -0,0 +1,6 @@
XBE_TITLE = tester
GEN_XISO = $(XBE_TITLE).iso
SRCS = $(CURDIR)/main.c
NXDK_DIR ?= $(CURDIR)/../..
include $(NXDK_DIR)/Makefile

38
test-xbe/main.c Normal file
View File

@ -0,0 +1,38 @@
#include <hal/debug.h>
#include <hal/video.h>
#include <windows.h>
#include <nxdk/mount.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
XVideoSetMode(640, 480, 32, REFRESH_DEFAULT);
debugPrint("Hello nxdk!\n");
BOOL ret = nxMountDrive('C', "\\Device\\Harddisk0\\Partition2\\");
if (!ret) {
debugPrint("Failed to mount C: drive!\n");
goto shutdown;
}
CreateDirectoryA("C:\\results", NULL);
FILE *f = fopen("C:\\results\\results.txt", "w");
if (!f) {
goto shutdown;
}
const char *buf = "Success";
fwrite(buf, strlen(buf), 1, f);
fclose(f);
shutdown:
HalInitiateShutdown();
while (1) {
Sleep(2000);
}
return 0;
}

36
test.sh Executable file
View File

@ -0,0 +1,36 @@
#!/bin/bash
set -e
set -o pipefail
SUPPORT_DIR=private
SUPPORT_ARCHIVE=${SUPPORT_DIR}.zip
SUPPORT_ARCHIVE_ENC=${SUPPORT_ARCHIVE}.enc
SUPPORT_URL=${SUPPORT_URL:-http://localhost:8080/${SUPPORT_ARCHIVE_ENC}}
if [[ ! -d ${SUPPORT_DIR} ]]; then
if [[ ! -e ${SUPPORT_ARCHIVE} ]]; then
if [[ ! -e ${SUPPORT_ARCHIVE_ENC} ]]; then
echo "[*] Downloading ${SUPPORT_ARCHIVE_ENC}"
wget -O ${SUPPORT_ARCHIVE_ENC} ${SUPPORT_URL} 1>/dev/null 2>&1
fi
echo "[*] Decrypting ${SUPPORT_ARCHIVE}"
gpg --quiet --batch --yes --decrypt --passphrase="$SUPPORT_PASSPHRASE" \
--output ./${SUPPORT_ARCHIVE} ${SUPPORT_ARCHIVE_ENC}
fi
unzip ${SUPPORT_ARCHIVE}
fi
echo "[*] Building test executable"
docker run --rm -v $PWD/test-xbe:/work -w /work ghcr.io/xboxdev/nxdk make
echo "[*] Pulling test container"
docker pull ghcr.io/mborgerson/xemu-test:master
echo "[*] Running tests"
rm -rf results
mkdir results
docker run --rm -p 5900:5900 -v $PWD:/work -w /work --device /dev/fuse \
--privileged \
ghcr.io/mborgerson/xemu-test:master \
python3 test_main.py 2>&1 | tee results/log.txt

109
test_main.py Normal file
View File

@ -0,0 +1,109 @@
#!/usr/bin/env python3
import subprocess
import shutil
import logging
import os
import signal
import time
logging.basicConfig(level=logging.INFO)
_l = logging.getLogger(__file__)
class Test:
def __init__(self):
self.flash_path = '/work/private/bios.bin'
self.mcpx_path = '/work/private/mcpx.bin'
self.blank_hdd_path = '/work/xbox_hdd.qcow2'
self.hdd_path = '/tmp/test.img'
self.mount_path = '/tmp/xemu-hdd-mount'
self.iso_path = '/work/test-xbe/tester.iso'
self.results_in_path = os.path.join(self.mount_path, 'results')
self.results_out_path = '/work/results'
self.video_capture_path = os.path.join(self.results_out_path, 'capture.mp4')
self.timeout = 60
def prepare_roms(self):
_l.info('Preparing ROM images')
# TODO
def prepare_hdd(self):
_l.info('Preparing HDD image')
subprocess.run(f'qemu-img convert {self.blank_hdd_path} {self.hdd_path}'.split(), check=True)
def prepare_config(self):
config = ('[system]\n'
f'flash_path = {self.flash_path}\n'
f'bootrom_path = {self.mcpx_path}\n'
f'hdd_path = {self.hdd_path}\n'
'shortanim = true\n'
)
_l.info('Prepared config file:\n%s', config)
with open('xemu.ini', 'w') as f:
f.write(config)
def launch_ffmpeg(self):
_l.info('Launching FFMPEG (capturing to %s)', self.video_capture_path)
c = ('/usr/bin/ffmpeg -loglevel error '
f'-video_size 640x480 -f x11grab -i {os.getenv("DISPLAY")} '
f'-c:v libx264 -preset fast -profile:v baseline -pix_fmt yuv420p '
f'{self.video_capture_path} -y')
self.ffmpeg = subprocess.Popen(c.split())
def terminate_ffmpeg(self):
_l.info('Shutting down FFMPEG')
self.ffmpeg.send_signal(signal.SIGINT)
for _ in range(10):
self.ffmpeg.poll()
if self.ffmpeg.returncode is not None:
_l.info('FFMPEG exited %d', self.ffmpeg.returncode)
break
time.sleep(0.1)
self.ffmpeg.poll()
if self.ffmpeg.returncode is None:
_l.warning('Terminating FFMPEG')
self.ffmpeg.terminate()
def launch_xemu(self):
_l.info('Launching xemu...')
c = (f'timeout {self.timeout} '
f'xemu -config_path ./xemu.ini -dvd_path {self.iso_path} '
'-full-screen')
subprocess.run(c.split())
def mount_hdd(self):
_l.info('Mounting HDD image')
os.makedirs(self.mount_path, exist_ok=True)
subprocess.run(f'fatxfs {self.hdd_path} {self.mount_path}'.split(), check=True)
def copy_results(self):
_l.info('Copying test results...')
shutil.copytree(self.results_in_path, self.results_out_path, dirs_exist_ok=True)
def unmount_hdd(self):
_l.info('Unmounting HDD image')
subprocess.run(f'fusermount -u {self.mount_path}'.split())
def analyze_results(self):
with open(os.path.join(self.results_out_path, 'results.txt')) as f:
assert(f.read().strip() == 'Success')
def run(self):
os.makedirs(self.results_out_path, exist_ok=True)
self.prepare_roms()
self.prepare_hdd()
self.prepare_config()
self.launch_ffmpeg()
self.launch_xemu()
self.terminate_ffmpeg()
self.mount_hdd()
self.copy_results()
self.unmount_hdd()
self.analyze_results()
def main():
test = Test()
test.run()
if __name__ == '__main__':
main()

BIN
xbox_hdd.qcow2 Normal file

Binary file not shown.