|
| 1 | +//! Custom test runner for building/running unit tests on the 3DS. |
| 2 | +
|
| 3 | +extern crate test; |
| 4 | + |
| 5 | +use std::io; |
| 6 | + |
| 7 | +use test::{ColorConfig, Options, OutputFormat, RunIgnored, TestDescAndFn, TestFn, TestOpts}; |
| 8 | + |
| 9 | +use crate::console::Console; |
| 10 | +use crate::gfx::Gfx; |
| 11 | +use crate::services::hid::{Hid, KeyPad}; |
| 12 | +use crate::services::Apt; |
| 13 | + |
| 14 | +/// A custom runner to be used with `#[test_runner]`. This simple implementation |
| 15 | +/// runs all tests in series, "failing" on the first one to panic (really, the |
| 16 | +/// panic is just treated the same as any normal application panic). |
| 17 | +pub(crate) fn run(tests: &[&TestDescAndFn]) { |
| 18 | + crate::init(); |
| 19 | + |
| 20 | + let gfx = Gfx::default(); |
| 21 | + let hid = Hid::init().unwrap(); |
| 22 | + let apt = Apt::init().unwrap(); |
| 23 | + |
| 24 | + let mut top_screen = gfx.top_screen.borrow_mut(); |
| 25 | + top_screen.set_wide_mode(true); |
| 26 | + let _console = Console::init(top_screen); |
| 27 | + |
| 28 | + // TODO: it would be nice to have a way of specifying argv to make these |
| 29 | + // configurable at runtime, but I can't figure out how to do it easily, |
| 30 | + // so for now, just hardcode everything. |
| 31 | + let opts = TestOpts { |
| 32 | + list: false, |
| 33 | + filters: Vec::new(), |
| 34 | + filter_exact: false, |
| 35 | + // Forking is not supported |
| 36 | + force_run_in_process: true, |
| 37 | + exclude_should_panic: false, |
| 38 | + run_ignored: RunIgnored::No, |
| 39 | + run_tests: true, |
| 40 | + // Don't run benchmarks. We may want to create a separate runner for them in the future |
| 41 | + bench_benchmarks: false, |
| 42 | + logfile: None, |
| 43 | + nocapture: false, |
| 44 | + // TODO: color doesn't work because of TERM/TERMINFO. |
| 45 | + // With RomFS we might be able to fake this out nicely... |
| 46 | + color: ColorConfig::AutoColor, |
| 47 | + format: OutputFormat::Pretty, |
| 48 | + shuffle: false, |
| 49 | + shuffle_seed: None, |
| 50 | + test_threads: None, |
| 51 | + skip: Vec::new(), |
| 52 | + time_options: None, |
| 53 | + options: Options::new(), |
| 54 | + }; |
| 55 | + |
| 56 | + // Use the default test implementation with our hardcoded options |
| 57 | + let _success = run_static_tests(&opts, tests).unwrap(); |
| 58 | + |
| 59 | + // Make sure the user can actually see the results before we exit |
| 60 | + println!("Press START to exit."); |
| 61 | + |
| 62 | + while apt.main_loop() { |
| 63 | + gfx.flush_buffers(); |
| 64 | + gfx.swap_buffers(); |
| 65 | + gfx.wait_for_vblank(); |
| 66 | + |
| 67 | + hid.scan_input(); |
| 68 | + if hid.keys_down().contains(KeyPad::KEY_START) { |
| 69 | + break; |
| 70 | + } |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +/// Adapted from [`test::test_main_static`] and [`test::make_owned_test`]. |
| 75 | +fn run_static_tests(opts: &TestOpts, tests: &[&TestDescAndFn]) -> io::Result<bool> { |
| 76 | + let tests = tests.iter().map(make_owned_test).collect(); |
| 77 | + test::run_tests_console(opts, tests) |
| 78 | +} |
| 79 | + |
| 80 | +/// Clones static values for putting into a dynamic vector, which test_main() |
| 81 | +/// needs to hand out ownership of tests to parallel test runners. |
| 82 | +/// |
| 83 | +/// This will panic when fed any dynamic tests, because they cannot be cloned. |
| 84 | +fn make_owned_test(test: &&TestDescAndFn) -> TestDescAndFn { |
| 85 | + match test.testfn { |
| 86 | + TestFn::StaticTestFn(f) => TestDescAndFn { |
| 87 | + testfn: TestFn::StaticTestFn(f), |
| 88 | + desc: test.desc.clone(), |
| 89 | + }, |
| 90 | + TestFn::StaticBenchFn(f) => TestDescAndFn { |
| 91 | + testfn: TestFn::StaticBenchFn(f), |
| 92 | + desc: test.desc.clone(), |
| 93 | + }, |
| 94 | + _ => panic!("non-static tests passed to test::test_main_static"), |
| 95 | + } |
| 96 | +} |
| 97 | + |
| 98 | +/// The following functions are stubs needed to link the test library, |
| 99 | +/// but do nothing because we don't actually need them for the runner to work. |
| 100 | +mod link_fix { |
| 101 | + #[no_mangle] |
| 102 | + extern "C" fn execvp( |
| 103 | + _argc: *const libc::c_char, |
| 104 | + _argv: *mut *const libc::c_char, |
| 105 | + ) -> libc::c_int { |
| 106 | + -1 |
| 107 | + } |
| 108 | + |
| 109 | + #[no_mangle] |
| 110 | + extern "C" fn pipe(_fildes: *mut libc::c_int) -> libc::c_int { |
| 111 | + -1 |
| 112 | + } |
| 113 | + |
| 114 | + #[no_mangle] |
| 115 | + extern "C" fn sigemptyset(_arg1: *mut libc::sigset_t) -> ::libc::c_int { |
| 116 | + -1 |
| 117 | + } |
| 118 | + |
| 119 | + #[no_mangle] |
| 120 | + extern "C" fn sysconf(_name: libc::c_int) -> libc::c_long { |
| 121 | + -1 |
| 122 | + } |
| 123 | +} |
0 commit comments