Unit tests

It is possible to write unit tests using agb.

Installing the mgba-test-runner (technically optional)

Firstly you'll need to install the mgba-test-runner which requires cmake and libelf installed via whichever mechanism you use to manage software, along with a C and C++ compiler.

Then run

cargo install --git https://github.com/agbrs/agb.git mgba-test-runner

Running unit tests

Running just cargo test will launch the test in mgba, which isn't particularly useful. To run the test using the test runner installed in step 1, use the following command:

CARGO_TARGET_THUMBV4T_NONE_EABI_RUNNER=mgba-test-runner cargo test

Writing unit tests

If you don't already have this in your main.rs file from the template, you need the following at the top to enable the custom test framework.


#![allow(unused)]
#![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))]
fn main() {
}

Then, you can write tests using the #[test_case] attribute:


#![allow(unused)]
fn main() {
#[test_case]
fn dummy_test(_gba: &mut Gba) {
    assert_eq!(1, 1);
}
}

Tests take a mutable reference to the Gba struct. There is no equivalent to #[should_panic] so these style of tests are not (currently) possible to write using agb.

If you want to check that the screen has pixels as expected, you can use the assert_image_output method. This only works when running the unit test under the mgba-test-runner installed in step 1.


#![allow(unused)]
fn main() {
#[test_case]
fn test_background_shows_correctly(gba: &mut Gba) {
    // you should never assume that the palettes are set up correctly in a test
    VRAM_MANAGER.set_background_palettes(...);

    let mut gfx = gba.graphics.get();
    let mut frame = gfx.frame();
    show_some_background(&mut frame);
    frame.commit();

    assert_image_output("src/tests/test_background_shows_correctly.png");
}
}

If the mentioned png file doesn't exist, then the mgba-test-runner will create the file. Subsequent runs will compare the current screen with the expected result and fail the test if it doesn't match.

Interpreting test output

When running unit tests, you'll get output that looks similar to this:

agb::display::blend::test::can_blend_affine_backgrounds...[ok: 1757950c ≈ 0.1s ≈ 625% frame]
agb::display::blend::test::can_blend_affine_object_to_black...[ok: 1656122c ≈ 0.1s ≈ 589% frame]
agb::display::blend::test::can_blend_object_shape_to_black...[ok: 1392767c ≈ 0.08s ≈ 496% frame]
agb::display::blend::test::can_blend_object_to_black...[ok: 1400985c ≈ 0.08s ≈ 498% frame]
agb::display::blend::test::can_blend_object_to_white...[ok: 1401004c ≈ 0.08s ≈ 498% frame]

It starts with the test name, and then lists something like [ok: 1757950c ≈ 0.1s ≈ 625% frame]. This means it took 1,757,950 CPU cycles to run the test, or about 0.1 seconds or 6.25 frames.

Any test which uses assert_image_output() will automatically take a few frames.

Doc tests

To write a doctest for your game in agb, create a function and mark it with the #[agb::doctest] attribute macro.


#![allow(unused)]
fn main() {
/// This is a cool rust function with some epic documentation which is checked
/// at compile time and the doctest will run when running the tests.
///
/// ```rust
/// # #![no_std]
/// # #![no_main]
/// #
/// # #[agb::doctest]
/// # fn test(gba: agb::Gba) {
/// assert_eq!(my_crate::my_cool_function(), 7);
/// # }
/// ```
fn my_cool_function() -> i32 {
    return 7;
}
}

You probably want to hide the boilerplate for the doctest as shown above to make it easier for your users to understand the relevant section.

These will run by default when running the cargo test command listed above, and you can run them explicitly with

CARGO_TARGET_THUMBV4T_NONE_EABI_RUNNER=mgba-test-runner cargo test --doc