Browse Source

Merge pull request #8 from ian-h-chamberlain/fix/refactor-simplify-action

pull/10/head
Ian Chamberlain 1 year ago committed by GitHub
parent
commit
22522b428c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      .github/actions/citra/action.yml
  2. 63
      .github/workflows/ci.yml
  3. 34
      Dockerfile
  4. 61
      README.md
  5. 50
      docker/entrypoint.sh
  6. 0
      run-tests/.dockerignore
  7. 54
      run-tests/Dockerfile
  8. 61
      run-tests/action.yml
  9. 2
      run-tests/docker/download_citra.sh
  10. 68
      run-tests/docker/entrypoint.sh
  11. 0
      run-tests/docker/sdl2-config.ini
  12. 0
      run-tests/docker/test-runner.gdb
  13. 14
      setup/action.yml
  14. 7
      test-runner/Cargo.toml
  15. 0
      test-runner/src/console.rs
  16. 0
      test-runner/src/gdb.rs
  17. 0
      test-runner/src/lib.rs
  18. 0
      test-runner/src/socket.rs
  19. 0
      test-runner/tests/integration.rs

12
.github/actions/citra/action.yml

@ -1,12 +0,0 @@ @@ -1,12 +0,0 @@
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 }}

63
.github/workflows/ci.yml

@ -9,10 +9,6 @@ on: @@ -9,10 +9,6 @@ on:
- main
workflow_dispatch:
env:
# actions-rust-lang/setup-rust-toolchain sets some default RUSTFLAGS, which we don't want to use
RUSTFLAGS: ""
jobs:
lint:
strategy:
@ -24,18 +20,19 @@ jobs: @@ -24,18 +20,19 @@ jobs:
container: devkitpro/devkitarm
steps:
- name: Checkout branch
uses: actions/checkout@v2
uses: actions/checkout@v4
- uses: ./.github/actions/setup
- uses: ./setup
with:
toolchain: ${{ matrix.toolchain }}
- name: Check formatting
working-directory: test-runner
run: cargo fmt --all --verbose -- --check
- name: Run clippy
# We have to build the test crate here since it's not included in build-std by default
run: cargo 3ds clippy -Zbuild-std=std,test --color=always --verbose --all-targets
working-directory: test-runner
run: cargo 3ds clippy --color=always --verbose --all-targets
test:
strategy:
@ -47,38 +44,44 @@ jobs: @@ -47,38 +44,44 @@ jobs:
continue-on-error: ${{ matrix.toolchain == 'nightly' }}
runs-on: ubuntu-latest
container: devkitpro/devkitarm
container:
image: devkitpro/devkitarm
volumes:
# So the test action can `docker run` the runner:
- '/var/run/docker.sock:/var/run/docker.sock'
steps:
- name: Checkout branch
uses: actions/checkout@v3
uses: actions/checkout@v4
- uses: ./.github/actions/setup
- uses: ./setup
with:
toolchain: ${{ matrix.toolchain }}
- name: Build lib tests
run: cargo 3ds test --no-run --lib
- name: Build integration tests
run: cargo 3ds test --no-run --test integration
- name: Build doc tests
run: cargo 3ds test --no-run --doc
- name: Run lib + integration tests
uses: ./.github/actions/citra
- name: Build and run tests (unit + integration)
uses: ./run-tests
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.
working-directory: test-runner
args: -- -v
# TODO(#4): run these suckers
# - name: Build and run doc tests
# # Let's still run doc tests even if lib/integration tests fail:
# if: ${{ !cancelled() }}
# env:
# # This ensures the citra logs and video output gets put in a directory
# # where we can upload as artifacts
# RUSTDOCFLAGS: " --persist-doctests ${{ env.GITHUB_WORKSPACE }}/target/armv6k-nintendo-3ds/debug/doctests"
# uses: ./run-tests
# with:
# working-directory: test-runner
# args: --doc -- -v
- name: Upload citra logs and capture videos
uses: actions/upload-artifact@v3
if: success() || failure()
# We always want to upload artifacts regardless of previous success/failure
if: ${{ !cancelled() }}
with:
name: citra-logs-${{ matrix.toolchain }}
path: |
target/armv6k-nintendo-3ds/debug/deps/*.txt
target/armv6k-nintendo-3ds/debug/deps/*.webm
target/armv6k-nintendo-3ds/debug/**/*.txt
target/armv6k-nintendo-3ds/debug/**/*.webm

34
Dockerfile

@ -1,34 +0,0 @@ @@ -1,34 +0,0 @@
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}
FROM devkitpro/devkitarm:latest as devkitarm
FROM ubuntu:latest
RUN --mount=type=cache,sharing=locked,target=/var/cache/apt \
apt-get update -y && \
apt-get install -y \
libswscale5 \
libsdl2-2.0-0 \
libavformat58 \
libavfilter7 \
xvfb
COPY --from=devkitarm /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 /app/
COPY ./docker/test-runner.gdb /app/
COPY ./docker/entrypoint.sh /app/
WORKDIR /app
ENTRYPOINT [ "/app/entrypoint.sh" ]

61
README.md

@ -6,11 +6,60 @@ A set of tools for running automated Rust tests against Citra (3DS emulator). @@ -6,11 +6,60 @@ A set of tools for running automated Rust tests against Citra (3DS emulator).
## Components
* `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
* `setup`: action for setting up the Rust 3DS toolchain in workflows
* `run-tests`: action for running test executables with Citra in workflows
<!-- TODO: usage section for github actions -->
## Usage
First the test runner to your crate:
```sh
cargo add --dev test-runner --git https://github.com/ian-h-chamberlain/test-runner-3ds
```
In `lib.rs` and any integration test files:
```rs
#![feature(custom_test_frameworks)]
#![test_runner(test_runner::run_gdb)]
```
Then use the `setup` and `run-tests` actions in your github workflow. This
example shows the default value for each of the inputs:
```yml
jobs:
test:
runs-on: ubuntu-latest
container:
image: devkitpro/devkitarm
volumes:
# This is required so the test action can `docker run` the runner:
- '/var/run/docker.sock:/var/run/docker.sock'
# This is required so doctest artifacts are accessible to the action:
- '/tmp:/tmp'
steps:
- name: Checkout branch
uses: actions/checkout@v4
- name: Setup Rust3DS toolchain
uses: ian-h-chamberlain/test-runner-3ds/setup@v1
with:
# Optionally use a more specific nightly toolchain here if desired
toolchain: nightly
- name: Build and run tests
uses: ian-h-chamberlain/test-runner-3ds/run-tests@v1
with:
# Optionally add arguments to pass to `cargo 3ds test`
args: ''
# Optionally set the name of the built test-runner docker image
runner-image: test-runner-3ds
# Optionally change to a given directory before running tests. Note
# that this should use the environment variable ${GITHUB_WORKSPACE}
# rather than ${{ github.workspace }} to avoid the issue described in
# https://github.com/actions/runner/issues/2058
working-directory: ${GITHUB_WORKSPACE}
```

50
docker/entrypoint.sh

@ -1,50 +0,0 @@ @@ -1,50 +0,0 @@
#!/bin/bash
# Clean up child processes on exit: https://stackoverflow.com/a/2173421/14436105
trap "pkill -P $$" EXIT INT TERM
mkdir -p ~/.config/citra-emu
cp /app/sdl2-config.ini ~/.config/citra-emu
# For some reason, log file is only written when this dir already exists,
# but it is only created after the first run of citra (our only run, in the container)
mkdir -p ~/.local/share/citra-emu/
ERRS=0
# shellcheck disable=SC2068
for EXE in $@; do
VIDEO_OUT="$(dirname "$EXE")/$(basename "$EXE" .elf)_capture.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 &
PID=$!
# Citra takes a little while to start up, so wait a little before we try to connect
sleep 3
arm-none-eabi-gdb --silent --batch-silent --command /app/test-runner.gdb "$EXE"
STATUS=$?
if [ $STATUS -ne 0 ]; then
echo >&2 "FAILED (exit status $STATUS): $EXE"
ERRS=$(( ERRS + 1 ))
fi
kill -INT $PID &>/dev/null
sleep 1
if kill -0 $PID &>/dev/null; then
kill -KILL $PID &>/dev/null
fi
CITRA_LOG=~/.local/share/citra-emu/log/citra_log.txt
CITRA_LOG_OUT="$(dirname "$EXE")/$(basename "$EXE" .elf)_citra_log.txt"
if test -f "$CITRA_LOG"; then
cp "$CITRA_LOG" "$CITRA_LOG_OUT"
else
echo "WARNING: citra log not found"
fi
done
exit $ERRS

0
.dockerignore → run-tests/.dockerignore

54
run-tests/Dockerfile

@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
FROM buildpack-deps:latest as builder
WORKDIR /tmp
COPY ./docker/download_citra.sh /usr/local/bin/download_citra
RUN apt-get update -y && apt-get install -y jq
ARG CITRA_CHANNEL=nightly
ARG CITRA_RELEASE=1995
RUN download_citra ${CITRA_CHANNEL} ${CITRA_RELEASE}
FROM devkitpro/devkitarm:latest as devkitarm
# For some reason, citra isn't always happy when you try to run it for the first time,
# so we build a simple dummy program to force it to create its directory structure
RUN cd /opt/devkitpro/examples/3ds/graphics/printing/hello-world && \
echo 'int main(int, char**) {}' > source/main.c && \
make && \
mv hello-world.3dsx /tmp/
FROM ubuntu:latest
RUN --mount=type=cache,sharing=locked,target=/var/cache/apt \
apt-get update -y && \
apt-get install -y \
libswscale5 \
libsdl2-2.0-0 \
libavformat58 \
libavfilter7 \
xvfb
COPY --from=devkitarm /opt/devkitpro /opt/devkitpro
# There's no way to copy ENV values from other stages properly:
# https://github.com/moby/moby/issues/37345
# Luckily in this case we know exactly what the values should be:
ENV DEVKITPRO=/opt/devkitpro
ENV DEVKITARM=${DEVKITPRO}/devkitARM
ENV PATH=${DEVKITARM}/bin:${PATH}
COPY --from=builder /tmp/citra.AppImage /usr/local/bin/citra
COPY --from=devkitarm /tmp/hello-world.3dsx /tmp/
# We run citra once before copying our config file, so it should create its
# necessary directory structure and run once with defaults
RUN xvfb-run citra --appimage-extract-and-run /tmp/hello-world.3dsx; \
rm -f /tmp/hello-world.3dsx
# Initial run seems to miss this one directory so just make it manually
RUN mkdir -p /root/.local/share/citra-emu/log
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" ]

61
run-tests/action.yml

@ -0,0 +1,61 @@ @@ -0,0 +1,61 @@
name: Cargo 3DS Test
description: >
Run `cargo 3ds test` executables using Citra. Note that to use this action,
you must mount `/var/run/docker.sock:/var/run/docker.sock` and `/tmp:/tmp` into
the container so that the runner image can be built and doctest artifacts can
be found, respectively.
inputs:
args:
description: Extra arguments to pass to `cargo 3ds test`
required: false
default: ''
runner-image:
description: The name of the container image to build for running tests in
required: false
default: test-runner-3ds
working-directory:
description: Change to this directory before running tests. Defaults to $GITHUB_WORKSPACE
required: false
default: ${GITHUB_WORKSPACE}
runs:
using: composite
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build test-runner image
uses: docker/build-push-action@v4
with:
context: ${{ github.action_path }}
tags: ${{ inputs.runner-image }}:latest
push: false
load: true
- name: Ensure docker is installed in the container
shell: bash
run: apt-get update -y && apt-get install docker.io -y
- name: Run cargo 3ds test
shell: bash
# Set a custom runner for `cargo test` commands to use.
# Use ${GITHUB_WORKSPACE} due to
# https://github.com/actions/runner/issues/2058, which also means
# we have to export this instead of using the env: key
run: |
cd ${{ inputs.working-directory }}
export CARGO_TARGET_ARMV6K_NINTENDO_3DS_RUNNER="
docker run --rm
-v ${{ runner.temp }}:${{ runner.temp }}
-v ${{ github.workspace }}/target:/app/target
-v ${{ github.workspace }}:${GITHUB_WORKSPACE}
${{ inputs.runner-image }}:latest"
env
cargo 3ds -v test ${{ inputs.args }}
env:
# Ensure that doctests get built into a path which is mounted on the host
# as well as in this container (via the bind mount in the RUNNER command)
TMPDIR: ${{ runner.temp }}

2
docker/download_citra.sh → run-tests/docker/download_citra.sh

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

68
run-tests/docker/entrypoint.sh

@ -0,0 +1,68 @@ @@ -0,0 +1,68 @@
#!/bin/bash
# Uncomment for debugging the action itself. Maybe consider a job summary or
# grouping the output, to keep this stuff visible but make it simpler to use:
# https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions
# set -x
function cleanup_jobs() {
# shellcheck disable=SC2317 # Unreachable because it's only used in trap
if [ -n "$(jobs -p)" ]; then
sleep 5 &
wait -n
# shellcheck disable=SC2046 # We want to expand jobs here and for `wait`
kill -9 $(jobs -p)
# shellcheck disable=SC2046
wait $(jobs -p) &>/dev/null
fi
}
trap cleanup_jobs EXIT
EXE_ELF=$1
EXE_3DSX="$(dirname "$EXE")/$(basename "$EXE" .elf).3dsx"
EXE_TO_RUN="$EXE_ELF"
if [ -f "$EXE_3DSX" ]; then
echo >&2 "Found $(basename "$EXE_3DSX"), it will be run instead of $(basename "$EXE_ELF")"
EXE_TO_RUN="$EXE_3DSX"
fi
VIDEO_OUT="$(dirname "$EXE_ELF")/$(basename "$EXE_ELF" .elf)_capture.webm"
CITRA_LOG_DIR=~/.local/share/citra-emu/log
CITRA_OUT="$CITRA_LOG_DIR/citra_output.txt"
xvfb-run --auto-servernum \
citra \
--appimage-extract-and-run \
--dump-video="$VIDEO_OUT" \
"$EXE_TO_RUN" \
&>"$CITRA_OUT" &
CITRA_PID=$!
# Citra takes a little while to start up, so wait a little before we try to connect
sleep 5
arm-none-eabi-gdb --silent --batch-silent --command /app/test-runner.gdb "$EXE_ELF"
STATUS=$?
kill $CITRA_PID
cleanup_jobs
CITRA_LOG="$CITRA_LOG_DIR/citra_log.txt"
for f in "$CITRA_LOG" "$CITRA_OUT"; do
OUT="$(dirname "$EXE_ELF")/$(basename "$EXE_ELF" .elf)_$(basename "$f")"
if test -f "$f"; then
cp "$f" "$OUT"
if [ $STATUS -ne 0 ]; then
echo >&2 "$(basename $f) copied to $OUT"
fi
else
echo >&2 "WARNING: $(basename "$f") not found"
fi
done
exit $STATUS

0
docker/sdl2-config.ini → run-tests/docker/sdl2-config.ini

0
docker/test-runner.gdb → run-tests/docker/test-runner.gdb

14
.github/actions/setup/action.yml → setup/action.yml

@ -10,14 +10,6 @@ inputs: @@ -10,14 +10,6 @@ inputs:
runs:
using: composite
steps:
# https://github.com/nektos/act/issues/917#issuecomment-1074421318
- if: ${{ env.ACT }}
shell: bash
name: Hack container for local development
run: |
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs
- name: Setup default Rust toolchain
# Use this helper action so we get matcher support
# https://github.com/actions-rust-lang/setup-rust-toolchain/pull/15
@ -25,11 +17,13 @@ runs: @@ -25,11 +17,13 @@ runs:
with:
components: clippy, rustfmt, rust-src
toolchain: ${{ inputs.toolchain }}
cache: false # We set up our own cache manually in the next step
rustflags: ""
- name: Set up Rust cache
uses: Swatinem/rust-cache@v2
with:
shared-key: rust3ds
cache-on-failure: true
- name: Install build tools for host
shell: bash
@ -38,7 +32,7 @@ runs: @@ -38,7 +32,7 @@ runs:
- name: Install cargo-3ds
shell: bash
# TODO: replace with crates.io version once published
run: cargo install --git https://github.com/rust3ds/cargo-3ds --branch feature/verbose-flag
run: cargo install --locked --git https://github.com/rust3ds/cargo-3ds
- name: Set PATH to include devkitARM
shell: bash

7
Cargo.toml → test-runner/Cargo.toml

@ -12,3 +12,10 @@ socket = [] @@ -12,3 +12,10 @@ socket = []
ctru-rs = { git = "https://github.com/rust3ds/ctru-rs" }
ctru-sys = { git = "https://github.com/rust3ds/ctru-rs" }
libc = "0.2.147"
[patch."https://github.com/ian-h-chamberlain/test-runner-3ds"]
test-runner = { path = "." }
# Future-proofing for a rename + org move:
[patch."https://github.com/rust3ds/test-runner"]
test-runner = { path = "." }

0
src/console.rs → test-runner/src/console.rs

0
src/gdb.rs → test-runner/src/gdb.rs

0
src/lib.rs → test-runner/src/lib.rs

0
src/socket.rs → test-runner/src/socket.rs

0
tests/integration.rs → test-runner/tests/integration.rs

Loading…
Cancel
Save