From 553a625c910dc4e18a6f062b9eb418fa8396232e Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sat, 25 Jun 2022 22:09:57 +0100 Subject: [PATCH 01/13] Mkae it possible to run tests in target crates (and add a basic tests in hat-wizard) --- agb/Cargo.toml | 1 + agb/src/agb_alloc/block_allocator.rs | 3 +- agb/src/agb_alloc/mod.rs | 2 +- agb/src/lib.rs | 26 ++++++++--- .../the-hat-chooses-the-wizard/Cargo.toml | 3 ++ .../the-hat-chooses-the-wizard/src/main.rs | 45 ++++++++++++++++++- 6 files changed, 70 insertions(+), 10 deletions(-) diff --git a/agb/Cargo.toml b/agb/Cargo.toml index 403da505..2b03d9a9 100644 --- a/agb/Cargo.toml +++ b/agb/Cargo.toml @@ -19,6 +19,7 @@ debug = true default = [] freq18157 = ["agb_sound_converter/freq18157"] freq32768 = ["agb_sound_converter/freq32768"] +testing = [] [dependencies] bitflags = "1" diff --git a/agb/src/agb_alloc/block_allocator.rs b/agb/src/agb_alloc/block_allocator.rs index a3e51cbd..a601131e 100644 --- a/agb/src/agb_alloc/block_allocator.rs +++ b/agb/src/agb_alloc/block_allocator.rs @@ -58,7 +58,8 @@ impl BlockAllocator { } } - #[cfg(test)] + #[doc(hidden)] + #[cfg(any(test, feature = "testing"))] pub unsafe fn number_of_blocks(&self) -> u32 { free(|key| { let mut state = self.state.borrow(key).borrow_mut(); diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 7357c17a..ecffeb72 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -42,7 +42,7 @@ static GLOBAL_ALLOC: BlockAllocator = unsafe { }) }; -#[cfg(test)] +#[cfg(any(test, feature = "testing"))] pub unsafe fn number_of_blocks() -> u32 { GLOBAL_ALLOC.number_of_blocks() } diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 97f1159c..c1647fc4 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -1,9 +1,15 @@ #![no_std] // This appears to be needed for testing to work -#![cfg_attr(test, no_main)] -#![cfg_attr(test, feature(custom_test_frameworks))] -#![cfg_attr(test, test_runner(crate::test_runner::test_runner))] -#![cfg_attr(test, reexport_test_harness_main = "test_main")] +#![cfg_attr(any(test, feature = "testing"), no_main)] +#![cfg_attr(any(test, feature = "testing"), feature(custom_test_frameworks))] +#![cfg_attr( + any(test, feature = "testing"), + test_runner(crate::test_runner::test_runner) +)] +#![cfg_attr( + any(test, feature = "testing"), + reexport_test_harness_main = "test_main" +)] #![feature(alloc_error_handler)] #![warn(clippy::all)] #![deny(clippy::must_use_candidate)] @@ -183,7 +189,7 @@ pub mod syscall; /// Interactions with the internal timers pub mod timer; -#[cfg(not(test))] +#[cfg(not(any(test, feature = "testing")))] #[panic_handler] #[allow(unused_must_use)] fn panic_implementation(info: &core::panic::PanicInfo) -> ! { @@ -249,8 +255,9 @@ impl Gba { } } -#[cfg(test)] -mod test_runner { +#[cfg(any(test, feature = "testing"))] +#[doc(hidden)] +pub mod test_runner { use super::*; #[doc(hidden)] @@ -320,8 +327,13 @@ mod test_runner { .unwrap(); } + #[cfg(test)] #[entry] fn agb_test_main(gba: Gba) -> ! { + agb_start_tests(gba, test_main); + } + + pub fn agb_start_tests(gba: Gba, test_main: impl Fn()) -> ! { unsafe { TEST_GBA = Some(gba) }; test_main(); #[allow(clippy::empty_loop)] diff --git a/examples/the-hat-chooses-the-wizard/Cargo.toml b/examples/the-hat-chooses-the-wizard/Cargo.toml index bc9e160c..4c41d7d3 100644 --- a/examples/the-hat-chooses-the-wizard/Cargo.toml +++ b/examples/the-hat-chooses-the-wizard/Cargo.toml @@ -9,6 +9,9 @@ edition = "2018" [dependencies] agb = { version = "0.9.2", path = "../../agb" } +[dev-dependencies] +agb = { version = "0.9.2", path = "../../agb", features = ["testing"] } + [build-dependencies] serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/examples/the-hat-chooses-the-wizard/src/main.rs b/examples/the-hat-chooses-the-wizard/src/main.rs index 80071419..0b458fc5 100644 --- a/examples/the-hat-chooses-the-wizard/src/main.rs +++ b/examples/the-hat-chooses-the-wizard/src/main.rs @@ -1,5 +1,14 @@ #![no_std] #![no_main] +#![cfg_attr(test, feature(custom_test_frameworks))] +#![cfg_attr(test, reexport_test_harness_main = "test_main")] +#![cfg_attr(test, test_runner(agb::test_runner::test_runner))] + +#[cfg(test)] +#[agb::entry] +fn main(mut gba: agb::Gba) -> ! { + agb::test_runner::agb_start_tests(gba, test_main); +} extern crate alloc; @@ -775,8 +784,13 @@ impl<'a, 'b> PlayingLevel<'a, 'b> { } } +#[cfg(not(test))] #[agb::entry] -fn main(mut agb: agb::Gba) -> ! { +fn agb_main(mut gba: agb::Gba) -> ! { + main(gba); +} + +pub fn main(mut agb: agb::Gba) -> ! { let (tiled, mut vram) = agb.display.video.tiled0(); vram.set_background_palettes(tile_sheet::background.palettes); let mut splash_screen = tiled.background(Priority::P0, RegularBackgroundSize::Background32x32); @@ -958,3 +972,32 @@ fn main(mut agb: agb::Gba) -> ! { ); } } + +#[cfg(test)] +mod tests { + use super::*; + use agb::Gba; + + #[test_case] + fn test_ping_pong(_gba: &mut Gba) { + let test_cases = [ + [0, 2, 0], + [0, 7, 0], + [1, 2, 1], + [2, 2, 0], + [3, 2, 1], + [4, 2, 0], + ]; + + for test_case in test_cases { + assert_eq!( + ping_pong(test_case[0], test_case[1]), + test_case[2], + "Expected ping_pong({}, {}) to equal {}", + test_case[0], + test_case[1], + test_case[2], + ); + } + } +} From 9231d1607156301741eae9912100ab38445fa571 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 27 Jun 2022 23:47:49 +0100 Subject: [PATCH 02/13] Add some documentation for the test runner --- agb/src/lib.rs | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/agb/src/lib.rs b/agb/src/lib.rs index c1647fc4..083e5874 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -256,7 +256,45 @@ impl Gba { } #[cfg(any(test, feature = "testing"))] -#[doc(hidden)] +/// *Unstable* support for running tests using `agb` +/// +/// In order to use this, you need to enable the unstable `custom_test_framework` feature and copy-paste +/// the following into the top of your application: +/// +/// ``` +/// #![cfg_attr(test, feature(custom_test_frameworks))] +/// #![cfg_attr(test, reexport_test_harness_main = "test_main")] +/// #![cfg_attr(test, test_runner(agb::test_runner::test_runner))] +/// +/// #[cfg(test)] +/// #[agb::entry] +/// fn main(mut gba: agb::Gba) -> ! { +/// agb::test_runner::agb_start_tests(gba, test_main); +/// } +/// ``` +/// +/// And ensure that your main does not build if testing, so do something similar to: +/// +/// ``` +/// #[cfg(not(test))] +/// #[agb::entry] +/// fn main(mut gba: agb::Gba) -> ! { +/// // ... +/// } +/// ``` +/// +/// With this support, you will be able to write tests which you can run using `mgba-test-runner`. +/// Tests are written using `#[test_case]` rather than `#[test]`. +/// +/// ``` +/// #[test_case] +/// fn test_ping_pong(_gba: &mut Gba) { +/// assert_eq!(1, 1); +/// } +/// ``` +/// +/// You can run the tests using `cargo test`, but it will work better through `mgba-test-runner` by +/// running something along the lines of `CARGO_TARGET_THUMBV4T_NONE_EABI_RUNNER=mgba-test-runner cargo test`. pub mod test_runner { use super::*; From 2e505f968408d36331e094bf872273769e9cb9fc Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sun, 3 Jul 2022 17:52:47 +0100 Subject: [PATCH 03/13] Remove the need for a special test implementation --- agb-macros/src/lib.rs | 14 +++++++++++ agb/src/lib.rs | 25 ++++++++----------- .../the-hat-chooses-the-wizard/src/main.rs | 6 ----- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/agb-macros/src/lib.rs b/agb-macros/src/lib.rs index 46e491ff..87dcaf6b 100644 --- a/agb-macros/src/lib.rs +++ b/agb-macros/src/lib.rs @@ -76,6 +76,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { ); quote!( + #[cfg(not(test))] #[export_name = "main"] #(#attrs)* pub fn #fn_name() -> ! { @@ -83,6 +84,19 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { #(#stmts)* } + + #[cfg(test)] + #[export_name = "main"] + #(#attrs)* + pub fn #fn_name() -> ! { + let mut #argument_name = unsafe { #argument_type ::new_in_entry() }; + + if cfg!(test) { + agb::test_runner::agb_start_tests(#argument_name, test_main); + } else { + #(#stmts)* + } + } ) .into() } diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 083e5874..c14e79c3 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -265,22 +265,12 @@ impl Gba { /// #![cfg_attr(test, feature(custom_test_frameworks))] /// #![cfg_attr(test, reexport_test_harness_main = "test_main")] /// #![cfg_attr(test, test_runner(agb::test_runner::test_runner))] -/// -/// #[cfg(test)] -/// #[agb::entry] -/// fn main(mut gba: agb::Gba) -> ! { -/// agb::test_runner::agb_start_tests(gba, test_main); -/// } /// ``` /// -/// And ensure that your main does not build if testing, so do something similar to: -/// -/// ``` -/// #[cfg(not(test))] -/// #[agb::entry] -/// fn main(mut gba: agb::Gba) -> ! { -/// // ... -/// } +/// And ensure you add agb with the `testing` feature to your `dev-dependencies` +/// ```toml +/// [dev-dependencies] +/// agb = { version = "", features = ["testing"] } /// ``` /// /// With this support, you will be able to write tests which you can run using `mgba-test-runner`. @@ -365,6 +355,13 @@ pub mod test_runner { .unwrap(); } + // needed to fudge the #[entry] below + mod agb { + pub mod test_runner { + pub use super::super::agb_start_tests; + } + } + #[cfg(test)] #[entry] fn agb_test_main(gba: Gba) -> ! { diff --git a/examples/the-hat-chooses-the-wizard/src/main.rs b/examples/the-hat-chooses-the-wizard/src/main.rs index 0b458fc5..ea363269 100644 --- a/examples/the-hat-chooses-the-wizard/src/main.rs +++ b/examples/the-hat-chooses-the-wizard/src/main.rs @@ -4,12 +4,6 @@ #![cfg_attr(test, reexport_test_harness_main = "test_main")] #![cfg_attr(test, test_runner(agb::test_runner::test_runner))] -#[cfg(test)] -#[agb::entry] -fn main(mut gba: agb::Gba) -> ! { - agb::test_runner::agb_start_tests(gba, test_main); -} - extern crate alloc; use agb::{ From 20aebd349a50331a8daa1ce74d9e759efd5c39b5 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sun, 3 Jul 2022 17:54:05 +0100 Subject: [PATCH 04/13] Provide an empty #[entry] in lib.rs --- agb/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/agb/src/lib.rs b/agb/src/lib.rs index c14e79c3..1bb35687 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -365,7 +365,8 @@ pub mod test_runner { #[cfg(test)] #[entry] fn agb_test_main(gba: Gba) -> ! { - agb_start_tests(gba, test_main); + #[allow(clippy::empty_loop)] + loop {} // full implementation provided by the #[entry] } pub fn agb_start_tests(gba: Gba, test_main: impl Fn()) -> ! { From 0111ec43bb6d0993a036b18818331fbceac481af Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sun, 3 Jul 2022 17:55:07 +0100 Subject: [PATCH 05/13] agb_start_tests can now be doc(hidden) --- agb/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 1bb35687..16fb1044 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -369,6 +369,7 @@ pub mod test_runner { loop {} // full implementation provided by the #[entry] } + #[doc(hidden)] pub fn agb_start_tests(gba: Gba, test_main: impl Fn()) -> ! { unsafe { TEST_GBA = Some(gba) }; test_main(); From 208104d036aaa517aa88106c290980db4f1282af Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sun, 3 Jul 2022 18:30:58 +0100 Subject: [PATCH 06/13] Update the template to have testing available --- template/Cargo.toml | 3 +++ template/src/main.rs | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/template/Cargo.toml b/template/Cargo.toml index 7ce1334f..fc99e68b 100644 --- a/template/Cargo.toml +++ b/template/Cargo.toml @@ -9,6 +9,9 @@ edition = "2018" [dependencies] agb = "0.9.2" +[dev-dependencies] +agb = { version = "0.9.2", features = ["testing"] } + [profile.release] panic = "abort" lto = true \ No newline at end of file diff --git a/template/src/main.rs b/template/src/main.rs index 1b6a9fd1..84683dc9 100644 --- a/template/src/main.rs +++ b/template/src/main.rs @@ -9,6 +9,10 @@ // using the #[agb::entry] proc macro. Failing to do so will cause failure in linking // which won't be a particularly clear error message. #![no_main] +// This is required to allow writing tests +#![cfg_attr(test, feature(custom_test_frameworks))] +#![cfg_attr(test, reexport_test_harness_main = "test_main")] +#![cfg_attr(test, test_runner(agb::test_runner::test_runner))] use agb::{display, syscall}; From c15d24ad456643c8dc9231c18901ea5c24e3ce8a Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Sun, 3 Jul 2022 18:34:34 +0100 Subject: [PATCH 07/13] Template agb version needs updating twice now --- release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release.sh b/release.sh index 8a1fe6c5..b1a880b1 100755 --- a/release.sh +++ b/release.sh @@ -46,7 +46,7 @@ fi TAGNAME="v$VERSION" -for PROJECT_TOML_FILE in agb/Cargo.toml agb-*/Cargo.toml; do +for PROJECT_TOML_FILE in agb/Cargo.toml agb-*/Cargo.toml template/Cargo.toml; do DIRECTORY=$(dirname "$PROJECT_TOML_FILE") # Update the version in Cargo.toml From 8cfcda9b541172e33480c212584f0a36b7db58e1 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 5 Jul 2022 20:24:31 +0100 Subject: [PATCH 08/13] Make it harder to use #entry incorrectly --- agb-macros/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb-macros/src/lib.rs b/agb-macros/src/lib.rs index 87dcaf6b..ed27f424 100644 --- a/agb-macros/src/lib.rs +++ b/agb-macros/src/lib.rs @@ -92,7 +92,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { let mut #argument_name = unsafe { #argument_type ::new_in_entry() }; if cfg!(test) { - agb::test_runner::agb_start_tests(#argument_name, test_main); + ::agb::test_runner::agb_start_tests(#argument_name, test_main); } else { #(#stmts)* } From 0f9d83887f0aa11742dd43e8fb33d8c08ca72f97 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 5 Jul 2022 20:24:44 +0100 Subject: [PATCH 09/13] This should now always be included --- examples/the-hat-chooses-the-wizard/src/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/the-hat-chooses-the-wizard/src/main.rs b/examples/the-hat-chooses-the-wizard/src/main.rs index ea363269..133afb4e 100644 --- a/examples/the-hat-chooses-the-wizard/src/main.rs +++ b/examples/the-hat-chooses-the-wizard/src/main.rs @@ -778,7 +778,6 @@ impl<'a, 'b> PlayingLevel<'a, 'b> { } } -#[cfg(not(test))] #[agb::entry] fn agb_main(mut gba: agb::Gba) -> ! { main(gba); From 0b6af6dbd22d237645ead2378b02682aaac9690f Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Tue, 5 Jul 2022 20:31:34 +0100 Subject: [PATCH 10/13] Can't do this as it breaks agb --- agb-macros/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb-macros/src/lib.rs b/agb-macros/src/lib.rs index ed27f424..87dcaf6b 100644 --- a/agb-macros/src/lib.rs +++ b/agb-macros/src/lib.rs @@ -92,7 +92,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { let mut #argument_name = unsafe { #argument_type ::new_in_entry() }; if cfg!(test) { - ::agb::test_runner::agb_start_tests(#argument_name, test_main); + agb::test_runner::agb_start_tests(#argument_name, test_main); } else { #(#stmts)* } From 8d08f18349a7152702e22d41940d47540d4732d1 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 25 Jul 2022 22:46:51 +0100 Subject: [PATCH 11/13] Fix shellcheck issues --- mgba-test-runner/build-mgba.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mgba-test-runner/build-mgba.sh b/mgba-test-runner/build-mgba.sh index 2131535d..6efa2f9c 100644 --- a/mgba-test-runner/build-mgba.sh +++ b/mgba-test-runner/build-mgba.sh @@ -1,22 +1,22 @@ -#!/bin/bash +#!/usr/bin/env bash MGBA_VERSION=$1 OUT_DIRECTORY=$2 CURRENT_DIRECTORY=$(pwd) -cd ${OUT_DIRECTORY} +cd "${OUT_DIRECTORY}" || exit if [[ -f libmgba-cycle.a ]]; then exit 0 fi -curl -L https://github.com/mgba-emu/mgba/archive/refs/tags/${MGBA_VERSION}.tar.gz -o mgba-${MGBA_VERSION}.tar.gz -tar -xvf mgba-${MGBA_VERSION}.tar.gz -cd mgba-${MGBA_VERSION} +curl -L "https://github.com/mgba-emu/mgba/archive/refs/tags/${MGBA_VERSION}.tar.gz" -o "mgba-${MGBA_VERSION}.tar.gz" +tar -xvf "mgba-${MGBA_VERSION}.tar.gz" +cd "mgba-${MGBA_VERSION}" || exit rm -rf build -patch --strip=1 < ${CURRENT_DIRECTORY}/add_cycles_register.patch +patch --strip=1 < "${CURRENT_DIRECTORY}/add_cycles_register.patch" mkdir -p build -cd build +cd build || exit cmake .. \ -DBUILD_STATIC=ON \ -DBUILD_SHARED=OFF \ From ecf43f738b5feb14e85d226e093550c82b9d3f3e Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 25 Jul 2022 22:48:28 +0100 Subject: [PATCH 12/13] Only download mgba if it doesn't exist --- mgba-test-runner/build-mgba.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mgba-test-runner/build-mgba.sh b/mgba-test-runner/build-mgba.sh index 6efa2f9c..5261e541 100644 --- a/mgba-test-runner/build-mgba.sh +++ b/mgba-test-runner/build-mgba.sh @@ -6,6 +6,10 @@ CURRENT_DIRECTORY=$(pwd) cd "${OUT_DIRECTORY}" || exit +if [[ ! -f "mgba-${MGBA_VERSION}.tar.gz" ]]; then + curl -L "https://github.com/mgba-emu/mgba/archive/refs/tags/${MGBA_VERSION}.tar.gz" -o "mgba-${MGBA_VERSION}.tar.gz" +fi + if [[ -f libmgba-cycle.a ]]; then exit 0 fi From b951c38638cacba2159855e6b5fc7725c922ec40 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 25 Jul 2022 22:53:03 +0100 Subject: [PATCH 13/13] Do this in the correct place (made a mistake when rebasing) --- release.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release.sh b/release.sh index b1a880b1..01a5156c 100755 --- a/release.sh +++ b/release.sh @@ -46,7 +46,7 @@ fi TAGNAME="v$VERSION" -for PROJECT_TOML_FILE in agb/Cargo.toml agb-*/Cargo.toml template/Cargo.toml; do +for PROJECT_TOML_FILE in agb/Cargo.toml agb-*/Cargo.toml; do DIRECTORY=$(dirname "$PROJECT_TOML_FILE") # Update the version in Cargo.toml @@ -59,7 +59,7 @@ for PROJECT_TOML_FILE in agb/Cargo.toml agb-*/Cargo.toml template/Cargo.toml; do # also update the agb version in the template and the examples sed -i -e "s/^agb = \".*\"/agb = \"$VERSION\"/" template/Cargo.toml - for EXAMPLE_TOML_FILE in examples/*/Cargo.toml book/games/*/Cargo.toml; do + for EXAMPLE_TOML_FILE in examples/*/Cargo.toml book/games/*/Cargo.toml template/Cargo.toml; do EXAMPLE_DIR=$(dirname "$EXAMPLE_TOML_FILE") sed -E -i -e "/agb =/ s/version = \"[^\"]+\"/version = \"$VERSION\"/" "$EXAMPLE_DIR/Cargo.toml" (cd "$EXAMPLE_DIR" && cargo update)