Browse Source

Merge pull request #135 from rust3ds/ci/reusable-actions

Use external actions / test runner crate so we can actually run our tests!
pull/139/head
Ian Chamberlain 1 year ago committed by GitHub
parent
commit
a636722b49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 46
      .github/actions/setup/action.yml
  2. 63
      .github/workflows/ci.yml
  3. 4
      Cargo.toml
  4. 7
      ctru-rs/Cargo.toml
  5. 27
      ctru-rs/src/applets/swkbd.rs
  6. 11
      ctru-rs/src/console.rs
  7. 5
      ctru-rs/src/error.rs
  8. 12
      ctru-rs/src/lib.rs
  9. 6
      ctru-rs/src/os.rs
  10. 15
      ctru-rs/src/services/am.rs
  11. 6
      ctru-rs/src/services/apt.rs
  12. 17
      ctru-rs/src/services/cam.rs
  13. 18
      ctru-rs/src/services/cfgu.rs
  14. 51
      ctru-rs/src/services/fs.rs
  15. 28
      ctru-rs/src/services/gfx.rs
  16. 21
      ctru-rs/src/services/hid.rs
  17. 19
      ctru-rs/src/services/ndsp/mod.rs
  18. 6
      ctru-rs/src/services/ndsp/wave.rs
  19. 12
      ctru-rs/src/services/ps.rs
  20. 5
      ctru-rs/src/services/romfs.rs
  21. 12
      ctru-rs/src/services/soc.rs
  22. 3
      ctru-rs/src/services/sslc.rs
  23. 73
      ctru-rs/src/test_runner.rs
  24. 3
      ctru-sys/build.rs
  25. 8
      ctru-sys/src/lib.rs

46
.github/actions/setup/action.yml

@ -1,46 +0,0 @@
name: Setup
description: Set up CI environment for Rust + 3DS development
inputs:
toolchain:
description: The Rust toolchain to use for the steps
required: true
default: nightly
runs:
using: composite
steps:
# https://github.com/nektos/act/issues/917#issuecomment-1074421318
- if: ${{ env.ACT }}
shell: bash
name: Hack container for local development
run: |
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs
- name: Setup default Rust toolchain
# Use this helper action so we get matcher support
# https://github.com/actions-rust-lang/setup-rust-toolchain/pull/15
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
components: clippy, rustfmt, rust-src
toolchain: ${{ inputs.toolchain }}
- name: Install build tools for host
shell: bash
run: sudo apt-get update && sudo apt-get install -y build-essential
- name: Install cargo-3ds
uses: actions-rs/cargo@v1
with:
command: install
# TODO: this should probably just be a released version from crates.io
# once cargo-3ds gets published somewhere...
args: >-
--git https://github.com/rust3ds/cargo-3ds
--rev 78a652fdfb01e2614a792d1a56b10c980ee1dae9
- name: Set PATH to include devkitARM
shell: bash
# For some reason devkitARM/bin is not part of the default PATH in the container
run: echo "${DEVKITARM}/bin" >> $GITHUB_PATH

63
.github/workflows/ci.yml

@ -9,19 +9,13 @@ on:
- master - master
workflow_dispatch: workflow_dispatch:
env:
# https://blog.rust-lang.org/2022/06/22/sparse-registry-testing.html
CARGO_UNSTABLE_SPARSE_REGISTRY: "true"
# actions-rust-lang/setup-rust-toolchain sets some default RUSTFLAGS
RUSTFLAGS: ""
jobs: jobs:
lint: lint:
strategy: strategy:
matrix: matrix:
toolchain: toolchain:
# Run against a "known good" nightly # Run against a "known good" nightly. Rustc version is 1 day behind the toolchain date
- nightly-2023-05-31 - nightly-2023-06-01
# Check for breakage on latest nightly # Check for breakage on latest nightly
- nightly - nightly
@ -33,11 +27,14 @@ jobs:
- name: Checkout branch - name: Checkout branch
uses: actions/checkout@v2 uses: actions/checkout@v2
- uses: ./.github/actions/setup - uses: rust3ds/test-runner/setup@v1
with: with:
toolchain: ${{ matrix.toolchain }} toolchain: ${{ matrix.toolchain }}
- name: Hide duplicate warnings from lint job # https://github.com/actions/runner/issues/504
# Removing the matchers won't keep the job from failing if there are errors,
# but will at least declutter pull request annotations (especially for warnings).
- name: Hide duplicate annotations from nightly
if: ${{ matrix.toolchain == 'nightly' }} if: ${{ matrix.toolchain == 'nightly' }}
run: | run: |
echo "::remove-matcher owner=clippy::" echo "::remove-matcher owner=clippy::"
@ -46,18 +43,17 @@ jobs:
- name: Check formatting - name: Check formatting
run: cargo fmt --all --verbose -- --check run: cargo fmt --all --verbose -- --check
- name: Cargo check - name: Cargo check ctru-sys (without tests)
run: cargo 3ds clippy --color=always --verbose --all-targets run: cargo 3ds clippy --package ctru-sys --color=always --verbose
# --deny=warnings would be nice, but can easily break CI for new clippy
# lints getting added. I'd also like to use Github's "inline warnings" - name: Cargo check ctru-rs (including tests)
# feature, but https://github.com/actions/runner/issues/2341 means we run: cargo 3ds clippy --package ctru-rs --color=always --verbose --all-targets
# can't have both that *and* colored output.
doctests: test:
strategy: strategy:
matrix: matrix:
toolchain: toolchain:
- nightly-2023-05-31 - nightly-2023-06-01
- nightly - nightly
continue-on-error: ${{ matrix.toolchain == 'nightly' }} continue-on-error: ${{ matrix.toolchain == 'nightly' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -66,15 +62,36 @@ jobs:
- name: Checkout branch - name: Checkout branch
uses: actions/checkout@v2 uses: actions/checkout@v2
- uses: ./.github/actions/setup - uses: rust3ds/test-runner/setup@v1
with: with:
toolchain: ${{ matrix.toolchain }} toolchain: ${{ matrix.toolchain }}
- name: Hide duplicated warnings from lint job - name: Hide duplicated warnings from lint job
run: echo "::remove-matcher owner=clippy::" run: echo "::remove-matcher owner=clippy::"
- name: Build doc tests # This needs to be done separately from running the tests to ensure the
run: cargo 3ds test --doc --verbose # lib tests' .3dsx is built before the test is run (for romfs). We don't
# really have a good way to build the 3dsx in between the build + test,
# unless cargo-3ds actually runs them as separate commands. See
# https://github.com/rust3ds/cargo-3ds/issues/44 for more details
- name: Build lib and integration tests
run: cargo 3ds test --no-run --tests --package ctru-rs
- name: Run lib and integration tests
uses: rust3ds/test-runner/run-tests@v1
with:
args: --tests --package ctru-rs
# TODO: it would be nice to actually build 3dsx for examples/tests, etc. - name: Build and run doc tests
# and run it somehow, but exactly how remains to be seen. uses: rust3ds/test-runner/run-tests@v1
with:
args: --doc --package ctru-rs
- name: Upload citra logs and capture videos
uses: actions/upload-artifact@v3
if: success() || failure() # always run unless the workflow was cancelled
with:
name: citra-logs-${{ matrix.toolchain }}
path: |
target/armv6k-nintendo-3ds/debug/deps/*.txt
target/armv6k-nintendo-3ds/debug/deps/*.webm

4
Cargo.toml

@ -4,5 +4,7 @@ default-members = ["ctru-rs", "ctru-sys"]
resolver = "2" resolver = "2"
[patch.'https://github.com/rust3ds/ctru-rs'] [patch.'https://github.com/rust3ds/ctru-rs']
# Make sure all dependencies use the local ctru-sys package # Make sure all dependencies use the local packages. This is needed for things
# like pthread-3ds that rely on ctru-sys, and test-runner which relies on ctru-rs
ctru-rs = { path = "ctru-rs" }
ctru-sys = { path = "ctru-sys" } ctru-sys = { path = "ctru-sys" }

7
ctru-rs/Cargo.toml

@ -29,13 +29,14 @@ widestring = "0.2.2"
toml = "0.5" toml = "0.5"
[dev-dependencies] [dev-dependencies]
bytemuck = "1.12.3"
cfg-if = "1.0.0"
ferris-says = "0.2.1" ferris-says = "0.2.1"
futures = "0.3" futures = "0.3"
lewton = "0.10.2"
test-runner = { git = "https://github.com/rust3ds/test-runner.git" }
time = "0.3.7" time = "0.3.7"
tokio = { version = "1.16", features = ["rt", "time", "sync", "macros"] } tokio = { version = "1.16", features = ["rt", "time", "sync", "macros"] }
cfg-if = "1.0.0"
bytemuck = "1.12.3"
lewton = "0.10.2"
[features] [features]
default = ["romfs", "big-stack"] default = ["romfs", "big-stack"]

27
ctru-rs/src/applets/swkbd.rs

@ -160,7 +160,8 @@ impl SoftwareKeyboard {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # fn main() { /// # fn main() {
/// # /// #
/// use ctru::applets::swkbd::{SoftwareKeyboard, Kind}; /// use ctru::applets::swkbd::{SoftwareKeyboard, Kind};
@ -191,7 +192,8 @@ impl SoftwareKeyboard {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -235,7 +237,8 @@ impl SoftwareKeyboard {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -266,7 +269,8 @@ impl SoftwareKeyboard {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # fn main() { /// # fn main() {
/// # /// #
/// use ctru::applets::swkbd::{SoftwareKeyboard, Features}; /// use ctru::applets::swkbd::{SoftwareKeyboard, Features};
@ -286,7 +290,8 @@ impl SoftwareKeyboard {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # fn main() { /// # fn main() {
/// # /// #
/// use ctru::applets::swkbd::{SoftwareKeyboard, ValidInput, Filters}; /// use ctru::applets::swkbd::{SoftwareKeyboard, ValidInput, Filters};
@ -309,7 +314,8 @@ impl SoftwareKeyboard {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # fn main() { /// # fn main() {
/// # /// #
/// use ctru::applets::swkbd::{SoftwareKeyboard, ValidInput, Filters}; /// use ctru::applets::swkbd::{SoftwareKeyboard, ValidInput, Filters};
@ -336,7 +342,8 @@ impl SoftwareKeyboard {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # fn main() { /// # fn main() {
/// # /// #
/// use ctru::applets::swkbd::SoftwareKeyboard; /// use ctru::applets::swkbd::SoftwareKeyboard;
@ -363,7 +370,8 @@ impl SoftwareKeyboard {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # fn main() { /// # fn main() {
/// # /// #
/// use ctru::applets::swkbd::{SoftwareKeyboard, Button, Kind}; /// use ctru::applets::swkbd::{SoftwareKeyboard, Button, Kind};
@ -402,7 +410,8 @@ impl SoftwareKeyboard {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # fn main() { /// # fn main() {
/// # /// #
/// use ctru::applets::swkbd::{SoftwareKeyboard, Button, Kind}; /// use ctru::applets::swkbd::{SoftwareKeyboard, Button, Kind};

11
ctru-rs/src/console.rs

@ -52,12 +52,13 @@ impl<'screen> Console<'screen> {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
/// use ctru::services::gfx::Gfx;
/// use ctru::console::Console; /// use ctru::console::Console;
/// use ctru::services::gfx::Gfx;
/// ///
/// // Initialize graphics. /// // Initialize graphics.
/// let gfx = Gfx::new()?; /// let gfx = Gfx::new()?;
@ -94,7 +95,8 @@ impl<'screen> Console<'screen> {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -131,7 +133,8 @@ impl<'screen> Console<'screen> {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #

5
ctru-rs/src/error.rs

@ -21,10 +21,11 @@ pub type Result<T> = ::std::result::Result<T, Error>;
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// use ctru::error::{Result, ResultCode}; /// use ctru::error::{Result, ResultCode};
/// ///
/// pub fn hid_init() -> Result<()> { /// pub fn main() -> Result<()> {
/// # let _runner = test_runner::GdbRunner::default();
/// // We run an unsafe function which returns a `ctru_sys::Result`. /// // We run an unsafe function which returns a `ctru_sys::Result`.
/// let result: ctru_sys::Result = unsafe { ctru_sys::hidInit() }; /// let result: ctru_sys::Result = unsafe { ctru_sys::hidInit() };
/// ///

12
ctru-rs/src/lib.rs

@ -18,11 +18,10 @@
#![crate_type = "rlib"] #![crate_type = "rlib"]
#![crate_name = "ctru"] #![crate_name = "ctru"]
#![warn(missing_docs)] #![warn(missing_docs)]
#![feature(test)]
#![feature(custom_test_frameworks)] #![feature(custom_test_frameworks)]
#![feature(try_trait_v2)] #![feature(try_trait_v2)]
#![feature(allocator_api)] #![feature(allocator_api)]
#![test_runner(test_runner::run)] #![test_runner(test_runner::run_gdb)] // TODO: does this make sense to have configurable?
#![doc( #![doc(
html_favicon_url = "https://user-images.githubusercontent.com/11131775/225929072-2fa1741c-93ae-4b47-9bdf-af70f3d59910.png" html_favicon_url = "https://user-images.githubusercontent.com/11131775/225929072-2fa1741c-93ae-4b47-9bdf-af70f3d59910.png"
)] )]
@ -40,7 +39,11 @@ extern crate shim_3ds;
/// ///
/// This value was chosen to support crate dependencies which expected more stack than provided. It's suggested to use less stack if possible. /// This value was chosen to support crate dependencies which expected more stack than provided. It's suggested to use less stack if possible.
#[no_mangle] #[no_mangle]
#[cfg(feature = "big-stack")] // When building lib tests, we don't want to redefine the same symbol twice,
// since ctru-rs is both the crate under test and a dev-dependency (non-test).
// We might also be able to use #[linkage] for similar effect, but this way
// works without depending on another unstable feature.
#[cfg(all(feature = "big-stack", not(test)))]
static __stacksize__: usize = 2 * 1024 * 1024; // 2MB static __stacksize__: usize = 2 * 1024 * 1024; // 2MB
macro_rules! from_impl { macro_rules! from_impl {
@ -111,7 +114,4 @@ pub mod os;
pub mod prelude; pub mod prelude;
pub mod services; pub mod services;
#[cfg(test)]
mod test_runner;
pub use crate::error::{Error, Result}; pub use crate::error::{Error, Result};

6
ctru-rs/src/os.rs

@ -4,7 +4,7 @@
/// ///
/// # Example /// # Example
/// ``` /// ```
/// # // let _runner = test_runner::GdbRunner::default(); /// # let _runner = test_runner::GdbRunner::default();
/// let firm_version = ctru::os::firm_version(); /// let firm_version = ctru::os::firm_version();
/// assert_ne!(firm_version.major(), 0); /// assert_ne!(firm_version.major(), 0);
/// ///
@ -61,7 +61,7 @@ pub fn kernel_version() -> Version {
/// ///
/// # Example /// # Example
/// ``` /// ```
/// # // let _runner = test_runner::GdbRunner::default(); /// # let _runner = test_runner::GdbRunner::default();
/// let all_memory = ctru::os::MemRegion::All; /// let all_memory = ctru::os::MemRegion::All;
/// ///
/// assert!(all_memory.size() > 0); /// assert!(all_memory.size() > 0);
@ -111,7 +111,7 @@ impl MemRegion {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// # // let _runner = test_runner::GdbRunner::default(); /// let _runner = test_runner::GdbRunner::default();
/// let strength = ctru::os::WifiStrength::current(); /// let strength = ctru::os::WifiStrength::current();
/// assert!((strength as u8) < 4); /// assert!((strength as u8) < 4);
/// ``` /// ```

15
ctru-rs/src/services/am.rs

@ -61,7 +61,8 @@ impl Am {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -84,11 +85,13 @@ impl Am {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
/// use ctru::services::{fs::FsMediaType, am::Am}; /// use ctru::services::am::Am;
/// use ctru::services::fs::FsMediaType;
/// let app_manager = Am::new()?; /// let app_manager = Am::new()?;
/// ///
/// // Number of titles installed on the Nand storage. /// // Number of titles installed on the Nand storage.
@ -113,11 +116,13 @@ impl Am {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
/// use ctru::services::{fs::FsMediaType, am::Am}; /// use ctru::services::am::Am;
/// use ctru::services::fs::FsMediaType;
/// let app_manager = Am::new()?; /// let app_manager = Am::new()?;
/// ///
/// // Number of apps installed on the SD card storage /// // Number of apps installed on the SD card storage

6
ctru-rs/src/services/apt.rs

@ -16,7 +16,8 @@ impl Apt {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -44,7 +45,8 @@ impl Apt {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// use std::error::Error; /// use std::error::Error;
/// use ctru::services::apt::Apt; /// use ctru::services::apt::Apt;
/// ///

17
ctru-rs/src/services/cam.rs

@ -343,7 +343,8 @@ pub trait Camera {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -372,7 +373,8 @@ pub trait Camera {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -841,12 +843,13 @@ pub trait Camera {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # use std::time::Duration; /// # use std::time::Duration;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
/// use ctru::services::cam::{Cam, Camera, ViewSize, OutputFormat}; /// use ctru::services::cam::{Cam, Camera, OutputFormat, ViewSize};
/// let mut cam = Cam::new()?; /// let mut cam = Cam::new()?;
/// ///
/// // We borrow the inward facing `Camera`. /// // We borrow the inward facing `Camera`.
@ -951,7 +954,8 @@ impl Cam {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -984,7 +988,8 @@ impl Cam {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #

18
ctru-rs/src/services/cfgu.rs

@ -84,7 +84,8 @@ impl Cfgu {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -105,7 +106,8 @@ impl Cfgu {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -129,7 +131,8 @@ impl Cfgu {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -153,7 +156,8 @@ impl Cfgu {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -177,7 +181,8 @@ impl Cfgu {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -205,7 +210,8 @@ impl Cfgu {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #

51
ctru-rs/src/services/fs.rs

@ -136,7 +136,8 @@ pub struct Fs(());
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// use ctru::services::fs::Fs; /// use ctru::services::fs::Fs;
/// ///
/// let mut fs = Fs::new().unwrap(); /// let mut fs = Fs::new().unwrap();
@ -158,12 +159,14 @@ pub struct Archive {
/// ///
/// Create a new file and write bytes to it: /// Create a new file and write bytes to it:
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
/// use std::io::prelude::*; /// use std::io::prelude::*;
/// use ctru::services::fs::{Fs, File}; ///
/// use ctru::services::fs::{File, Fs};
/// ///
/// let mut fs = Fs::new()?; /// let mut fs = Fs::new()?;
/// let mut sdmc = fs.sdmc()?; /// let mut sdmc = fs.sdmc()?;
@ -174,12 +177,14 @@ pub struct Archive {
/// ///
/// Read the contents of a file into a `String`:: /// Read the contents of a file into a `String`::
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
/// use std::io::prelude::*; /// use std::io::prelude::*;
/// use ctru::services::fs::{Fs, File}; ///
/// use ctru::services::fs::{File, Fs};
/// ///
/// let mut fs = Fs::new()?; /// let mut fs = Fs::new()?;
/// let mut sdmc = fs.sdmc()?; /// let mut sdmc = fs.sdmc()?;
@ -196,13 +201,15 @@ pub struct Archive {
/// It can be more efficient to read the contents of a file with a buffered /// It can be more efficient to read the contents of a file with a buffered
/// `Read`er. This can be accomplished with `BufReader<R>`: /// `Read`er. This can be accomplished with `BufReader<R>`:
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
/// use std::io::BufReader;
/// use std::io::prelude::*; /// use std::io::prelude::*;
/// use ctru::services::fs::{Fs, File}; /// use std::io::BufReader;
///
/// use ctru::services::fs::{File, Fs};
/// ///
/// let mut fs = Fs::new()?; /// let mut fs = Fs::new()?;
/// let mut sdmc = fs.sdmc()?; /// let mut sdmc = fs.sdmc()?;
@ -247,22 +254,25 @@ pub struct Metadata {
/// ///
/// Opening a file to read: /// Opening a file to read:
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// use ctru::services::fs::{Fs, OpenOptions}; /// use ctru::services::fs::{Fs, OpenOptions};
/// ///
/// let mut fs = Fs::new().unwrap(); /// let mut fs = Fs::new().unwrap();
/// let mut sdmc_archive = fs.sdmc().unwrap(); /// let mut sdmc_archive = fs.sdmc().unwrap();
/// let file = OpenOptions::new() /// let result = OpenOptions::new()
/// .read(true) /// .read(true)
/// .archive(&sdmc_archive) /// .archive(&sdmc_archive)
/// .open("foo.txt") /// .open("foo.txt");
/// .unwrap(); ///
/// assert!(result.is_err());
/// ``` /// ```
/// ///
/// Opening a file for both reading and writing, as well as creating it if it /// Opening a file for both reading and writing, as well as creating it if it
/// doesn't exist: /// doesn't exist:
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// use ctru::services::fs::{Fs, OpenOptions}; /// use ctru::services::fs::{Fs, OpenOptions};
/// ///
/// let mut fs = Fs::new().unwrap(); /// let mut fs = Fs::new().unwrap();
@ -272,7 +282,7 @@ pub struct Metadata {
/// .write(true) /// .write(true)
/// .create(true) /// .create(true)
/// .archive(&sdmc_archive) /// .archive(&sdmc_archive)
/// .open("foo.txt") /// .open("/foo.txt")
/// .unwrap(); /// .unwrap();
/// ``` /// ```
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
@ -380,12 +390,14 @@ impl File {
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```
/// use ctru::services::fs::{Fs, File}; /// # let _runner = test_runner::GdbRunner::default();
/// use ctru::services::fs::{File, Fs};
/// ///
/// let mut fs = Fs::new().unwrap(); /// let mut fs = Fs::new().unwrap();
/// let mut sdmc_archive = fs.sdmc().unwrap(); /// let mut sdmc_archive = fs.sdmc().unwrap();
/// let mut f = File::open(&sdmc_archive, "/foo.txt").unwrap(); /// // Non-existent file:
/// assert!(File::open(&sdmc_archive, "/foo.txt").is_err());
/// ``` /// ```
pub fn open<P: AsRef<Path>>(arch: &Archive, path: P) -> IoResult<File> { pub fn open<P: AsRef<Path>>(arch: &Archive, path: P) -> IoResult<File> {
OpenOptions::new() OpenOptions::new()
@ -407,8 +419,9 @@ impl File {
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```
/// use ctru::services::fs::{Fs, File}; /// # let _runner = test_runner::GdbRunner::default();
/// use ctru::services::fs::{File, Fs};
/// ///
/// let mut fs = Fs::new().unwrap(); /// let mut fs = Fs::new().unwrap();
/// let mut sdmc_archive = fs.sdmc().unwrap(); /// let mut sdmc_archive = fs.sdmc().unwrap();

28
ctru-rs/src/services/gfx.rs

@ -253,7 +253,8 @@ impl Gfx {
/// ///
/// It's the same as calling: /// It's the same as calling:
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -268,7 +269,8 @@ impl Gfx {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -290,11 +292,13 @@ impl Gfx {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
/// use ctru::services::{gfx::Gfx, gspgpu::FramebufferFormat}; /// use ctru::services::gfx::Gfx;
/// use ctru::services::gspgpu::FramebufferFormat;
/// ///
/// // Top screen uses RGBA8, bottom screen uses RGB565. /// // Top screen uses RGBA8, bottom screen uses RGB565.
/// // The screen buffers are allocated in the standard HEAP memory, and not in VRAM. /// // The screen buffers are allocated in the standard HEAP memory, and not in VRAM.
@ -333,18 +337,20 @@ impl Gfx {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
/// use ctru::services::{apt::Apt, gfx::Gfx}; /// use ctru::services::apt::Apt;
/// use ctru::services::gfx::Gfx;
/// let apt = Apt::new()?; /// let apt = Apt::new()?;
/// let gfx = Gfx::new()?; /// let gfx = Gfx::new()?;
/// ///
/// // Simple main loop. /// // Simple main loop.
/// while apt.main_loop() { /// while apt.main_loop() {
/// // Main program logic /// // Main program logic
/// ///
/// // Wait for the screens to refresh. /// // Wait for the screens to refresh.
/// // This blocks the current thread to make it run at 60Hz. /// // This blocks the current thread to make it run at 60Hz.
/// gfx.wait_for_vblank(); /// gfx.wait_for_vblank();
@ -376,7 +382,8 @@ impl TopScreen3D<'_> {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -499,7 +506,10 @@ mod tests {
#[test] #[test]
fn gfx_duplicate() { fn gfx_duplicate() {
// We don't need to build a `Gfx` because the test runner has one already // NOTE: this is expected to fail if using the console test runner, since
// that necessarily creates a Gfx as part of its test setup:
let _gfx = Gfx::new().unwrap();
assert!(matches!(Gfx::new(), Err(Error::ServiceAlreadyActive))); assert!(matches!(Gfx::new(), Err(Error::ServiceAlreadyActive)));
} }
} }

21
ctru-rs/src/services/hid.rs

@ -87,7 +87,8 @@ impl Hid {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -113,7 +114,8 @@ impl Hid {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -135,7 +137,8 @@ impl Hid {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -164,7 +167,8 @@ impl Hid {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -193,7 +197,8 @@ impl Hid {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -225,7 +230,8 @@ impl Hid {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -258,7 +264,8 @@ impl Hid {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #

19
ctru-rs/src/services/ndsp/mod.rs

@ -3,8 +3,17 @@
//! The NDSP service is used to handle communications to the DSP processor present on the console's motherboard. //! The NDSP service is used to handle communications to the DSP processor present on the console's motherboard.
//! Thanks to the DSP processor the program can play sound effects and music on the console's built-in speakers or to any audio device //! Thanks to the DSP processor the program can play sound effects and music on the console's built-in speakers or to any audio device
//! connected via the audio jack. //! connected via the audio jack.
//!
//! To use NDSP audio, you will need to dump DSP firmware from a real 3DS using
//! something like [DSP1](https://www.gamebrew.org/wiki/DSP1_3DS).
//!
//! `libctru` expects to find it at `sdmc:/3ds/dspfirm.cdc` when initializing the NDSP service.
#![doc(alias = "audio")] #![doc(alias = "audio")]
// As a result of requiring DSP firmware to initialize, all of the doctests in
// this module are `no_run`, since Citra doesn't provide a stub for the DSP firmware:
// https://github.com/citra-emu/citra/issues/6111
pub mod wave; pub mod wave;
use wave::{Status, Wave}; use wave::{Status, Wave};
@ -114,7 +123,8 @@ impl Ndsp {
/// # Errors /// # Errors
/// ///
/// This function will return an error if an instance of the [`Ndsp`] struct already exists /// This function will return an error if an instance of the [`Ndsp`] struct already exists
/// or if there are any issues during initialization. /// or if there are any issues during initialization (for example, DSP firmware
/// cannot be found. See [module documentation](super::ndsp) for more details.).
/// ///
/// # Example /// # Example
/// ///
@ -499,14 +509,15 @@ impl Channel<'_> {
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
/// # use ctru::linear::LinearAllocator; /// # use ctru::linear::LinearAllocator;
/// use ctru::services::ndsp::{AudioFormat, Ndsp, wave::Wave}; /// use ctru::services::ndsp::wave::Wave;
/// use ctru::services::ndsp::{AudioFormat, Ndsp};
/// let ndsp = Ndsp::new()?; /// let ndsp = Ndsp::new()?;
/// let mut channel_0 = ndsp.channel(0)?; /// let mut channel_0 = ndsp.channel(0)?;
/// ///
/// # let _audio_data = Box::new_in([0u8; 96], LinearAllocator); /// # let audio_data = Box::new_in([0u8; 96], LinearAllocator);
/// ///
/// // Provide your own audio data. /// // Provide your own audio data.
/// let mut wave = Wave::new(_audio_data, AudioFormat::PCM16Stereo, false); /// let mut wave = Wave::new(audio_data, AudioFormat::PCM16Stereo, false);
/// ///
/// // Clear the audio queue and stop playback. /// // Clear the audio queue and stop playback.
/// channel_0.queue_wave(&mut wave); /// channel_0.queue_wave(&mut wave);

6
ctru-rs/src/services/ndsp/wave.rs

@ -36,9 +36,10 @@ impl Wave {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # #![feature(allocator_api)] /// # #![feature(allocator_api)]
/// # fn main() { /// # fn main() {
/// # let _runner = test_runner::GdbRunner::default();
/// # /// #
/// use ctru::linear::LinearAllocator; /// use ctru::linear::LinearAllocator;
/// use ctru::services::ndsp::{AudioFormat, wave::Wave}; /// use ctru::services::ndsp::{AudioFormat, wave::Wave};
@ -110,9 +111,10 @@ impl Wave {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # #![feature(allocator_api)] /// # #![feature(allocator_api)]
/// # fn main() { /// # fn main() {
/// # let _runner = test_runner::GdbRunner::default();
/// # /// #
/// # use ctru::linear::LinearAllocator; /// # use ctru::linear::LinearAllocator;
/// # let _audio_data = Box::new_in([0u8; 96], LinearAllocator); /// # let _audio_data = Box::new_in([0u8; 96], LinearAllocator);

12
ctru-rs/src/services/ps.rs

@ -63,7 +63,8 @@ impl Ps {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -86,7 +87,8 @@ impl Ps {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -110,7 +112,8 @@ impl Ps {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -134,7 +137,8 @@ impl Ps {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #

5
ctru-rs/src/services/romfs.rs

@ -45,7 +45,8 @@ impl RomFS {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -84,6 +85,8 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
// NOTE: this test only passes when run with a .3dsx, which for now requires separate build
// and run steps so the 3dsx is built before the runner looks for the executable
fn romfs_counter() { fn romfs_counter() {
let _romfs = RomFS::new().unwrap(); let _romfs = RomFS::new().unwrap();
let value = *ROMFS_ACTIVE.lock().unwrap(); let value = *ROMFS_ACTIVE.lock().unwrap();

12
ctru-rs/src/services/soc.rs

@ -30,7 +30,8 @@ impl Soc {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -56,7 +57,8 @@ impl Soc {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -97,7 +99,8 @@ impl Soc {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #
@ -130,7 +133,8 @@ impl Soc {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #

3
ctru-rs/src/services/sslc.rs

@ -12,7 +12,8 @@ impl SslC {
/// ///
/// # Example /// # Example
/// ///
/// ```no_run /// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error; /// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> { /// # fn main() -> Result<(), Box<dyn Error>> {
/// # /// #

73
ctru-rs/src/test_runner.rs

@ -1,73 +0,0 @@
//! Custom test runner for building/running unit tests on the 3DS.
extern crate test;
use std::io;
use test::{ColorConfig, OutputFormat, TestDescAndFn, TestFn, TestOpts};
use crate::prelude::*;
/// A custom runner to be used with `#[test_runner]`. This simple implementation
/// runs all tests in series, "failing" on the first one to panic (really, the
/// panic is just treated the same as any normal application panic).
pub(crate) fn run(tests: &[&TestDescAndFn]) {
let gfx = Gfx::new().unwrap();
let mut hid = Hid::new().unwrap();
let apt = Apt::new().unwrap();
let mut top_screen = gfx.top_screen.borrow_mut();
top_screen.set_wide_mode(true);
let _console = Console::new(top_screen);
let opts = TestOpts {
force_run_in_process: true,
run_tests: true,
// TODO: color doesn't work because of TERM/TERMINFO.
// With RomFS we might be able to fake this out nicely...
color: ColorConfig::AutoColor,
format: OutputFormat::Pretty,
// Hopefully this interface is more stable vs specifying individual options,
// and parsing the empty list of args should always work, I think.
// TODO Ideally we could pass actual std::env::args() here too
..test::test::parse_opts(&[]).unwrap().unwrap()
};
// Use the default test implementation with our hardcoded options
let _success = run_static_tests(&opts, tests).unwrap();
// Make sure the user can actually see the results before we exit
println!("Press START to exit.");
while apt.main_loop() {
gfx.wait_for_vblank();
hid.scan_input();
if hid.keys_down().contains(KeyPad::START) {
break;
}
}
}
/// Adapted from [`test::test_main_static`] and [`test::make_owned_test`].
fn run_static_tests(opts: &TestOpts, tests: &[&TestDescAndFn]) -> io::Result<bool> {
let tests = tests.iter().map(make_owned_test).collect();
test::run_tests_console(opts, tests)
}
/// Clones static values for putting into a dynamic vector, which test_main()
/// needs to hand out ownership of tests to parallel test runners.
///
/// This will panic when fed any dynamic tests, because they cannot be cloned.
fn make_owned_test(test: &&TestDescAndFn) -> TestDescAndFn {
match test.testfn {
TestFn::StaticTestFn(f) => TestDescAndFn {
testfn: TestFn::StaticTestFn(f),
desc: test.desc.clone(),
},
TestFn::StaticBenchFn(f) => TestDescAndFn {
testfn: TestFn::StaticBenchFn(f),
desc: test.desc.clone(),
},
_ => panic!("non-static tests passed to test::test_main_static"),
}
}

3
ctru-sys/build.rs

@ -171,7 +171,8 @@ fn check_libctru_version() -> Result<(String, String, String), Box<dyn Error>> {
if lib_version != crate_built_version { if lib_version != crate_built_version {
return Err(format!( return Err(format!(
"libctru version is {lib_version} but this crate was built for {crate_built_version}" "libctru version is {lib_version} but this crate was built for {crate_built_version}"
))?; )
.into());
} }
let Output { stdout, .. } = Command::new(pacman) let Output { stdout, .. } = Command::new(pacman)

8
ctru-sys/src/lib.rs

@ -14,3 +14,11 @@ include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
pub unsafe fn errno() -> s32 { pub unsafe fn errno() -> s32 {
*__errno() *__errno()
} }
// TODO: not sure if there's a better way to do this, but I have gotten myself
// with this a couple times so having the hint seems nice to have.
#[cfg(test)]
compile_error!(concat!(
"ctru-sys doesn't have tests and its lib test will fail to build at link time. ",
"Try specifying `--package ctru-rs` to build those tests.",
));

Loading…
Cancel
Save