tests: Move all wgpu code out of test framework, make it abstract over any/no renderer, and make wgpu option if imgtests isn't enabled
This commit is contained in:
parent
c36f4cfe95
commit
bce0608e1f
|
@ -4271,7 +4271,6 @@ dependencies = [
|
|||
"ruffle_core",
|
||||
"ruffle_input_format",
|
||||
"ruffle_render",
|
||||
"ruffle_render_wgpu",
|
||||
"ruffle_socket_format",
|
||||
"ruffle_video_software",
|
||||
"serde",
|
||||
|
@ -4922,8 +4921,11 @@ name = "tests"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
"image",
|
||||
"libtest-mimic",
|
||||
"ruffle_core",
|
||||
"ruffle_render_wgpu",
|
||||
"ruffle_test_framework",
|
||||
"walkdir",
|
||||
]
|
||||
|
|
|
@ -2270,6 +2270,13 @@ impl PlayerBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the rendering backend of the player.
|
||||
#[inline]
|
||||
pub fn with_boxed_renderer(mut self, renderer: Box<dyn RenderBackend>) -> Self {
|
||||
self.renderer = Some(renderer);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the storage backend of the player.
|
||||
#[inline]
|
||||
pub fn with_storage(mut self, storage: impl 'static + StorageBackend) -> Self {
|
||||
|
|
|
@ -11,16 +11,21 @@ version.workspace = true
|
|||
# Enable running image comparison tests. This is off by default,
|
||||
# since the images we compare against are generated on CI, and may
|
||||
# not match your local machine's Vulkan version / image output.
|
||||
imgtests = ["ruffle_test_framework/imgtests"]
|
||||
imgtests = ["ruffle_test_framework/ruffle_video_software", "ruffle_render_wgpu"]
|
||||
jpegxr = ["ruffle_test_framework/jpegxr"]
|
||||
lzma = ["ruffle_test_framework/lzma"]
|
||||
|
||||
[dependencies]
|
||||
ruffle_render_wgpu = { path = "../render/wgpu", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
ruffle_core = { path = "../core", features = ["deterministic", "timeline_debug", "avm_debug", "audio", "mp3", "default_font"] }
|
||||
ruffle_test_framework = { path = "framework" }
|
||||
libtest-mimic = "0.6.1"
|
||||
walkdir = "2.4.0"
|
||||
anyhow = "1.0.75"
|
||||
image = { version = "0.24.7", default-features = false, features = ["png"] }
|
||||
futures = "0.3.29"
|
||||
|
||||
[[test]]
|
||||
name = "tests"
|
||||
|
|
|
@ -10,7 +10,6 @@ version.workspace = true
|
|||
[dependencies]
|
||||
futures = "0.3.29"
|
||||
ruffle_core = { path = "../../core", features = ["deterministic", "timeline_debug", "avm_debug", "audio", "mp3", "default_font"] }
|
||||
ruffle_render_wgpu = { path = "../../render/wgpu" }
|
||||
ruffle_render = { path = "../../render" }
|
||||
ruffle_input_format = { path = "../input-format" }
|
||||
ruffle_socket_format = { path = "../socket-format" }
|
||||
|
@ -32,6 +31,5 @@ anyhow = "1.0.75"
|
|||
async-channel = "1.9.0"
|
||||
|
||||
[features]
|
||||
imgtests = ["ruffle_video_software"]
|
||||
jpegxr = ["ruffle_core/jpegxr"]
|
||||
lzma = ["ruffle_core/lzma"]
|
|
@ -1,43 +1,33 @@
|
|||
use ruffle_render_wgpu::backend::request_adapter_and_device;
|
||||
use ruffle_render_wgpu::descriptors::Descriptors;
|
||||
use ruffle_render_wgpu::wgpu;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use crate::options::RenderOptions;
|
||||
|
||||
/*
|
||||
It can be expensive to construct WGPU, much less Descriptors, so we put it off as long as we can
|
||||
and share it across tests in the same process.
|
||||
pub use ruffle_render::backend::RenderBackend;
|
||||
|
||||
Remember:
|
||||
`cargo test` will run all tests in the same process.
|
||||
`cargo nextest run` will create a different process per test.
|
||||
pub trait Environment {
|
||||
/// Checks if this environment supports rendering the given test.
|
||||
///
|
||||
/// This isn't a guarantee that it _will_ construct a renderer,
|
||||
/// but rather a check that it theoretically _can_.
|
||||
///
|
||||
/// This should be a cheap test to filter out test viability early,
|
||||
/// without creating any expensive rendering overhead.
|
||||
fn is_render_supported(&self, _requirements: &RenderOptions) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
For `cargo test` it's relatively okay if we spend the time to construct descriptors once,
|
||||
but for `cargo nextest run` it's a big cost per test if it's not going to use it.
|
||||
*/
|
||||
|
||||
fn create_wgpu_device() -> Option<(wgpu::Instance, wgpu::Adapter, wgpu::Device, wgpu::Queue)> {
|
||||
let instance = wgpu::Instance::new(Default::default());
|
||||
futures::executor::block_on(request_adapter_and_device(
|
||||
wgpu::Backends::all(),
|
||||
&instance,
|
||||
None,
|
||||
Default::default(),
|
||||
None,
|
||||
))
|
||||
.ok()
|
||||
.map(|(adapter, device, queue)| (instance, adapter, device, queue))
|
||||
}
|
||||
|
||||
fn build_wgpu_descriptors() -> Option<Arc<Descriptors>> {
|
||||
if let Some((instance, adapter, device, queue)) = create_wgpu_device() {
|
||||
Some(Arc::new(Descriptors::new(instance, adapter, device, queue)))
|
||||
} else {
|
||||
/// Creates a render backend for the given test.
|
||||
///
|
||||
/// If [Self::is_render_supported] returned false, this won't be attempted.
|
||||
fn create_renderer(&self, _width: u32, _height: u32) -> Option<Box<dyn RenderBackend>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wgpu_descriptors() -> Option<&'static Arc<Descriptors>> {
|
||||
// TODO: Use `std::sync::LazyLock` once it's stabilized?
|
||||
static WGPU: OnceLock<Option<Arc<Descriptors>>> = OnceLock::new();
|
||||
WGPU.get_or_init(build_wgpu_descriptors).as_ref()
|
||||
/// Gets the name of this environment, for use in test reporting.
|
||||
///
|
||||
/// This name may be used in file paths, so it should contain appropriate characters for such.
|
||||
fn name(&self) -> String;
|
||||
|
||||
/// Capture the stage rendered out by the given render backend.
|
||||
///
|
||||
/// The provided backend will have previously been created by [Environment::create_renderer].
|
||||
fn capture_renderer(&self, renderer: &mut Box<dyn RenderBackend>) -> image::RgbaImage;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::backends::TestAudioBackend;
|
||||
use crate::environment::wgpu_descriptors;
|
||||
use crate::environment::Environment;
|
||||
use crate::image_trigger::ImageTrigger;
|
||||
use anyhow::{anyhow, Result};
|
||||
use approx::assert_relative_eq;
|
||||
|
@ -7,7 +7,6 @@ use regex::Regex;
|
|||
use ruffle_core::tag_utils::SwfMovie;
|
||||
use ruffle_core::{PlayerBuilder, ViewportDimensions};
|
||||
use ruffle_render::quality::StageQuality;
|
||||
use ruffle_render_wgpu::wgpu;
|
||||
use serde::Deserialize;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs;
|
||||
|
@ -141,6 +140,7 @@ impl PlayerOptions {
|
|||
&self,
|
||||
mut player_builder: PlayerBuilder,
|
||||
movie: &SwfMovie,
|
||||
environment: &impl Environment,
|
||||
) -> Result<PlayerBuilder> {
|
||||
if let Some(max_execution_duration) = self.max_execution_duration {
|
||||
player_builder = player_builder.with_max_execution_duration(max_execution_duration);
|
||||
|
@ -161,27 +161,16 @@ impl PlayerOptions {
|
|||
};
|
||||
|
||||
if let Some(render_options) = &self.with_renderer {
|
||||
use ruffle_render_wgpu::backend::WgpuRenderBackend;
|
||||
use ruffle_render_wgpu::target::TextureTarget;
|
||||
|
||||
if let Some(descriptors) = wgpu_descriptors() {
|
||||
if render_options.is_supported(&descriptors.adapter) {
|
||||
let target = TextureTarget::new(&descriptors.device, (width, height))
|
||||
.map_err(|e| anyhow!(e.to_string()))?;
|
||||
|
||||
player_builder = player_builder
|
||||
.with_quality(match render_options.sample_count {
|
||||
player_builder = player_builder.with_quality(match render_options.sample_count {
|
||||
16 => StageQuality::High16x16,
|
||||
8 => StageQuality::High8x8,
|
||||
4 => StageQuality::High,
|
||||
2 => StageQuality::Medium,
|
||||
_ => StageQuality::Low,
|
||||
})
|
||||
.with_renderer(
|
||||
WgpuRenderBackend::new(descriptors.clone(), target)
|
||||
.map_err(|e| anyhow!(e.to_string()))?,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(renderer) = environment.create_renderer(width, height) {
|
||||
player_builder = player_builder.with_boxed_renderer(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -189,7 +178,7 @@ impl PlayerOptions {
|
|||
player_builder = player_builder.with_audio(TestAudioBackend::default());
|
||||
}
|
||||
|
||||
#[cfg(feature = "imgtests")]
|
||||
#[cfg(feature = "ruffle_video_software")]
|
||||
if self.with_video {
|
||||
use ruffle_video_software::backend::SoftwareVideoBackend;
|
||||
player_builder = player_builder.with_video(SoftwareVideoBackend::new())
|
||||
|
@ -198,19 +187,13 @@ impl PlayerOptions {
|
|||
Ok(player_builder)
|
||||
}
|
||||
|
||||
pub fn can_run(&self, check_renderer: bool) -> bool {
|
||||
pub fn can_run(&self, check_renderer: bool, environment: &impl Environment) -> bool {
|
||||
if let Some(render) = &self.with_renderer {
|
||||
// If we don't actually want to check the renderer (ie we're just listing potential tests),
|
||||
// don't spend the cost to create it
|
||||
if check_renderer && !render.optional {
|
||||
if let Some(descriptors) = wgpu_descriptors() {
|
||||
if !render.is_supported(&descriptors.adapter) {
|
||||
if check_renderer && !render.optional && !environment.is_render_supported(render) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
@ -224,31 +207,27 @@ pub struct ImageComparison {
|
|||
pub trigger: ImageTrigger,
|
||||
}
|
||||
|
||||
#[cfg(feature = "imgtests")]
|
||||
fn calc_difference(lhs: u8, rhs: u8) -> u8 {
|
||||
(lhs as i16 - rhs as i16).unsigned_abs() as u8
|
||||
}
|
||||
|
||||
impl ImageComparison {
|
||||
#[cfg(feature = "imgtests")]
|
||||
pub fn test(
|
||||
&self,
|
||||
name: &str,
|
||||
actual_image: image::RgbaImage,
|
||||
expected_image: image::RgbaImage,
|
||||
test_path: &Path,
|
||||
adapter_info: wgpu::AdapterInfo,
|
||||
environment_name: String,
|
||||
known_failure: bool,
|
||||
) -> Result<()> {
|
||||
use anyhow::Context;
|
||||
|
||||
let suffix = format!("{}-{:?}", std::env::consts::OS, adapter_info.backend);
|
||||
|
||||
let save_actual_image = || {
|
||||
if !known_failure {
|
||||
// If we're expecting failure, spamming files isn't productive.
|
||||
actual_image
|
||||
.save(test_path.join(format!("{name}.actual-{suffix}.png")))
|
||||
.save(test_path.join(format!("{name}.actual-{environment_name}.png")))
|
||||
.context("Couldn't save actual image")
|
||||
} else {
|
||||
Ok(())
|
||||
|
@ -323,7 +302,7 @@ impl ImageComparison {
|
|||
difference_color,
|
||||
)
|
||||
.context("Couldn't create color difference image")?
|
||||
.save(test_path.join(format!("{name}.difference-color-{suffix}.png")))
|
||||
.save(test_path.join(format!("{name}.difference-color-{environment_name}.png")))
|
||||
.context("Couldn't save color difference image")?;
|
||||
}
|
||||
|
||||
|
@ -343,7 +322,7 @@ impl ImageComparison {
|
|||
difference_alpha,
|
||||
)
|
||||
.context("Couldn't create alpha difference image")?
|
||||
.save(test_path.join(format!("{name}.difference-alpha-{suffix}.png")))
|
||||
.save(test_path.join(format!("{name}.difference-alpha-{environment_name}.png")))
|
||||
.context("Couldn't save alpha difference image")?;
|
||||
}
|
||||
}
|
||||
|
@ -367,8 +346,8 @@ impl ImageComparison {
|
|||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct RenderOptions {
|
||||
optional: bool,
|
||||
sample_count: u32,
|
||||
exclude_warp: bool,
|
||||
pub sample_count: u32,
|
||||
pub exclude_warp: bool,
|
||||
}
|
||||
|
||||
impl Default for RenderOptions {
|
||||
|
@ -380,14 +359,3 @@ impl Default for RenderOptions {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOptions {
|
||||
pub fn is_supported(&self, adapter: &wgpu::Adapter) -> bool {
|
||||
let info = adapter.get_info();
|
||||
// 5140 & 140 is WARP, https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/d3d10-graphics-programming-guide-dxgi#new-info-about-enumerating-adapters-for-windows-8
|
||||
if self.exclude_warp && cfg!(windows) && info.vendor == 5140 && info.device == 140 {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::backends::{TestLogBackend, TestNavigatorBackend};
|
||||
use crate::environment::Environment;
|
||||
use crate::fs_commands::{FsCommand, TestFsCommandProvider};
|
||||
use crate::image_trigger::ImageTrigger;
|
||||
use crate::options::ImageComparison;
|
||||
|
@ -15,7 +16,7 @@ use ruffle_input_format::{
|
|||
AutomatedEvent, InputInjector, MouseButton as InputMouseButton,
|
||||
TextControlCode as InputTextControlCode,
|
||||
};
|
||||
use ruffle_render_wgpu::descriptors::Descriptors;
|
||||
use ruffle_render::backend::null::NullRenderer;
|
||||
use ruffle_socket_format::SocketEvent;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
@ -29,6 +30,7 @@ pub fn run_swf(
|
|||
socket_events: Option<Vec<SocketEvent>>,
|
||||
before_start: impl FnOnce(Arc<Mutex<Player>>) -> Result<()>,
|
||||
before_end: impl FnOnce(Arc<Mutex<Player>>) -> Result<()>,
|
||||
environment: &impl Environment,
|
||||
) -> Result<String> {
|
||||
let base_path = Path::new(&test.output_path).parent().unwrap();
|
||||
let mut executor = NullExecutor::new();
|
||||
|
@ -65,18 +67,20 @@ pub fn run_swf(
|
|||
let player = test
|
||||
.options
|
||||
.player_options
|
||||
.setup(builder, &movie)?
|
||||
.setup(builder, &movie, environment)?
|
||||
.with_movie(movie)
|
||||
.with_autoplay(true) //.tick() requires playback
|
||||
.build();
|
||||
|
||||
let mut images = test.options.image_comparisons.clone();
|
||||
// If we set anything but a null renderer, then the Environment must have created a valid renderer.
|
||||
let has_renderer = player
|
||||
.lock()
|
||||
.unwrap()
|
||||
.renderer()
|
||||
.downcast_ref::<NullRenderer>()
|
||||
.is_none();
|
||||
|
||||
let wgpu_descriptors = if cfg!(feature = "imgtests") && !images.is_empty() {
|
||||
crate::environment::wgpu_descriptors()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut images = test.options.image_comparisons.clone();
|
||||
|
||||
before_start(player.clone())?;
|
||||
|
||||
|
@ -142,10 +146,11 @@ pub fn run_swf(
|
|||
capture_and_compare_image(
|
||||
base_path,
|
||||
&player,
|
||||
wgpu_descriptors,
|
||||
&name,
|
||||
image_comparison,
|
||||
test.options.known_failure,
|
||||
environment,
|
||||
has_renderer,
|
||||
)?;
|
||||
} else {
|
||||
return Err(anyhow!("Encountered fscommand to capture and compare image '{name}', but no [image_comparison] was set up for this."));
|
||||
|
@ -214,10 +219,11 @@ pub fn run_swf(
|
|||
capture_and_compare_image(
|
||||
base_path,
|
||||
&player,
|
||||
wgpu_descriptors,
|
||||
&name,
|
||||
image_comparison,
|
||||
test.options.known_failure,
|
||||
environment,
|
||||
has_renderer,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
@ -234,10 +240,11 @@ pub fn run_swf(
|
|||
capture_and_compare_image(
|
||||
base_path,
|
||||
&player,
|
||||
wgpu_descriptors,
|
||||
&name,
|
||||
image_comparison,
|
||||
test.options.known_failure,
|
||||
environment,
|
||||
has_renderer,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
@ -260,48 +267,22 @@ pub fn run_swf(
|
|||
Ok(normalized_trace)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "imgtests"))]
|
||||
fn capture_and_compare_image(
|
||||
_base_path: &Path,
|
||||
_player: &Arc<Mutex<Player>>,
|
||||
_wgpu_descriptors: Option<&Arc<Descriptors>>,
|
||||
_name: &str,
|
||||
_image_comparison: ImageComparison,
|
||||
known_failure: bool,
|
||||
) -> Result<()> {
|
||||
if known_failure {
|
||||
// It's possible that the trace output matched but the image might not.
|
||||
// If we aren't checking the image, pretend the match failed (which makes it actually pass, since it's expecting failure).
|
||||
Err(anyhow!(
|
||||
"Not checking images, pretending this failed since we don't know if it worked."
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "imgtests")]
|
||||
fn capture_and_compare_image(
|
||||
base_path: &Path,
|
||||
player: &Arc<Mutex<Player>>,
|
||||
wgpu_descriptors: Option<&Arc<Descriptors>>,
|
||||
name: &String,
|
||||
image_comparison: ImageComparison,
|
||||
known_failure: bool,
|
||||
environment: &impl Environment,
|
||||
has_renderer: bool,
|
||||
) -> Result<()> {
|
||||
use anyhow::Context;
|
||||
use ruffle_render_wgpu::backend::WgpuRenderBackend;
|
||||
use ruffle_render_wgpu::target::TextureTarget;
|
||||
|
||||
if let Some(wgpu_descriptors) = wgpu_descriptors {
|
||||
if has_renderer {
|
||||
let mut player_lock = player.lock().unwrap();
|
||||
player_lock.render();
|
||||
let renderer = player_lock
|
||||
.renderer_mut()
|
||||
.downcast_mut::<WgpuRenderBackend<TextureTarget>>()
|
||||
.unwrap();
|
||||
|
||||
let actual_image = renderer.capture_frame().expect("Failed to capture image");
|
||||
let actual_image = environment.capture_renderer(player_lock.renderer_mut());
|
||||
|
||||
let expected_image_path = base_path.join(format!("{name}.expected.png"));
|
||||
if expected_image_path.is_file() {
|
||||
|
@ -314,7 +295,7 @@ fn capture_and_compare_image(
|
|||
actual_image,
|
||||
expected_image,
|
||||
base_path,
|
||||
wgpu_descriptors.adapter.get_info(),
|
||||
environment.name(),
|
||||
known_failure,
|
||||
)?;
|
||||
} else if !known_failure {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::environment::Environment;
|
||||
use crate::options::TestOptions;
|
||||
use crate::runner::run_swf;
|
||||
use crate::set_logger;
|
||||
|
@ -49,6 +50,7 @@ impl Test {
|
|||
&self,
|
||||
before_start: impl FnOnce(Arc<Mutex<Player>>) -> Result<()>,
|
||||
before_end: impl FnOnce(Arc<Mutex<Player>>) -> Result<()>,
|
||||
environment: &impl Environment,
|
||||
) -> std::result::Result<(), libtest_mimic::Failed> {
|
||||
set_logger();
|
||||
let injector = if self.input_path.is_file() {
|
||||
|
@ -61,17 +63,27 @@ impl Test {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
let output = run_swf(self, injector, socket_events, before_start, before_end)?;
|
||||
let output = run_swf(
|
||||
self,
|
||||
injector,
|
||||
socket_events,
|
||||
before_start,
|
||||
before_end,
|
||||
environment,
|
||||
)?;
|
||||
self.compare_output(&output)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn should_run(&self, check_renderer: bool) -> bool {
|
||||
pub fn should_run(&self, check_renderer: bool, environment: &impl Environment) -> bool {
|
||||
if self.options.ignore {
|
||||
return false;
|
||||
}
|
||||
self.options.required_features.can_run()
|
||||
&& self.options.player_options.can_run(check_renderer)
|
||||
&& self
|
||||
.options
|
||||
.player_options
|
||||
.can_run(check_renderer, environment)
|
||||
}
|
||||
|
||||
pub fn compare_output(&self, actual_output: &str) -> Result<()> {
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
use image::RgbaImage;
|
||||
use ruffle_test_framework::environment::{Environment, RenderBackend};
|
||||
|
||||
#[cfg(feature = "imgtests")]
|
||||
use ruffle_test_framework::options::RenderOptions;
|
||||
|
||||
#[cfg(feature = "imgtests")]
|
||||
use ruffle_render_wgpu::{
|
||||
backend::{request_adapter_and_device, WgpuRenderBackend},
|
||||
descriptors::Descriptors,
|
||||
target::TextureTarget,
|
||||
wgpu,
|
||||
};
|
||||
|
||||
#[cfg(feature = "imgtests")]
|
||||
use {std::sync::Arc, std::sync::OnceLock};
|
||||
|
||||
pub struct NativeEnvironment;
|
||||
|
||||
impl NativeEnvironment {
|
||||
#[cfg(feature = "imgtests")]
|
||||
fn descriptors(&self) -> Option<&Arc<Descriptors>> {
|
||||
WGPU.get_or_init(build_wgpu_descriptors).as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Environment for NativeEnvironment {
|
||||
#[cfg(feature = "imgtests")]
|
||||
fn is_render_supported(&self, requirements: &RenderOptions) -> bool {
|
||||
if let Some(descriptors) = self.descriptors() {
|
||||
let adapter_info = descriptors.adapter.get_info();
|
||||
let is_warp =
|
||||
cfg!(windows) && adapter_info.vendor == 5140 && adapter_info.device == 140;
|
||||
|
||||
!requirements.exclude_warp || !is_warp
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "imgtests")]
|
||||
fn create_renderer(&self, width: u32, height: u32) -> Option<Box<dyn RenderBackend>> {
|
||||
if let Some(descriptors) = self.descriptors() {
|
||||
let target = TextureTarget::new(&descriptors.device, (width, height)).expect(
|
||||
"WGPU Texture Target creation must not fail, everything was checked ahead of time",
|
||||
);
|
||||
|
||||
Some(Box::new(
|
||||
WgpuRenderBackend::new(descriptors.clone(), target)
|
||||
.expect("WGPU Render backend creation must not fail, everything was checked ahead of time"),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "imgtests"))]
|
||||
fn name(&self) -> String {
|
||||
std::env::consts::OS.to_string()
|
||||
}
|
||||
|
||||
#[cfg(feature = "imgtests")]
|
||||
fn name(&self) -> String {
|
||||
if let Some(descriptors) = self.descriptors() {
|
||||
let adapter_info = descriptors.adapter.get_info();
|
||||
format!("{}-{:?}", std::env::consts::OS, adapter_info.backend)
|
||||
} else {
|
||||
std::env::consts::OS.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "imgtests"))]
|
||||
fn capture_renderer(&self, _backend: &mut Box<dyn RenderBackend>) -> RgbaImage {
|
||||
panic!("Cannot capture renderer as imgtests are not enabled")
|
||||
}
|
||||
|
||||
#[cfg(feature = "imgtests")]
|
||||
fn capture_renderer(&self, backend: &mut Box<dyn RenderBackend>) -> RgbaImage {
|
||||
let renderer = backend
|
||||
.downcast_mut::<WgpuRenderBackend<TextureTarget>>()
|
||||
.unwrap();
|
||||
|
||||
renderer.capture_frame().expect("Failed to capture image")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "imgtests")]
|
||||
static WGPU: OnceLock<Option<Arc<Descriptors>>> = OnceLock::new();
|
||||
|
||||
/*
|
||||
It can be expensive to construct WGPU, much less Descriptors, so we put it off as long as we can
|
||||
and share it across tests in the same process.
|
||||
|
||||
Remember:
|
||||
`cargo test` will run all tests in the same process.
|
||||
`cargo nextest run` will create a different process per test.
|
||||
|
||||
For `cargo test` it's relatively okay if we spend the time to construct descriptors once,
|
||||
but for `cargo nextest run` it's a big cost per test if it's not going to use it.
|
||||
*/
|
||||
|
||||
#[cfg(feature = "imgtests")]
|
||||
fn create_wgpu_device() -> Option<(wgpu::Instance, wgpu::Adapter, wgpu::Device, wgpu::Queue)> {
|
||||
let instance = wgpu::Instance::new(Default::default());
|
||||
futures::executor::block_on(request_adapter_and_device(
|
||||
wgpu::Backends::all(),
|
||||
&instance,
|
||||
None,
|
||||
Default::default(),
|
||||
None,
|
||||
))
|
||||
.ok()
|
||||
.map(|(adapter, device, queue)| (instance, adapter, device, queue))
|
||||
}
|
||||
|
||||
#[cfg(feature = "imgtests")]
|
||||
fn build_wgpu_descriptors() -> Option<Arc<Descriptors>> {
|
||||
if let Some((instance, adapter, device, queue)) = create_wgpu_device() {
|
||||
Some(Arc::new(Descriptors::new(instance, adapter, device, queue)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
|
@ -1,12 +1,15 @@
|
|||
use crate::external_interface::ExternalInterfaceTestProvider;
|
||||
use ruffle_core::external::Value as ExternalValue;
|
||||
use ruffle_test_framework::environment::Environment;
|
||||
use ruffle_test_framework::options::TestOptions;
|
||||
use ruffle_test_framework::set_logger;
|
||||
use ruffle_test_framework::test::Test;
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn external_interface_avm1() -> Result<(), libtest_mimic::Failed> {
|
||||
pub fn external_interface_avm1(
|
||||
environment: &impl Environment,
|
||||
) -> Result<(), libtest_mimic::Failed> {
|
||||
set_logger();
|
||||
Test::from_options(
|
||||
TestOptions {
|
||||
|
@ -59,10 +62,13 @@ pub fn external_interface_avm1() -> Result<(), libtest_mimic::Failed> {
|
|||
));
|
||||
Ok(())
|
||||
},
|
||||
environment,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn external_interface_avm2() -> Result<(), libtest_mimic::Failed> {
|
||||
pub fn external_interface_avm2(
|
||||
environment: &impl Environment,
|
||||
) -> Result<(), libtest_mimic::Failed> {
|
||||
set_logger();
|
||||
Test::from_options(
|
||||
TestOptions {
|
||||
|
@ -106,5 +112,6 @@ pub fn external_interface_avm2() -> Result<(), libtest_mimic::Failed> {
|
|||
));
|
||||
Ok(())
|
||||
},
|
||||
environment,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
//!
|
||||
//! Trace output can be compared with correct output from the official Flash Player.
|
||||
|
||||
use crate::environment::NativeEnvironment;
|
||||
use crate::external_interface::tests::{external_interface_avm1, external_interface_avm2};
|
||||
use crate::shared_object::{shared_object_avm1, shared_object_avm2, shared_object_self_ref_avm1};
|
||||
use anyhow::Context;
|
||||
|
@ -11,6 +12,7 @@ use ruffle_test_framework::test::Test;
|
|||
use std::panic::{catch_unwind, resume_unwind};
|
||||
use std::path::Path;
|
||||
|
||||
mod environment;
|
||||
mod external_interface;
|
||||
mod shared_object;
|
||||
|
||||
|
@ -55,9 +57,9 @@ fn main() {
|
|||
let test = Test::from_options_file(file.path(), name.clone())
|
||||
.with_context(|| format!("Couldn't create test {name}"))
|
||||
.unwrap();
|
||||
let ignore = !test.should_run(!args.list);
|
||||
let ignore = !test.should_run(!args.list, &NativeEnvironment);
|
||||
let mut trial = Trial::test(test.name.to_string(), move || {
|
||||
let unwind_result = catch_unwind(|| test.run(|_| Ok(()), |_| Ok(())));
|
||||
let unwind_result = catch_unwind(|| test.run(|_| Ok(()), |_| Ok(()), &NativeEnvironment));
|
||||
if test.options.known_failure {
|
||||
match unwind_result {
|
||||
Ok(Ok(())) => Err(
|
||||
|
@ -83,20 +85,21 @@ fn main() {
|
|||
.collect();
|
||||
|
||||
// Manual tests here, since #[test] doesn't work once we use our own test harness
|
||||
tests.push(Trial::test("shared_object_avm1", shared_object_avm1));
|
||||
tests.push(Trial::test(
|
||||
"shared_object_self_ref_avm1",
|
||||
shared_object_self_ref_avm1,
|
||||
));
|
||||
tests.push(Trial::test("shared_object_avm2", shared_object_avm2));
|
||||
tests.push(Trial::test(
|
||||
"external_interface_avm1",
|
||||
external_interface_avm1,
|
||||
));
|
||||
tests.push(Trial::test(
|
||||
"external_interface_avm2",
|
||||
external_interface_avm2,
|
||||
));
|
||||
tests.push(Trial::test("shared_object_avm1", || {
|
||||
shared_object_avm1(&NativeEnvironment)
|
||||
}));
|
||||
tests.push(Trial::test("shared_object_self_ref_avm1", || {
|
||||
shared_object_self_ref_avm1(&NativeEnvironment)
|
||||
}));
|
||||
tests.push(Trial::test("shared_object_avm2", || {
|
||||
shared_object_avm2(&NativeEnvironment)
|
||||
}));
|
||||
tests.push(Trial::test("external_interface_avm1", || {
|
||||
external_interface_avm1(&NativeEnvironment)
|
||||
}));
|
||||
tests.push(Trial::test("external_interface_avm2", || {
|
||||
external_interface_avm2(&NativeEnvironment)
|
||||
}));
|
||||
|
||||
tests.sort_unstable_by(|a, b| a.name().cmp(b.name()));
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use ruffle_core::backend::storage::{MemoryStorageBackend, StorageBackend};
|
||||
use ruffle_test_framework::environment::Environment;
|
||||
use ruffle_test_framework::options::TestOptions;
|
||||
use ruffle_test_framework::set_logger;
|
||||
use ruffle_test_framework::test::Test;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn shared_object_avm1() -> Result<(), libtest_mimic::Failed> {
|
||||
pub fn shared_object_avm1(environment: &impl Environment) -> Result<(), libtest_mimic::Failed> {
|
||||
set_logger();
|
||||
// Test SharedObject persistence. Run an SWF that saves data
|
||||
// to a shared object twice and verify that the data is saved.
|
||||
|
@ -29,6 +30,7 @@ pub fn shared_object_avm1() -> Result<(), libtest_mimic::Failed> {
|
|||
std::mem::swap(player.storage_mut(), &mut memory_storage_backend);
|
||||
Ok(())
|
||||
},
|
||||
environment,
|
||||
)?;
|
||||
|
||||
// Verify that the flash cookie matches the expected one
|
||||
|
@ -58,12 +60,15 @@ pub fn shared_object_avm1() -> Result<(), libtest_mimic::Failed> {
|
|||
Ok(())
|
||||
},
|
||||
|_| Ok(()),
|
||||
environment,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn shared_object_self_ref_avm1() -> Result<(), libtest_mimic::Failed> {
|
||||
pub fn shared_object_self_ref_avm1(
|
||||
environment: &impl Environment,
|
||||
) -> Result<(), libtest_mimic::Failed> {
|
||||
set_logger();
|
||||
// Test SharedObject persistence. Run an SWF that saves data
|
||||
// to a shared object twice and verify that the data is saved.
|
||||
|
@ -88,6 +93,7 @@ pub fn shared_object_self_ref_avm1() -> Result<(), libtest_mimic::Failed> {
|
|||
std::mem::swap(player.storage_mut(), &mut memory_storage_backend);
|
||||
Ok(())
|
||||
},
|
||||
environment,
|
||||
)?;
|
||||
|
||||
// Verify that the flash cookie matches the expected one
|
||||
|
@ -117,12 +123,13 @@ pub fn shared_object_self_ref_avm1() -> Result<(), libtest_mimic::Failed> {
|
|||
Ok(())
|
||||
},
|
||||
|_| Ok(()),
|
||||
environment,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn shared_object_avm2() -> Result<(), libtest_mimic::Failed> {
|
||||
pub fn shared_object_avm2(environment: &impl Environment) -> Result<(), libtest_mimic::Failed> {
|
||||
set_logger();
|
||||
// Test SharedObject persistence. Run an SWF that saves data
|
||||
// to a shared object twice and verify that the data is saved.
|
||||
|
@ -147,6 +154,7 @@ pub fn shared_object_avm2() -> Result<(), libtest_mimic::Failed> {
|
|||
std::mem::swap(player.storage_mut(), &mut memory_storage_backend);
|
||||
Ok(())
|
||||
},
|
||||
environment,
|
||||
)?;
|
||||
|
||||
// Verify that the flash cookie matches the expected one
|
||||
|
@ -176,6 +184,7 @@ pub fn shared_object_avm2() -> Result<(), libtest_mimic::Failed> {
|
|||
Ok(())
|
||||
},
|
||||
|_player| Ok(()),
|
||||
environment,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in New Issue