Browse Source

Lots of refactor / doc comments

pull/10/head
Ian Chamberlain 1 year ago
parent
commit
8ff3c1c63f
No known key found for this signature in database
GPG Key ID: AE5484D09405AA60
  1. 12
      test-runner/src/console.rs
  2. 65
      test-runner/src/gdb.rs
  3. 36
      test-runner/src/lib.rs
  4. 39
      test-runner/src/macros.rs
  5. 27
      test-runner/src/socket.rs

12
test-runner/src/console.rs

@ -5,14 +5,18 @@ use ctru::services::gfx::{Flush, Swap};
use super::TestRunner; use super::TestRunner;
/// Run tests using the [`ctru::console::Console`] (print results to the 3DS screen).
/// This is mostly useful for running tests manually, especially on real hardware.
pub struct ConsoleRunner { pub struct ConsoleRunner {
gfx: Gfx, gfx: Gfx,
hid: Hid, hid: Hid,
apt: Apt, apt: Apt,
} }
impl Default for ConsoleRunner { impl TestRunner for ConsoleRunner {
fn default() -> Self { type Context<'this> = Console<'this>;
fn new() -> Self {
let gfx = Gfx::new().unwrap(); let gfx = Gfx::new().unwrap();
let hid = Hid::new().unwrap(); let hid = Hid::new().unwrap();
let apt = Apt::new().unwrap(); let apt = Apt::new().unwrap();
@ -21,10 +25,6 @@ impl Default for ConsoleRunner {
Self { gfx, hid, apt } Self { gfx, hid, apt }
} }
}
impl TestRunner for ConsoleRunner {
type Context<'this> = Console<'this>;
fn setup(&mut self) -> Self::Context<'_> { fn setup(&mut self) -> Self::Context<'_> {
Console::new(self.gfx.top_screen.borrow_mut()) Console::new(self.gfx.top_screen.borrow_mut())

65
test-runner/src/gdb.rs

@ -4,21 +4,42 @@ use ctru::error::ResultCode;
use super::TestRunner; use super::TestRunner;
#[derive(Default)] // We use a little trick with cfg(doctest) to make code fences appear in
pub struct GdbRunner; // rustdoc output, but compile without them when doctesting. This raises warnings
// for invalid code, though, so silence that lint here.
#[cfg_attr(not(doctest), allow(rustdoc::invalid_rust_codeblocks))]
/// Show test output in GDB, using the [File I/O Protocol] (called HIO in some 3DS
/// homebrew resources). Both stdout and stderr will be printed to the GDB console.
///
/// Creating this runner at the beginning of a doctest enables output from failing
/// tests. Without `GdbRunner`, tests will still fail on panic, but they won't display
/// anything written to `stdout` or `stderr`.
///
/// The runner should remain in scope for the remainder of the test.
///
/// [File I/O Protocol]: https://sourceware.org/gdb/onlinedocs/gdb/File_002dI_002fO-Overview.html#File_002dI_002fO-Overview
///
/// # Examples
///
#[cfg_attr(not(doctest), doc = "````")]
/// ```
/// let _runner = test_runner::GdbRunner::default();
/// assert_eq!(2 + 2, 4);
/// ```
#[cfg_attr(not(doctest), doc = "````")]
///
#[cfg_attr(not(doctest), doc = "````")]
/// ```should_panic
/// let _runner = test_runner::GdbRunner::default();
/// assert_eq!(2 + 2, 5);
/// ```
#[cfg_attr(not(doctest), doc = "````")]
pub struct GdbRunner(());
impl Drop for GdbRunner { impl Default for GdbRunner {
fn drop(&mut self) { fn default() -> Self {
unsafe { ctru_sys::gdbHioDevExit() }
}
}
impl TestRunner for GdbRunner {
type Context<'this> = ();
fn setup(&mut self) -> Self::Context<'_> {
// TODO: `ctru` expose safe API to do this and call that instead
|| -> ctru::Result<()> { || -> ctru::Result<()> {
// TODO: `ctru` expose safe API to do this and call that instead
unsafe { unsafe {
ResultCode(ctru_sys::gdbHioDevInit())?; ResultCode(ctru_sys::gdbHioDevInit())?;
// TODO: should we actually redirect stdin or nah? // TODO: should we actually redirect stdin or nah?
@ -27,8 +48,26 @@ impl TestRunner for GdbRunner {
Ok(()) Ok(())
}() }()
.expect("failed to redirect I/O streams to GDB"); .expect("failed to redirect I/O streams to GDB");
Self(())
}
} }
impl Drop for GdbRunner {
fn drop(&mut self) {
unsafe { ctru_sys::gdbHioDevExit() }
}
}
impl TestRunner for GdbRunner {
type Context<'this> = ();
fn new() -> Self {
Self::default()
}
fn setup(&mut self) -> Self::Context<'_> {}
fn cleanup<T: Termination>(self, test_result: T) -> T { fn cleanup<T: Termination>(self, test_result: T) -> T {
// GDB actually has the opportunity to inspect the exit code, // GDB actually has the opportunity to inspect the exit code,
// unlike other runners, so let's follow the default behavior of the // unlike other runners, so let's follow the default behavior of the

36
test-runner/src/lib.rs

@ -13,7 +13,6 @@ extern crate test;
mod console; mod console;
mod gdb; mod gdb;
mod macros;
mod socket; mod socket;
use std::process::{ExitCode, Termination}; use std::process::{ExitCode, Termination};
@ -23,28 +22,20 @@ pub use gdb::GdbRunner;
pub use socket::SocketRunner; pub use socket::SocketRunner;
use test::{ColorConfig, OutputFormat, TestDescAndFn, TestFn, TestOpts}; use test::{ColorConfig, OutputFormat, TestDescAndFn, TestFn, TestOpts};
/// Show test output in GDB, using the [File I/O Protocol] (called HIO in some 3DS /// Run tests using the [`GdbRunner`].
/// homebrew resources). Both stdout and stderr will be printed to the GDB console. /// This function can be used with the `#[test_runner]` attribute.
///
/// [File I/O Protocol]: https://sourceware.org/gdb/onlinedocs/gdb/File_002dI_002fO-Overview.html#File_002dI_002fO-Overview
pub fn run_gdb(tests: &[&TestDescAndFn]) { pub fn run_gdb(tests: &[&TestDescAndFn]) {
run::<GdbRunner>(tests); run::<GdbRunner>(tests);
} }
/// Run tests using the `ctru` [`Console`] (print results to the 3DS screen). /// Run tests using the [`ConsoleRunner`].
/// This is mostly useful for running tests manually, especially on real hardware. /// This function can be used with the `#[test_runner]` attribute.
///
/// [`Console`]: ctru::console::Console
pub fn run_console(tests: &[&TestDescAndFn]) { pub fn run_console(tests: &[&TestDescAndFn]) {
run::<ConsoleRunner>(tests); run::<ConsoleRunner>(tests);
} }
/// Show test output via a network socket to `3dslink`. This runner is only useful /// Run tests using the [`SocketRunner`].
/// on real hardware, since `3dslink` doesn't work with emulators. /// This function can be used with the `#[test_runner]` attribute.
///
/// See [`Soc::redirect_to_3dslink`] for more details.
///
/// [`Soc::redirect_to_3dslink`]: ctru::services::soc::Soc::redirect_to_3dslink
pub fn run_socket(tests: &[&TestDescAndFn]) { pub fn run_socket(tests: &[&TestDescAndFn]) {
run::<SocketRunner>(tests); run::<SocketRunner>(tests);
} }
@ -52,7 +43,7 @@ pub fn run_socket(tests: &[&TestDescAndFn]) {
fn run<Runner: TestRunner>(tests: &[&TestDescAndFn]) { fn run<Runner: TestRunner>(tests: &[&TestDescAndFn]) {
std::env::set_var("RUST_BACKTRACE", "1"); std::env::set_var("RUST_BACKTRACE", "1");
let mut runner = Runner::default(); let mut runner = Runner::new();
let ctx = runner.setup(); let ctx = runner.setup();
let opts = TestOpts { let opts = TestOpts {
@ -101,16 +92,8 @@ fn make_owned_test(test: &TestDescAndFn) -> TestDescAndFn {
} }
} }
mod private {
pub trait Sealed {}
impl Sealed for super::ConsoleRunner {}
impl Sealed for super::GdbRunner {}
impl Sealed for super::SocketRunner {}
}
/// A helper trait to make the behavior of test runners consistent. /// A helper trait to make the behavior of test runners consistent.
pub trait TestRunner: private::Sealed + Sized + Default { trait TestRunner: Sized {
/// Any context the test runner needs to remain alive for the duration of /// Any context the test runner needs to remain alive for the duration of
/// the test. This can be used for things that need to borrow the test runner /// the test. This can be used for things that need to borrow the test runner
/// itself. /// itself.
@ -119,6 +102,9 @@ pub trait TestRunner: private::Sealed + Sized + Default {
where where
Self: 'this; Self: 'this;
/// Initialize the test runner.
fn new() -> Self;
/// Create the [`Context`](Self::Context), if any. /// Create the [`Context`](Self::Context), if any.
fn setup(&mut self) -> Self::Context<'_>; fn setup(&mut self) -> Self::Context<'_>;

39
test-runner/src/macros.rs

@ -1,39 +0,0 @@
//! Macros for working with test runners.
// We use a little trick with cfg(doctest) to make code fences appear in
// rustdoc output, but compile without them when doctesting. This raises warnings
// for invalid code, though, so silence that lint here.
#[cfg_attr(not(doctest), allow(rustdoc::invalid_rust_codeblocks))]
/// Helper macro for writing doctests using this runner. Call this macro at the
/// beginning of a doctest enables output from failing tests using this crate's
/// [`GdbRunner`](crate::GdbRunner). Without `setup_doctest!()`, doctests will
/// still fail on panic, but they won't display anything written to `stdout` or
/// `stderr`.
///
/// # Examples
///
#[cfg_attr(not(doctest), doc = "````")]
/// ```
/// test_runner::setup_doctest!();
/// assert_eq!(2 + 2, 4);
/// ```
#[cfg_attr(not(doctest), doc = "````")]
///
#[cfg_attr(not(doctest), doc = "````")]
/// ```should_panic
/// test_runner::setup_doctest!();
/// assert_eq!(2 + 2, 5);
/// ```
#[cfg_attr(not(doctest), doc = "````")]
#[macro_export]
macro_rules! setup_doctest {
() => {
use $crate::TestRunner as _;
let mut runner = $crate::GdbRunner::default();
runner.setup();
// We don't bother with cleanup here, since the macro is meant to be used
// in a doctest context (i.e. `fn main()`, not used as a test runner)
};
}

27
test-runner/src/socket.rs

@ -2,24 +2,27 @@ use ctru::prelude::*;
use super::TestRunner; use super::TestRunner;
/// Show test output via a network socket to `3dslink`. This runner is only useful
/// on real hardware, since `3dslink` doesn't work with emulators.
///
/// See [`Soc::redirect_to_3dslink`] for more details.
///
/// [`Soc::redirect_to_3dslink`]: ctru::services::soc::Soc::redirect_to_3dslink
pub struct SocketRunner { pub struct SocketRunner {
soc: Soc, soc: Soc,
} }
impl Default for SocketRunner {
fn default() -> Self {
Self {
soc: Soc::new().expect("failed to initialize network service"),
}
}
}
impl TestRunner for SocketRunner { impl TestRunner for SocketRunner {
type Context<'this> = (); type Context<'this> = &'this Soc;
fn setup(&mut self) -> Self::Context<'_> { fn new() -> Self {
self.soc let mut soc = Soc::new().expect("failed to initialize network service");
.redirect_to_3dslink(true, true) soc.redirect_to_3dslink(true, true)
.expect("failed to redirect to socket"); .expect("failed to redirect to socket");
Self { soc }
}
fn setup(&mut self) -> Self::Context<'_> {
&self.soc
} }
} }

Loading…
Cancel
Save