From d7fde0815f9d094b0c9c261bcb6ce2350b3e59e6 Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Mon, 8 Jan 2024 09:17:03 -0800 Subject: [PATCH] Trying to make Mac CI fail gracefully This message may be repeated multiple times as I work on the CI configuration. Refs #129 --- .github/workflows/rust.yml | 9 ++++++++- examples/offscreen-apng.rs | 38 ++++++++++++++++++------------------- examples/offscreen.rs | 21 +++++++++----------- examples/shared/mod.rs | 39 ++++++++++++++++++++++++++++++++++++++ src/window.rs | 22 +++++++++++++++++++++ 5 files changed, 96 insertions(+), 33 deletions(-) create mode 100644 examples/shared/mod.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f3d77bc..0d7e7b8 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -45,4 +45,11 @@ jobs: # fixed purely by updating the rust version. if: matrix.version == 'stable' run: | - cargo test --all-features --all-targets + cargo test --all-features --all-targets -- --nocapture + env: + # When running on Mac OS CI, it's pretty common to not get an adapter + # returned. We don't want errors specifically caused by not being able + # to create a wgpu Adapter to cause unit test failures on CI. Long + # term it would be nice to have a reliable way to run Mac CI with a + # GPU adapter available. + NO_ADAPTER: ${{ matris.os == "macos-stable" && "github-ci" || "" }} \ No newline at end of file diff --git a/examples/offscreen-apng.rs b/examples/offscreen-apng.rs index e1e699b..348ceae 100644 --- a/examples/offscreen-apng.rs +++ b/examples/offscreen-apng.rs @@ -2,35 +2,33 @@ use std::time::Duration; use cushy::animation::easings::EaseInOutSine; use cushy::widget::MakeWidget; +use cushy::window::VirtualRecorderError; use figures::units::Px; use figures::{Point, Size}; +#[macro_use] +mod shared; + fn ui() -> impl MakeWidget { "Hello World".into_button().centered() } -fn main() { - let mut recorder = ui() - .build_recorder() - .size(Size::new(320, 240)) - .finish() - .unwrap(); +fn main() -> Result<(), VirtualRecorderError> { + let mut recorder = ui().build_recorder().size(Size::new(320, 240)).finish()?; let initial_point = Point::new(Px::new(140), Px::new(150)); recorder.set_cursor_position(initial_point); recorder.set_cursor_visible(true); - recorder.refresh().unwrap(); + recorder.refresh()?; let mut animation = recorder.record_animated_png(60); - animation - .animate_cursor_to( - Point::new(Px::new(160), Px::new(120)), - Duration::from_millis(250), - EaseInOutSine, - ) - .unwrap(); - animation.wait_for(Duration::from_millis(500)).unwrap(); - animation - .animate_cursor_to(initial_point, Duration::from_millis(250), EaseInOutSine) - .unwrap(); - animation.wait_for(Duration::from_millis(500)).unwrap(); - animation.write_to("examples/offscreen-apng.png").unwrap(); + animation.animate_cursor_to( + Point::new(Px::new(160), Px::new(120)), + Duration::from_millis(250), + EaseInOutSine, + )?; + animation.wait_for(Duration::from_millis(500))?; + animation.animate_cursor_to(initial_point, Duration::from_millis(250), EaseInOutSine)?; + animation.wait_for(Duration::from_millis(500))?; + animation.write_to("examples/offscreen-apng.png") } + +adapter_required_test!(main); diff --git a/examples/offscreen.rs b/examples/offscreen.rs index 7ac5d96..6e11ade 100644 --- a/examples/offscreen.rs +++ b/examples/offscreen.rs @@ -1,17 +1,17 @@ use cushy::widget::MakeWidget; +use cushy::window::VirtualRecorderError; use figures::Size; +#[macro_use] +mod shared; + fn ui() -> impl MakeWidget { "Hello World".into_button().centered() } -fn main() { +fn main() -> Result<(), VirtualRecorderError> { // The default recorder generated solid, rgb images. - let recorder = ui() - .build_recorder() - .size(Size::new(320, 240)) - .finish() - .unwrap(); + let recorder = ui().build_recorder().size(Size::new(320, 240)).finish()?; recorder.image().save("examples/offscreen.png").unwrap(); // Creating a recorder with alpha makes the virtual window transparent. @@ -19,12 +19,9 @@ fn main() { .build_recorder() .with_alpha() .size(Size::new(320, 240)) - .finish() - .unwrap(); + .finish()?; recorder.image().save("examples/offscreen.png").unwrap(); + Ok(()) } -#[test] -fn runs() { - main(); -} +adapter_required_test!(main); diff --git a/examples/shared/mod.rs b/examples/shared/mod.rs new file mode 100644 index 0000000..9f34abc --- /dev/null +++ b/examples/shared/mod.rs @@ -0,0 +1,39 @@ +/// This macro creates a unit test that calls the named function, and +/// potentially ignores a `NoAdapter` error. +/// +/// # Background +/// +/// On GitHub CI, it seems fairly common for the MacOS runners to be configured +/// in such a way that wgpu returns no adapters from a request with the default +/// settings. The default settings appear to imply as high of flexibility as +/// possible, and sometimes the runners succeed in returning an adapter. +/// +/// Because of these spurious failures, this macro checks for the environment +/// variable `NO_ADAPTER`. If it is set to a value, a warning will be printed +/// instead of panicking. +#[macro_export] +macro_rules! adapter_required_test { + ($name:ident) => { + #[test] + fn runs() { + let no_adapter_setting = std::env::var("NO_ADAPTER"); + match ($name(), no_adapter_setting) { + (Ok(()), _) => {} + (Err(cushy::window::VirtualRecorderError::NoAdapter), Ok(no_adapter)) + if !no_adapter.is_empty() => + { + let prefix = match no_adapter.as_ref() { + "github-ci" => "::warning::", + _ => "", + }; + println!( + "{prefix}Ignoring {}:{}: no graphics adapters available", + file!(), + stringify!($name) + ); + } + (Err(err), _) => unreachable!("Error testing example: {err}"), + } + } + }; +} diff --git a/src/window.rs b/src/window.rs index c93d2f2..a8c580c 100644 --- a/src/window.rs +++ b/src/window.rs @@ -3607,6 +3607,28 @@ impl From for VirtualRecorderError { } } +impl std::fmt::Display for VirtualRecorderError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + VirtualRecorderError::NoAdapter => { + f.write_str("no compatible graphics adapters were found") + } + VirtualRecorderError::RequestDevice(err) => { + write!(f, "error requesting graphics device: {err}") + } + VirtualRecorderError::TooLarge => { + f.write_str("the rendered surface is too large for this cpu architecture") + } + VirtualRecorderError::MapBuffer(err) => { + write!(f, "error reading rendered graphics data: {err}") + } + VirtualRecorderError::PngEncode(err) => write!(f, "error encoding png: {err}"), + } + } +} + +impl std::error::Error for VirtualRecorderError {} + /// A unique identifier of an input device. #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum DeviceId {