Browse Source

Merge pull request #10 from ian-h-chamberlain/fix/compile-with-items

pull/13/head
Ian Chamberlain 1 year ago committed by GitHub
parent
commit
abe134bc65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      test-runner/src/console.rs
  2. 65
      test-runner/src/gdb.rs
  3. 36
      test-runner/src/lib.rs
  4. 101
      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}; @@ -5,14 +5,18 @@ use ctru::services::gfx::{Flush, Swap};
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 {
gfx: Gfx,
hid: Hid,
apt: Apt,
}
impl Default for ConsoleRunner {
fn default() -> Self {
impl TestRunner for ConsoleRunner {
type Context<'this> = Console<'this>;
fn new() -> Self {
let gfx = Gfx::new().unwrap();
let hid = Hid::new().unwrap();
let apt = Apt::new().unwrap();
@ -21,10 +25,6 @@ impl Default for ConsoleRunner { @@ -21,10 +25,6 @@ impl Default for ConsoleRunner {
Self { gfx, hid, apt }
}
}
impl TestRunner for ConsoleRunner {
type Context<'this> = Console<'this>;
fn setup(&mut self) -> Self::Context<'_> {
Console::new(self.gfx.top_screen.borrow_mut())

65
test-runner/src/gdb.rs

@ -4,21 +4,42 @@ use ctru::error::ResultCode; @@ -4,21 +4,42 @@ use ctru::error::ResultCode;
use super::TestRunner;
#[derive(Default)]
pub struct GdbRunner;
// 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))]
/// 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 {
fn drop(&mut 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
impl Default for GdbRunner {
fn default() -> Self {
|| -> ctru::Result<()> {
// TODO: `ctru` expose safe API to do this and call that instead
unsafe {
ResultCode(ctru_sys::gdbHioDevInit())?;
// TODO: should we actually redirect stdin or nah?
@ -27,8 +48,26 @@ impl TestRunner for GdbRunner { @@ -27,8 +48,26 @@ impl TestRunner for GdbRunner {
Ok(())
}()
.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 {
// GDB actually has the opportunity to inspect the exit code,
// 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; @@ -13,7 +13,6 @@ extern crate test;
mod console;
mod gdb;
mod macros;
mod socket;
use std::process::{ExitCode, Termination};
@ -23,28 +22,20 @@ pub use gdb::GdbRunner; @@ -23,28 +22,20 @@ pub use gdb::GdbRunner;
pub use socket::SocketRunner;
use test::{ColorConfig, OutputFormat, TestDescAndFn, TestFn, TestOpts};
/// 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.
///
/// [File I/O Protocol]: https://sourceware.org/gdb/onlinedocs/gdb/File_002dI_002fO-Overview.html#File_002dI_002fO-Overview
/// Run tests using the [`GdbRunner`].
/// This function can be used with the `#[test_runner]` attribute.
pub fn run_gdb(tests: &[&TestDescAndFn]) {
run::<GdbRunner>(tests);
}
/// Run tests using the `ctru` [`Console`] (print results to the 3DS screen).
/// This is mostly useful for running tests manually, especially on real hardware.
///
/// [`Console`]: ctru::console::Console
/// Run tests using the [`ConsoleRunner`].
/// This function can be used with the `#[test_runner]` attribute.
pub fn run_console(tests: &[&TestDescAndFn]) {
run::<ConsoleRunner>(tests);
}
/// 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
/// Run tests using the [`SocketRunner`].
/// This function can be used with the `#[test_runner]` attribute.
pub fn run_socket(tests: &[&TestDescAndFn]) {
run::<SocketRunner>(tests);
}
@ -52,7 +43,7 @@ pub fn run_socket(tests: &[&TestDescAndFn]) { @@ -52,7 +43,7 @@ pub fn run_socket(tests: &[&TestDescAndFn]) {
fn run<Runner: TestRunner>(tests: &[&TestDescAndFn]) {
std::env::set_var("RUST_BACKTRACE", "1");
let mut runner = Runner::default();
let mut runner = Runner::new();
let ctx = runner.setup();
let opts = TestOpts {
@ -101,16 +92,8 @@ fn make_owned_test(test: &TestDescAndFn) -> TestDescAndFn { @@ -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.
pub trait TestRunner: private::Sealed + Sized + Default {
trait TestRunner: Sized {
/// 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
/// itself.
@ -119,6 +102,9 @@ pub trait TestRunner: private::Sealed + Sized + Default { @@ -119,6 +102,9 @@ pub trait TestRunner: private::Sealed + Sized + Default {
where
Self: 'this;
/// Initialize the test runner.
fn new() -> Self;
/// Create the [`Context`](Self::Context), if any.
fn setup(&mut self) -> Self::Context<'_>;

101
test-runner/src/macros.rs

@ -1,101 +0,0 @@ @@ -1,101 +0,0 @@
//! Macros for working with test runners.
// Use a neat little trick with cfg(doctest) to make code fences appear in
// rustdoc output, but still compile normally when doctesting. This raises warnings
// for invalid code though, so we also silence that lint here.
#[cfg_attr(not(doctest), allow(rustdoc::invalid_rust_codeblocks))]
/// Helper macro for writing doctests using this runner. Wrap this macro around
/// your normal doctest to enable running it with the test runners in this crate.
///
/// You may optionally specify a runner before the test body, and may use any of
/// the various [`fn main()`](https://doc.rust-lang.org/rustdoc/write-documentation/documentation-tests.html#using--in-doc-tests)
/// signatures allowed by documentation tests.
///
/// # Examples
///
/// ## Basic usage
///
#[cfg_attr(not(doctest), doc = "````")]
/// ```
/// test_runner::doctest! {
/// assert_eq!(2 + 2, 4);
/// }
/// ```
#[cfg_attr(not(doctest), doc = "````")]
///
/// ## Custom runner
///
#[cfg_attr(not(doctest), doc = "````")]
/// ```no_run
/// test_runner::doctest! { SocketRunner,
/// assert_eq!(2 + 2, 4);
/// }
/// ```
#[cfg_attr(not(doctest), doc = "````")]
///
/// ## `should_panic`
///
#[cfg_attr(not(doctest), doc = "````")]
/// ```should_panic
/// test_runner::doctest! {
/// assert_eq!(2 + 2, 5);
/// }
/// ```
#[cfg_attr(not(doctest), doc = "````")]
///
/// ## Custom `fn main`
///
#[cfg_attr(not(doctest), doc = "````")]
/// ```
/// test_runner::doctest! {
/// fn main() {
/// assert_eq!(2 + 2, 4);
/// }
/// }
/// ```
#[cfg_attr(not(doctest), doc = "````")]
///
#[cfg_attr(not(doctest), doc = "````")]
/// ```
/// test_runner::doctest! {
/// fn main() -> Result<(), Box<dyn std::error::Error>> {
/// assert_eq!(2 + 2, 4);
/// Ok(())
/// }
/// }
/// ```
#[cfg_attr(not(doctest), doc = "````")]
///
/// ## Implicit return type
///
/// Note that for the rustdoc preprocessor to understand the return type, the
/// `Ok(())` expression must be written _outside_ the `doctest!` invocation.
///
#[cfg_attr(not(doctest), doc = "````")]
/// ```
/// test_runner::doctest! {
/// assert_eq!(2 + 2, 4);
/// }
/// Ok::<(), std::io::Error>(())
/// ```
#[cfg_attr(not(doctest), doc = "````")]
#[macro_export]
macro_rules! doctest {
($runner:ident, fn main() $(-> $ret:ty)? { $($body:tt)* } ) => {
fn main() $(-> $ret)? {
$crate::doctest!{ $runner, $($body)* }
}
};
($runner:ident, $($body:tt)*) => {
use $crate::TestRunner as _;
let mut _runner = $crate::$runner::default();
_runner.setup();
let _result = { $($body)* };
_runner.cleanup(_result)
};
($($body:tt)*) => {
$crate::doctest!{ GdbRunner,
$($body)*
}
};
}

27
test-runner/src/socket.rs

@ -2,24 +2,27 @@ use ctru::prelude::*; @@ -2,24 +2,27 @@ use ctru::prelude::*;
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 {
soc: Soc,
}
impl Default for SocketRunner {
fn default() -> Self {
Self {
soc: Soc::new().expect("failed to initialize network service"),
}
}
}
impl TestRunner for SocketRunner {
type Context<'this> = ();
type Context<'this> = &'this Soc;
fn setup(&mut self) -> Self::Context<'_> {
self.soc
.redirect_to_3dslink(true, true)
fn new() -> Self {
let mut soc = Soc::new().expect("failed to initialize network service");
soc.redirect_to_3dslink(true, true)
.expect("failed to redirect to socket");
Self { soc }
}
fn setup(&mut self) -> Self::Context<'_> {
&self.soc
}
}

Loading…
Cancel
Save