Browse Source

Create reusable action for running citra

Minor updates to setup etc as well
pull/1/head
Ian Chamberlain 1 year ago
parent
commit
05b8c86e71
No known key found for this signature in database
GPG Key ID: AE5484D09405AA60
  1. 2
      .dockerignore
  2. 1
      .github/CODEOWNERS
  3. 12
      .github/actions/citra/action.yml
  4. 6
      .github/actions/setup/action.yml
  5. 13
      .github/workflows/ci.yml
  6. 38
      Dockerfile
  7. 39
      README.md
  8. 6
      docker/download_citra.sh
  9. 26
      docker/entrypoint.sh
  10. 338
      docker/sdl2-config.ini
  11. 7
      docker/test-runner.gdb

2
.dockerignore

@ -0,0 +1,2 @@
**
!docker/

1
.github/CODEOWNERS

@ -1 +0,0 @@
* @rust3ds/active

12
.github/actions/citra/action.yml

@ -0,0 +1,12 @@
name: 'Run 3DS Executable'
description: 'Run a given 3DS executable with citra and GDB'
inputs:
executable:
description: >
The 3DS executable(s) to run. Globs and space separated lists allowed.
required: true
runs:
using: docker
image: ../../../Dockerfile
args:
- ${{ inputs.executable }}

6
.github/actions/setup/action.yml

@ -26,13 +26,17 @@ runs:
components: clippy, rustfmt, rust-src components: clippy, rustfmt, rust-src
toolchain: ${{ inputs.toolchain }} toolchain: ${{ inputs.toolchain }}
- name: Set up Rust cache
uses: Swatinem/rust-cache@v2
- name: Install build tools for host - name: Install build tools for host
shell: bash shell: bash
run: sudo apt-get update && sudo apt-get install -y build-essential run: sudo apt-get update && sudo apt-get install -y build-essential
- name: Install cargo-3ds - name: Install cargo-3ds
shell: bash shell: bash
run: cargo install cargo-3ds # TODO: replace with crates.io version once published
run: cargo install --git https://github.com/rust3ds/cargo-3ds --branch feature/verbose-flag
- name: Set PATH to include devkitARM - name: Set PATH to include devkitARM
shell: bash shell: bash

13
.github/workflows/ci.yml

@ -3,10 +3,10 @@ name: CI
on: on:
push: push:
branches: branches:
- master - main
pull_request: pull_request:
branches: branches:
- master - main
workflow_dispatch: workflow_dispatch:
env: env:
@ -65,3 +65,12 @@ jobs:
- name: Build doc tests - name: Build doc tests
# need build-std=test until https://github.com/rust3ds/cargo-3ds/pull/42 # need build-std=test until https://github.com/rust3ds/cargo-3ds/pull/42
run: cargo 3ds test --doc -Zbuild-std=std,test run: cargo 3ds test --doc -Zbuild-std=std,test
- name: Run lib + integration tests
uses: ./.github/actions/citra
with:
executable: ./target/armv6k-nintendo-3ds/debug/deps/*.elf
# TODO: run doc tests. We might be able to do something with e.g.
# cargo's "runner" configuration, but it seems we also need a test
# runtime and stuff for that to work.

38
Dockerfile

@ -0,0 +1,38 @@
FROM buildpack-deps:latest as builder
ARG CITRA_CHANNEL=nightly
ARG CITRA_RELEASE=1962
WORKDIR /tmp
COPY ./docker/download_citra.sh /usr/local/bin/download_citra
RUN apt-get update -y && apt-get install -y jq
RUN download_citra ${CITRA_CHANNEL} ${CITRA_RELEASE}
RUN wget https://apt.devkitpro.org/install-devkitpro-pacman && \
chmod +x ./install-devkitpro-pacman && \
yes | /tmp/install-devkitpro-pacman
RUN dkp-pacman -S --noconfirm \
devkitARM-gdb \
libctru
FROM ubuntu:latest
RUN apt-get update -y && \
apt-get install -y \
libswscale5 \
libsdl2-2.0-0 \
libavformat58 \
libavfilter7 \
xvfb
COPY --from=builder /opt/devkitpro /opt/devkitpro
ENV PATH=/opt/devkitpro/devkitARM/bin:${PATH}
COPY --from=builder /tmp/citra.AppImage /usr/local/bin/citra
COPY ./docker/sdl2-config.ini /root/.config/citra-emu/
COPY ./docker/test-runner.gdb /app/
COPY ./docker/entrypoint.sh /app/
WORKDIR /app
ENTRYPOINT [ "/app/entrypoint.sh" ]

39
README.md

@ -3,35 +3,14 @@
A set of tools for running automated Rust tests against Citra (3DS emulator). A set of tools for running automated Rust tests against Citra (3DS emulator).
## Usage ## Components
`./run.sh 3DSX_FILE` * `test-runner`: a Rust crate for writing tests for 3DS homebrew
* `Dockerfile`: builds a container for running test executables with Citra.
* GitHub Actions:
* `.github/actions/setup`: action for setting up the Rust 3DS toolchain in
workflows
* `.github/actions/citra`: action for running test executables with Citra in
workflows
## Goals <!-- TODO: usage section for github actions -->
* Docker container for manually running tests against Citra
* GitHub Action for running automated tests
* Rust testing framework (custom runner) for use with the 3ds
* (maybe) Acceptance testing framework or glue for one?
## Workflow / Notes
1. Build a test executable (type tbd)
1. `citra-emu` container: bind-mount test executable and choose it
1. `driver` container perform input / output as needed for test, via VNC
* possible extension: `3dslink -s` to get actual stdout/stderr (return code?)
* acceptance testing of images, hopefully via screenshot
## To do work
* [ ] Reorganize docker build files vs runtime files a bit
* [ ] Make this repo useable as a github action
* [ ] Run itself as part of CI? I guess?
* [ ] Simpler user-run workflow:
* Ideally, a single command to spin everything up, build + load a 3dsx and run a vdo script.
* Maybe cargo args passed in as environment variable or something?
* [ ] Clearly defined dependencies + use cases:
* Should this be usable without Rust?
* Is docker the only real dependency?
* Does this need a separate binary, or can we just use native cargo test
capabilities?

6
docker/download_citra.sh

@ -9,7 +9,7 @@ RELEASE_API="https://api.github.com/repos/citra-emu/citra-${CITRA_CHANNEL}/relea
curl "${RELEASE_API}" | curl "${RELEASE_API}" |
jq --raw-output '.assets[].browser_download_url' | jq --raw-output '.assets[].browser_download_url' |
grep -E 'citra-linux-.*.tar.xz' | grep -E 'citra-linux-.*.tar.gz' |
xargs wget -O citra-linux.tar.xz xargs wget -O citra-linux.tar.gz
tar --strip-components 1 -xvf citra-linux.tar.xz tar --strip-components 1 -xvf citra-linux.tar.gz

26
docker/entrypoint.sh

@ -0,0 +1,26 @@
#!/bin/bash
# Clean up child processes on exit: https://stackoverflow.com/a/2173421/14436105
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
ls -lart $@
ERRS=0
# shellcheck disable=SC2068
for EXE in $@; do
VIDEO_OUT="$(dirname "$EXE")/$(basename "$EXE" .elf)_out.webm"
# colored logs would be nice, but we can always just grab the plaintext log file
xvfb-run citra --appimage-extract-and-run --dump-video="$VIDEO_OUT" "$EXE" &>/dev/null &
# Citra takes a little while to start up, so wait a little before we try to connect
sleep 3
arm-none-eabi-gdb --batch-silent --command /app/test-runner.gdb "$EXE"
STATUS=$?
if [ $STATUS -ne 0 ]; then
ERRS=$((ERRS + 1))
fi
done
exit $ERRS

338
docker/sdl2-config.ini

@ -1,342 +1,12 @@
[Controls]
# The input devices and parameters for each 3DS native input
# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
# for button input, the following devices are available:
# - "keyboard" (default) for keyboard input. Required parameters:
# - "code": the code of the key to bind
# - "sdl" for joystick input using SDL. Required parameters:
# - "joystick": the index of the joystick to bind
# - "button"(optional): the index of the button to bind
# - "hat"(optional): the index of the hat to bind as direction buttons
# - "axis"(optional): the index of the axis to bind
# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right"
# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
# triggered if the axis value crosses
# - "direction"(only used for axis): "+" means the button is triggered when the axis value
# is greater than the threshold; "-" means the button is triggered when the axis value
# is smaller than the threshold
button_a=
button_b=
button_x=
button_y=
button_up=
button_down=
button_left=
button_right=
button_l=
button_r=
button_start=
button_select=
button_debug=
button_gpio14=
button_zl=
button_zr=
button_home=
# for analog input, the following devices are available:
# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
# - "up", "down", "left", "right": sub-devices for each direction.
# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
# - "modifier": sub-devices as a modifier.
# - "modifier_scale": a float number representing the applied modifier scale to the analog input.
# Must be in range of 0.0-1.0. Defaults to 0.5
# - "sdl" for joystick input using SDL. Required parameters:
# - "joystick": the index of the joystick to bind
# - "axis_x": the index of the axis to bind as x-axis (default to 0)
# - "axis_y": the index of the axis to bind as y-axis (default to 1)
circle_pad=
c_stick=
# for motion input, the following devices are available:
# - "motion_emu" (default) for emulating motion input from mouse input. Required parameters:
# - "update_period": update period in milliseconds (default to 100)
# - "sensitivity": the coefficient converting mouse movement to tilting angle (default to 0.01)
# - "tilt_clamp": the max value of the tilt angle in degrees (default to 90)
# - "cemuhookudp" reads motion input from a udp server that uses cemuhook's udp protocol
motion_device=
# for touch input, the following devices are available:
# - "emu_window" (default) for emulating touch input from mouse input to the emulation window. No parameters required
# - "cemuhookudp" reads touch input from a udp server that uses cemuhook's udp protocol
# - "min_x", "min_y", "max_x", "max_y": defines the udp device's touch screen coordinate system
touch_device=
# Most desktop operating systems do not expose a way to poll the motion state of the controllers
# so as a way around it, cemuhook created a udp client/server protocol to broadcast the data directly
# from a controller device to the client program. Citra has a client that can connect and read
# from any cemuhook compatible motion program.
# IPv4 address of the udp input server (Default "127.0.0.1")
udp_input_address=
# Port of the udp input server. (Default 26760)
udp_input_port=
# The pad to request data on. Should be between 0 (Pad 1) and 3 (Pad 4). (Default 0)
udp_pad_index=
[Core]
# Whether to use the Just-In-Time (JIT) compiler for CPU emulation
# 0: Interpreter (slow), 1 (default): JIT (fast)
use_cpu_jit =
# Change the Clock Frequency of the emulated 3DS CPU.
# Underclocking can increase the performance of the game at the risk of freezing.
# Overclocking may fix lag that happens on console, but also comes with the risk of freezing.
# Range is any positive integer (but we suspect 25 - 400 is a good idea) Default is 100
cpu_clock_percentage =
[Renderer]
# Whether to render using GLES or OpenGL
# 0 (default): OpenGL, 1: GLES
use_gles =
# Whether to use software or hardware rendering.
# 0: Software, 1 (default): Hardware
use_hw_renderer =
# Whether to use hardware shaders to emulate 3DS shaders
# 0: Software, 1 (default): Hardware
use_hw_shader =
# Whether to use separable shaders to emulate 3DS shaders (macOS only)
# 0: Off (Default), 1 : On
separable_shader =
# Whether to use accurate multiplication in hardware shaders
# 0: Off (Faster, but causes issues in some games) 1: On (Default. Slower, but correct)
shaders_accurate_mul =
# Whether to use the Just-In-Time (JIT) compiler for shader emulation
# 0: Interpreter (slow), 1 (default): JIT (fast)
use_shader_jit =
# Forces VSync on the display thread. Usually doesn't impact performance, but on some drivers it can
# so only turn this off if you notice a speed difference.
# 0: Off, 1 (default): On
use_vsync_new =
# Reduce stuttering by storing and loading generated shaders to disk
# 0: Off, 1 (default. On)
use_disk_shader_cache =
# Resolution scale factor
# 0: Auto (scales resolution to window size), 1: Native 3DS screen resolution, Otherwise a scale
# factor for the 3DS resolution
resolution_factor =
# Texture filter name
texture_filter_name =
# Limits the speed of the game to run no faster than this value as a percentage of target speed.
# Will not have an effect if unthrottled is enabled.
# 5 - 995: Speed limit as a percentage of target game speed. 0 for unthrottled. 100 (default)
frame_limit =
# Overrides the frame limiter to use frame_limit_alternate instead of frame_limit.
# 0: Off (default), 1: On
use_frame_limit_alternate =
# Alternate speed limit to be used instead of frame_limit if use_frame_limit_alternate is enabled
# 5 - 995: Speed limit as a percentage of target game speed. 0 for unthrottled. 200 (default)
frame_limit_alternate =
# The clear color for the renderer. What shows up on the sides of the bottom screen.
# Must be in range of 0.0-1.0. Defaults to 0.0 for all.
bg_red = 0.5
bg_blue = 0.5
bg_green = 0.5
# Whether and how Stereoscopic 3D should be rendered
# 0 (default): Off, 1: Side by Side, 2: Anaglyph, 3: Interlaced, 4: Reverse Interlaced
render_3d =
# Change 3D Intensity
# 0 - 100: Intensity. 0 (default)
factor_3d =
# The name of the post processing shader to apply.
# Loaded from shaders if render_3d is off or side by side.
# Loaded from shaders/anaglyph if render_3d is anaglyph
pp_shader_name =
# Whether to enable linear filtering or not
# This is required for some shaders to work correctly
# 0: Nearest, 1 (default): Linear
filter_mode =
[Layout]
# Layout for the screen inside the render window.
# 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen, 3: Side by Side
layout_option =
# Toggle custom layout (using the settings below) on or off.
# 0 (default): Off, 1: On
custom_layout =
# Screen placement when using Custom layout option
# 0x, 0y is the top left corner of the render window.
custom_top_left =
custom_top_top =
custom_top_right =
custom_top_bottom =
custom_bottom_left =
custom_bottom_top =
custom_bottom_right =
custom_bottom_bottom =
# Swaps the prominent screen with the other screen.
# For example, if Single Screen is chosen, setting this to 1 will display the bottom screen instead of the top screen.
# 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent
swap_screen =
# Toggle upright orientation, for book style games.
# 0 (default): Off, 1: On
upright_screen =
# Dumps textures as PNG to dump/textures/[Title ID]/.
# 0 (default): Off, 1: On
dump_textures =
# Reads PNG files from load/textures/[Title ID]/ and replaces textures.
# 0 (default): Off, 1: On
custom_textures =
# Loads all custom textures into memory before booting.
# 0 (default): Off, 1: On
preload_textures =
[Audio]
# Whether or not to enable DSP LLE
# 0 (default): No, 1: Yes
enable_dsp_lle =
# Whether or not to run DSP LLE on a different thread
# 0 (default): No, 1: Yes
enable_dsp_lle_thread =
# Which audio output engine to use.
# auto (default): Auto-select, null: No audio output, sdl2: SDL2 (if available)
output_engine =
# Whether or not to enable the audio-stretching post-processing effect.
# This effect adjusts audio speed to match emulation speed and helps prevent audio stutter,
# at the cost of increasing audio latency.
# 0: No, 1 (default): Yes
enable_audio_stretching =
# Which audio device to use.
# auto (default): Auto-select
output_device =
# Output volume.
# 1.0 (default): 100%, 0.0; mute
volume =
[Data Storage]
# Whether to create a virtual SD card.
# 1 (default): Yes, 0: No
use_virtual_sd =
# The path of the virtual SD card directory.
# empty (default) will use the user_path
sdmc_directory =
# The path of NAND directory.
# empty (default) will use the user_path
nand_directory =
[System]
# The system model that Citra will try to emulate
# 0: Old 3DS, 1: New 3DS (default)
is_new_3ds =
# The system region that Citra will use during emulation
# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
region_value =
# The clock to use when citra starts
# 0: System clock (default), 1: fixed time
init_clock =
# Time used when init_clock is set to fixed_time in the format %Y-%m-%d %H:%M:%S
# set to fixed time. Default 2000-01-01 00:00:01
# Note: 3DS can only handle times later then Jan 1 2000
init_time =
[Camera]
# Which camera engine to use for the right outer camera
# blank (default): a dummy camera that always returns black image
camera_outer_right_name =
# A config string for the right outer camera. Its meaning is defined by the camera engine
camera_outer_right_config =
# The image flip to apply
# 0: None (default), 1: Horizontal, 2: Vertical, 3: Reverse
camera_outer_right_flip =
# ... for the left outer camera
camera_outer_left_name =
camera_outer_left_config =
camera_outer_left_flip =
# ... for the inner camera
camera_inner_name =
camera_inner_config =
camera_inner_flip =
[Miscellaneous] [Miscellaneous]
# A filter which removes logs below a certain logging level.
# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
log_filter = *:Info Debug.Emulated:Debug log_filter = *:Info Debug.Emulated:Debug
# Kernel:Debug Service.APT:Trace
[Debugging] [Debugging]
# Record frame time data, can be found in the log directory. Boolean value use_gdbstub=true
record_frame_times = gdbstub_port=4000
# Port for listening to GDB connections.
use_gdbstub=false
gdbstub_port=24689
# To LLE a service module add "LLE\<module name>=true"
[WebService] [WebService]
# Whether or not to enable telemetry enable_telemetry = 0
# 0: No, 1 (default): Yes
enable_telemetry = 1
# URL for Web API
web_api_url = https://api.citra-emu.org
# Username and token for Citra Web Service
# See https://profile.citra-emu.org/ for more info
citra_username =
citra_token =
[Video Dumping] [Video Dumping]
# Format of the video to output, default: webm output_format = webm
output_format =
# Options passed to the muxer (optional)
# This is a param package, format: [key1]:[value1],[key2]:[value2],...
format_options =
# Video encoder used, default: libvpx-vp9
video_encoder = libvpx-vp9
# Options passed to the video codec (optional)
video_encoder_options = quality:realtime,speed:6,tile-columns:4,frame-parallel:1,threads:8,row-mt:1
# Video bitrate, default: 2500000
video_bitrate =
# Audio encoder used, default: libvorbis
audio_encoder = libvorbis
# Options passed to the audio codec (optional)
audio_encoder_options =
# Audio bitrate, default: 64000
audio_bitrate =

7
docker/test-runner.gdb

@ -2,15 +2,12 @@
# or should this be `_exit` ? # or should this be `_exit` ?
break __ctru_exit break __ctru_exit
commands commands
# TODO: needed?
continue
# ARM calling convention will put the exit code in r0 when __ctru_exit is called. # ARM calling convention will put the exit code in r0 when __ctru_exit is called.
# Just tell GDB to exit with the same code, since it doesn't get passed back when # Just tell GDB to exit with the same code, since it doesn't get passed back when
# the program exits # the program exits
quit $r0 quit $r0
end end
# TODO: parametrize or pass as command line arg instead target extended-remote :4000
target extended-remote 192.168.0.167:4003
# target extended-remote :4000
continue continue
quit

Loading…
Cancel
Save