tests: Make tests use a virtual filesystem, which currently maps to the regular physical FS rooted in a test directory
This commit is contained in:
parent
c7a1e1178a
commit
d6f16b0be5
|
@ -4379,6 +4379,7 @@ dependencies = [
|
|||
"env_logger",
|
||||
"futures",
|
||||
"image",
|
||||
"percent-encoding",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"ruffle_core",
|
||||
|
@ -4391,6 +4392,7 @@ dependencies = [
|
|||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
"vfs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5563,6 +5565,12 @@ version = "0.9.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "vfs"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e4fe92cfc1bad19c19925d5eee4b30584dbbdee4ff10183b261acccbef74e2d"
|
||||
|
||||
[[package]]
|
||||
name = "vswhom"
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -533,8 +533,7 @@ pub fn resolve_url_with_relative_base_path<NavigatorType: NavigatorBackend>(
|
|||
}
|
||||
}
|
||||
|
||||
/// This is the fetch implementation for the TestNavigatorBackend and the
|
||||
/// NullNavigatorBackend.
|
||||
/// This is the fetch implementation for NullNavigatorBackend.
|
||||
///
|
||||
/// It tries to fetch the given URL as a local path and read and return
|
||||
/// its content. It returns an ErrorResponse if the URL is not valid, not
|
||||
|
|
|
@ -27,6 +27,8 @@ serde = "1.0.192"
|
|||
toml = "0.8.8"
|
||||
anyhow = "1.0.75"
|
||||
async-channel = "1.9.0"
|
||||
vfs = "0.10.0"
|
||||
percent-encoding = "2.3.0"
|
||||
|
||||
[features]
|
||||
jpegxr = ["ruffle_core/jpegxr"]
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
use crate::backends::TestLogBackend;
|
||||
use crate::util::read_bytes;
|
||||
use async_channel::Receiver;
|
||||
use percent_encoding::percent_decode_str;
|
||||
use ruffle_core::backend::log::LogBackend;
|
||||
use ruffle_core::backend::navigator::{
|
||||
fetch_path, resolve_url_with_relative_base_path, ErrorResponse, NavigationMethod,
|
||||
NavigatorBackend, NullExecutor, NullSpawner, OwnedFuture, Request, SuccessResponse,
|
||||
async_return, create_fetch_error, ErrorResponse, NavigationMethod, NavigatorBackend,
|
||||
NullExecutor, NullSpawner, OwnedFuture, Request, SuccessResponse,
|
||||
};
|
||||
use ruffle_core::indexmap::IndexMap;
|
||||
use ruffle_core::loader::Error;
|
||||
use ruffle_core::socket::{ConnectionState, SocketAction, SocketHandle};
|
||||
use ruffle_socket_format::SocketEvent;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::time::Duration;
|
||||
use url::{ParseError, Url};
|
||||
use vfs::VfsPath;
|
||||
|
||||
/// A `NavigatorBackend` used by tests that supports logging fetch requests.
|
||||
///
|
||||
|
@ -27,21 +29,21 @@ use url::{ParseError, Url};
|
|||
/// URLs can be used in Flash Player when writing tests
|
||||
pub struct TestNavigatorBackend {
|
||||
spawner: NullSpawner,
|
||||
relative_base_path: PathBuf,
|
||||
relative_base_path: VfsPath,
|
||||
socket_events: Option<Vec<SocketEvent>>,
|
||||
log: Option<TestLogBackend>,
|
||||
}
|
||||
|
||||
impl TestNavigatorBackend {
|
||||
pub fn new(
|
||||
path: &Path,
|
||||
path: VfsPath,
|
||||
executor: &NullExecutor,
|
||||
socket_events: Option<Vec<SocketEvent>>,
|
||||
log: Option<TestLogBackend>,
|
||||
) -> Result<Self, std::io::Error> {
|
||||
Ok(Self {
|
||||
spawner: executor.spawner(),
|
||||
relative_base_path: path.canonicalize()?,
|
||||
relative_base_path: path,
|
||||
socket_events,
|
||||
log,
|
||||
})
|
||||
|
@ -125,16 +127,86 @@ impl NavigatorBackend for TestNavigatorBackend {
|
|||
}
|
||||
}
|
||||
|
||||
fetch_path(
|
||||
self,
|
||||
"TestNavigatorBackend",
|
||||
request.url(),
|
||||
Some(&self.relative_base_path),
|
||||
)
|
||||
let url = match self.resolve_url(request.url()) {
|
||||
Ok(url) => url,
|
||||
Err(e) => return async_return(create_fetch_error(request.url(), e)),
|
||||
};
|
||||
|
||||
let base_path = self.relative_base_path.clone();
|
||||
|
||||
Box::pin(async move {
|
||||
let path = if url.scheme() == "file" {
|
||||
// Flash supports query parameters with local urls.
|
||||
// SwfMovie takes care of exposing those to ActionScript -
|
||||
// when we actually load a filesystem url, strip them out.
|
||||
let mut filesystem_url = url.clone();
|
||||
filesystem_url.set_query(None);
|
||||
|
||||
base_path
|
||||
.join(
|
||||
percent_decode_str(filesystem_url.path())
|
||||
.decode_utf8()
|
||||
.map_err(|e| ErrorResponse {
|
||||
url: url.to_string(),
|
||||
error: Error::FetchError(e.to_string()),
|
||||
})?,
|
||||
)
|
||||
.map_err(|e| ErrorResponse {
|
||||
url: url.to_string(),
|
||||
error: Error::FetchError(e.to_string()),
|
||||
})?
|
||||
} else {
|
||||
// Turn a url like https://localhost/foo/bar to {base_path}/localhost/foo/bar
|
||||
let mut path = base_path.clone();
|
||||
if let Some(host) = url.host_str() {
|
||||
path = path.join(host).map_err(|e| ErrorResponse {
|
||||
url: url.to_string(),
|
||||
error: Error::FetchError(e.to_string()),
|
||||
})?;
|
||||
}
|
||||
if let Some(remaining) = url.path().strip_prefix('/') {
|
||||
path = path
|
||||
.join(percent_decode_str(remaining).decode_utf8().map_err(|e| {
|
||||
ErrorResponse {
|
||||
url: url.to_string(),
|
||||
error: Error::FetchError(e.to_string()),
|
||||
}
|
||||
})?)
|
||||
.map_err(|e| ErrorResponse {
|
||||
url: url.to_string(),
|
||||
error: Error::FetchError(e.to_string()),
|
||||
})?;
|
||||
}
|
||||
path
|
||||
};
|
||||
|
||||
let body = read_bytes(&path).map_err(|error| ErrorResponse {
|
||||
url: url.to_string(),
|
||||
error: Error::FetchError(error.to_string()),
|
||||
})?;
|
||||
Ok(SuccessResponse {
|
||||
url: url.to_string(),
|
||||
body,
|
||||
status: 0,
|
||||
redirected: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_url(&self, url: &str) -> Result<Url, ParseError> {
|
||||
resolve_url_with_relative_base_path(self, self.relative_base_path.clone(), url)
|
||||
let mut base_url = Url::parse("file:///")?;
|
||||
|
||||
// Make sure we have a trailing slash, so that joining a request url like 'data.txt'
|
||||
// gets appended, rather than replacing the last component.
|
||||
base_url.path_segments_mut().unwrap().push("");
|
||||
if let Ok(parsed_url) = base_url.join(url) {
|
||||
Ok(self.pre_process_url(parsed_url))
|
||||
} else {
|
||||
match Url::parse(url) {
|
||||
Ok(parsed_url) => Ok(self.pre_process_url(parsed_url)),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_future(&mut self, future: OwnedFuture<(), Error>) {
|
||||
|
|
|
@ -5,7 +5,10 @@ pub mod options;
|
|||
pub mod runner;
|
||||
pub mod test;
|
||||
|
||||
pub use vfs;
|
||||
|
||||
mod backends;
|
||||
mod util;
|
||||
|
||||
pub fn set_logger() {
|
||||
let _ = env_logger::Builder::from_env(
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use crate::backends::TestAudioBackend;
|
||||
use crate::environment::{Environment, RenderInterface};
|
||||
use crate::image_trigger::ImageTrigger;
|
||||
use crate::util::write_image;
|
||||
use anyhow::{anyhow, Result};
|
||||
use approx::assert_relative_eq;
|
||||
use image::ImageOutputFormat;
|
||||
use regex::Regex;
|
||||
use ruffle_core::tag_utils::SwfMovie;
|
||||
use ruffle_core::{PlayerBuilder, ViewportDimensions};
|
||||
|
@ -10,9 +12,8 @@ use ruffle_render::backend::RenderBackend;
|
|||
use ruffle_render::quality::StageQuality;
|
||||
use serde::Deserialize;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
use vfs::VfsPath;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
|
@ -20,7 +21,7 @@ pub struct TestOptions {
|
|||
pub num_frames: Option<u32>,
|
||||
pub num_ticks: Option<u32>,
|
||||
pub tick_rate: Option<f64>,
|
||||
pub output_path: PathBuf,
|
||||
pub output_path: String,
|
||||
pub sleep_to_meet_frame_rate: bool,
|
||||
pub image_comparisons: HashMap<String, ImageComparison>,
|
||||
pub ignore: bool,
|
||||
|
@ -37,7 +38,7 @@ impl Default for TestOptions {
|
|||
num_frames: None,
|
||||
num_ticks: None,
|
||||
tick_rate: None,
|
||||
output_path: PathBuf::from("output.txt"),
|
||||
output_path: "output.txt".to_string(),
|
||||
sleep_to_meet_frame_rate: false,
|
||||
image_comparisons: Default::default(),
|
||||
ignore: false,
|
||||
|
@ -51,8 +52,8 @@ impl Default for TestOptions {
|
|||
}
|
||||
|
||||
impl TestOptions {
|
||||
pub fn read<P: AsRef<Path>>(path: P) -> Result<Self> {
|
||||
let result: Self = toml::from_str(&fs::read_to_string(path)?)?;
|
||||
pub fn read(path: &VfsPath) -> Result<Self> {
|
||||
let result: Self = toml::from_str(&path.read_to_string()?)?;
|
||||
result.validate()?;
|
||||
Ok(result)
|
||||
}
|
||||
|
@ -75,8 +76,8 @@ impl TestOptions {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn output_path(&self, test_directory: &Path) -> PathBuf {
|
||||
test_directory.join(&self.output_path)
|
||||
pub fn output_path(&self, test_directory: &VfsPath) -> Result<VfsPath> {
|
||||
Ok(test_directory.join(&self.output_path)?)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,7 +217,7 @@ impl ImageComparison {
|
|||
name: &str,
|
||||
actual_image: image::RgbaImage,
|
||||
expected_image: image::RgbaImage,
|
||||
test_path: &Path,
|
||||
test_path: &VfsPath,
|
||||
environment_name: String,
|
||||
known_failure: bool,
|
||||
) -> Result<()> {
|
||||
|
@ -225,9 +226,11 @@ impl ImageComparison {
|
|||
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-{environment_name}.png")))
|
||||
.context("Couldn't save actual image")
|
||||
write_image(
|
||||
&test_path.join(format!("{name}.actual-{environment_name}.png"))?,
|
||||
&actual_image,
|
||||
ImageOutputFormat::Png,
|
||||
)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
@ -295,14 +298,17 @@ impl ImageComparison {
|
|||
|
||||
if !known_failure {
|
||||
// If we're expecting failure, spamming files isn't productive.
|
||||
image::RgbImage::from_raw(
|
||||
let difference_image = image::RgbImage::from_raw(
|
||||
actual_image.width(),
|
||||
actual_image.height(),
|
||||
difference_color,
|
||||
)
|
||||
.context("Couldn't create color difference image")?
|
||||
.save(test_path.join(format!("{name}.difference-color-{environment_name}.png")))
|
||||
.context("Couldn't save color difference image")?;
|
||||
.context("Couldn't create color difference image")?;
|
||||
write_image(
|
||||
&test_path.join(format!("{name}.difference-color-{environment_name}.png"))?,
|
||||
&difference_image,
|
||||
ImageOutputFormat::Png,
|
||||
)?;
|
||||
}
|
||||
|
||||
if is_alpha_different {
|
||||
|
@ -315,14 +321,18 @@ impl ImageComparison {
|
|||
|
||||
if !known_failure {
|
||||
// If we're expecting failure, spamming files isn't productive.
|
||||
image::GrayImage::from_raw(
|
||||
let difference_image = image::GrayImage::from_raw(
|
||||
actual_image.width(),
|
||||
actual_image.height(),
|
||||
difference_alpha,
|
||||
)
|
||||
.context("Couldn't create alpha difference image")?
|
||||
.save(test_path.join(format!("{name}.difference-alpha-{environment_name}.png")))
|
||||
.context("Couldn't save alpha difference image")?;
|
||||
.context("Couldn't create alpha difference image")?;
|
||||
write_image(
|
||||
&test_path
|
||||
.join(format!("{name}.difference-alpha-{environment_name}.png"))?,
|
||||
&difference_image,
|
||||
ImageOutputFormat::Png,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,9 @@ use crate::fs_commands::{FsCommand, TestFsCommandProvider};
|
|||
use crate::image_trigger::ImageTrigger;
|
||||
use crate::options::ImageComparison;
|
||||
use crate::test::Test;
|
||||
use crate::util::{read_bytes, write_image};
|
||||
use anyhow::{anyhow, Result};
|
||||
use image::ImageOutputFormat;
|
||||
use ruffle_core::backend::navigator::NullExecutor;
|
||||
use ruffle_core::events::MouseButton as RuffleMouseButton;
|
||||
use ruffle_core::events::{KeyCode, TextControlCode as RuffleTextControlCode};
|
||||
|
@ -17,9 +19,9 @@ use ruffle_input_format::{
|
|||
};
|
||||
use ruffle_render::backend::{RenderBackend, ViewportDimensions};
|
||||
use ruffle_socket_format::SocketEvent;
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
use vfs::VfsPath;
|
||||
|
||||
/// Loads an SWF and runs it through the Ruffle core for a number of frames.
|
||||
/// Tests that the trace output matches the given expected output.
|
||||
|
@ -34,7 +36,6 @@ pub fn run_swf(
|
|||
renderer: Option<(Box<dyn RenderInterface>, Box<dyn RenderBackend>)>,
|
||||
viewport_dimensions: ViewportDimensions,
|
||||
) -> Result<String> {
|
||||
let base_path = Path::new(&test.output_path).parent().unwrap();
|
||||
let mut executor = NullExecutor::new();
|
||||
let mut frame_time = 1000.0 / movie.frame_rate().to_f64();
|
||||
if let Some(tr) = test.options.tick_rate {
|
||||
|
@ -46,7 +47,7 @@ pub fn run_swf(
|
|||
let log = TestLogBackend::default();
|
||||
let (fs_command_provider, fs_commands) = TestFsCommandProvider::new();
|
||||
let navigator = TestNavigatorBackend::new(
|
||||
base_path,
|
||||
test.root_path.clone(),
|
||||
&executor,
|
||||
socket_events,
|
||||
test.options.log_fetch.then(|| log.clone()),
|
||||
|
@ -87,7 +88,7 @@ pub fn run_swf(
|
|||
if test.options.num_frames.is_none() && test.options.num_ticks.is_none() {
|
||||
return Err(anyhow!(
|
||||
"Test {} must specify at least one of num_frames or num_ticks",
|
||||
test.swf_path.to_string_lossy()
|
||||
&test.name
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -144,7 +145,7 @@ pub fn run_swf(
|
|||
return Err(anyhow!("Encountered fscommand to capture and compare image '{name}', but the trigger was expected to be {:?}", image_comparison.trigger));
|
||||
}
|
||||
capture_and_compare_image(
|
||||
base_path,
|
||||
&test.root_path,
|
||||
&player,
|
||||
&name,
|
||||
image_comparison,
|
||||
|
@ -216,7 +217,7 @@ pub fn run_swf(
|
|||
.remove(&name)
|
||||
.expect("Name was just retrieved from map, should not be missing!");
|
||||
capture_and_compare_image(
|
||||
base_path,
|
||||
&test.root_path,
|
||||
&player,
|
||||
&name,
|
||||
image_comparison,
|
||||
|
@ -236,7 +237,7 @@ pub fn run_swf(
|
|||
.expect("Name was just retrieved from map, should not be missing!");
|
||||
|
||||
capture_and_compare_image(
|
||||
base_path,
|
||||
&test.root_path,
|
||||
&player,
|
||||
&name,
|
||||
image_comparison,
|
||||
|
@ -265,7 +266,7 @@ pub fn run_swf(
|
|||
}
|
||||
|
||||
fn capture_and_compare_image(
|
||||
base_path: &Path,
|
||||
base_path: &VfsPath,
|
||||
player: &Arc<Mutex<Player>>,
|
||||
name: &String,
|
||||
image_comparison: ImageComparison,
|
||||
|
@ -280,9 +281,9 @@ fn capture_and_compare_image(
|
|||
|
||||
let actual_image = render_interface.capture(player_lock.renderer_mut());
|
||||
|
||||
let expected_image_path = base_path.join(format!("{name}.expected.png"));
|
||||
if expected_image_path.is_file() {
|
||||
let expected_image = image::open(&expected_image_path)
|
||||
let expected_image_path = base_path.join(format!("{name}.expected.png"))?;
|
||||
if expected_image_path.is_file()? {
|
||||
let expected_image = image::load_from_memory(&read_bytes(&expected_image_path)?)
|
||||
.context("Failed to open expected image")?
|
||||
.into_rgba8();
|
||||
|
||||
|
@ -296,7 +297,7 @@ fn capture_and_compare_image(
|
|||
)?;
|
||||
} else if !known_failure {
|
||||
// If we're expecting this to be wrong, don't save a likely wrong image
|
||||
actual_image.save(expected_image_path)?;
|
||||
write_image(&expected_image_path, &actual_image, ImageOutputFormat::Png)?;
|
||||
}
|
||||
} else if known_failure {
|
||||
// It's possible that the trace output matched but the image might not.
|
||||
|
|
|
@ -2,30 +2,32 @@ use crate::environment::Environment;
|
|||
use crate::options::TestOptions;
|
||||
use crate::runner::run_swf;
|
||||
use crate::set_logger;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use crate::util::read_bytes;
|
||||
use anyhow::{anyhow, Result};
|
||||
use pretty_assertions::Comparison;
|
||||
use ruffle_core::tag_utils::SwfMovie;
|
||||
use ruffle_core::Player;
|
||||
use ruffle_input_format::InputInjector;
|
||||
use ruffle_socket_format::SocketEvent;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use vfs::VfsPath;
|
||||
|
||||
pub struct Test {
|
||||
pub options: TestOptions,
|
||||
pub swf_path: PathBuf,
|
||||
pub input_path: PathBuf,
|
||||
pub socket_path: PathBuf,
|
||||
pub output_path: PathBuf,
|
||||
pub swf_path: VfsPath,
|
||||
pub input_path: VfsPath,
|
||||
pub socket_path: VfsPath,
|
||||
pub output_path: VfsPath,
|
||||
pub root_path: VfsPath,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl Test {
|
||||
pub fn from_options(options: TestOptions, test_dir: &Path, name: String) -> Result<Self> {
|
||||
let swf_path = test_dir.join("test.swf");
|
||||
let input_path = test_dir.join("input.json");
|
||||
let socket_path = test_dir.join("socket.json");
|
||||
let output_path = options.output_path(test_dir);
|
||||
pub fn from_options(options: TestOptions, test_dir: VfsPath, name: String) -> Result<Self> {
|
||||
let swf_path = test_dir.join("test.swf")?;
|
||||
let input_path = test_dir.join("input.json")?;
|
||||
let socket_path = test_dir.join("socket.json")?;
|
||||
let output_path = options.output_path(&test_dir)?;
|
||||
|
||||
Ok(Self {
|
||||
options,
|
||||
|
@ -33,20 +35,11 @@ impl Test {
|
|||
input_path,
|
||||
socket_path,
|
||||
output_path,
|
||||
root_path: test_dir,
|
||||
name,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_options_file(options_path: &Path, name: String) -> Result<Self> {
|
||||
Self::from_options(
|
||||
TestOptions::read(options_path).context("Couldn't load test options")?,
|
||||
options_path
|
||||
.parent()
|
||||
.context("Couldn't get test directory")?,
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn run(
|
||||
&self,
|
||||
mut before_start: impl FnMut(Arc<Mutex<Player>>) -> Result<()>,
|
||||
|
@ -54,8 +47,10 @@ impl Test {
|
|||
environment: &impl Environment,
|
||||
) -> Result<()> {
|
||||
set_logger();
|
||||
let movie =
|
||||
SwfMovie::from_path(&self.swf_path, None).map_err(|e| anyhow!(e.to_string()))?;
|
||||
|
||||
let data = read_bytes(&self.swf_path)?;
|
||||
let movie = SwfMovie::from_data(&data, format!("file:///{}", self.swf_path.as_str()), None)
|
||||
.map_err(|e| anyhow!(e.to_string()))?;
|
||||
let viewport_dimensions = self.options.player_options.viewport_dimensions(&movie);
|
||||
let renderers = self
|
||||
.options
|
||||
|
@ -94,16 +89,18 @@ impl Test {
|
|||
}
|
||||
|
||||
fn socket_events(&self) -> Result<Option<Vec<SocketEvent>>> {
|
||||
Ok(if self.socket_path.is_file() {
|
||||
Some(SocketEvent::from_file(&self.socket_path)?)
|
||||
Ok(if self.socket_path.is_file()? {
|
||||
Some(SocketEvent::from_reader(
|
||||
&read_bytes(&self.socket_path)?[..],
|
||||
)?)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
fn input_injector(&self) -> Result<InputInjector> {
|
||||
Ok(if self.input_path.is_file() {
|
||||
InputInjector::from_file(&self.input_path)?
|
||||
Ok(if self.input_path.is_file()? {
|
||||
InputInjector::from_reader(&read_bytes(&self.input_path)?[..])?
|
||||
} else {
|
||||
InputInjector::empty()
|
||||
})
|
||||
|
@ -121,7 +118,7 @@ impl Test {
|
|||
}
|
||||
|
||||
pub fn compare_output(&self, actual_output: &str) -> Result<()> {
|
||||
let expected_output = std::fs::read_to_string(&self.output_path)?.replace("\r\n", "\n");
|
||||
let expected_output = self.output_path.read_to_string()?.replace("\r\n", "\n");
|
||||
|
||||
if let Some(approximations) = &self.options.approximations {
|
||||
if actual_output.lines().count() != expected_output.lines().count() {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
use image::{EncodableLayout, ImageBuffer, ImageOutputFormat, Pixel, PixelWithColorType};
|
||||
use std::io::{Cursor, Read, Write};
|
||||
use std::ops::Deref;
|
||||
use vfs::{VfsError, VfsPath};
|
||||
|
||||
pub fn read_bytes(path: &VfsPath) -> Result<Vec<u8>, VfsError> {
|
||||
let mut bytes = Vec::with_capacity(path.metadata()?.len as usize);
|
||||
path.open_file()?.read_to_end(&mut bytes)?;
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
pub fn write_bytes(path: &VfsPath, data: &[u8]) -> Result<(), VfsError> {
|
||||
path.create_file()?.write_all(data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_image<P, Container>(
|
||||
path: &VfsPath,
|
||||
image: &ImageBuffer<P, Container>,
|
||||
format: ImageOutputFormat,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
P: Pixel + PixelWithColorType,
|
||||
[P::Subpixel]: EncodableLayout,
|
||||
Container: Deref<Target = [P::Subpixel]>,
|
||||
{
|
||||
let mut buffer = vec![];
|
||||
image.write_to(&mut Cursor::new(&mut buffer), format)?;
|
||||
write_bytes(path, &buffer)?;
|
||||
Ok(())
|
||||
}
|
|
@ -57,6 +57,19 @@ impl InputInjector {
|
|||
})
|
||||
}
|
||||
|
||||
/// Construct an input injector from an input reader and a platform-specific
|
||||
/// event sink.
|
||||
pub fn from_reader<R>(reader: R) -> Result<Self, io::Error>
|
||||
where
|
||||
R: io::Read,
|
||||
{
|
||||
Ok(Self {
|
||||
items: from_reader(reader)?,
|
||||
pos: 0,
|
||||
buttons: MouseButtons::empty(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create an empty input injector with no input to inject.
|
||||
///
|
||||
/// Useful to represent a missing input file in cases where providing one
|
||||
|
|
|
@ -25,4 +25,11 @@ impl SocketEvent {
|
|||
|
||||
Ok(from_reader(file)?)
|
||||
}
|
||||
|
||||
pub fn from_reader<R>(reader: R) -> Result<Vec<Self>, io::Error>
|
||||
where
|
||||
R: io::Read,
|
||||
{
|
||||
Ok(from_reader(reader)?)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ 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 ruffle_test_framework::vfs::{PhysicalFS, VfsPath};
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn external_interface_avm1(
|
||||
environment: &impl Environment,
|
||||
|
@ -16,7 +16,7 @@ pub fn external_interface_avm1(
|
|||
num_frames: Some(1),
|
||||
..Default::default()
|
||||
},
|
||||
Path::new("tests/swfs/avm1/external_interface/"),
|
||||
VfsPath::new(PhysicalFS::new("tests/swfs/avm1/external_interface/")),
|
||||
"external_interface_avm1".to_string(),
|
||||
)?
|
||||
.run(
|
||||
|
@ -75,7 +75,7 @@ pub fn external_interface_avm2(
|
|||
num_frames: Some(1),
|
||||
..Default::default()
|
||||
},
|
||||
Path::new("tests/swfs/avm2/external_interface/"),
|
||||
VfsPath::new(PhysicalFS::new("tests/swfs/avm2/external_interface/")),
|
||||
"external_interface_avm2".to_string(),
|
||||
)?
|
||||
.run(
|
||||
|
|
|
@ -8,8 +8,10 @@ use crate::shared_object::{shared_object_avm1, shared_object_avm2, shared_object
|
|||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use libtest_mimic::{Arguments, Trial};
|
||||
use ruffle_test_framework::options::TestOptions;
|
||||
use ruffle_test_framework::test::Test;
|
||||
use std::panic::{catch_unwind, resume_unwind};
|
||||
use ruffle_test_framework::vfs::{PhysicalFS, VfsPath};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||
use std::path::Path;
|
||||
|
||||
mod environment;
|
||||
|
@ -54,11 +56,17 @@ fn main() {
|
|||
.to_string_lossy()
|
||||
.replace('\\', "/");
|
||||
if is_candidate(&args, &name) {
|
||||
let test = Test::from_options_file(file.path(), name.clone())
|
||||
let root = VfsPath::new(PhysicalFS::new(file.path().parent().unwrap() ));
|
||||
let test = Test::from_options(
|
||||
TestOptions::read(&root.join("test.toml").unwrap()).context("Couldn't load test options").unwrap(),
|
||||
root,
|
||||
name.clone(),
|
||||
)
|
||||
.with_context(|| format!("Couldn't create test {name}"))
|
||||
.unwrap();
|
||||
let ignore = !test.should_run(!args.list, &NativeEnvironment);
|
||||
let mut trial = Trial::test(test.name.to_string(), move || {
|
||||
let test = AssertUnwindSafe(test);
|
||||
let unwind_result = catch_unwind(|| test.run(|_| Ok(()), |_| Ok(()), &NativeEnvironment));
|
||||
if test.options.known_failure {
|
||||
match unwind_result {
|
||||
|
|
|
@ -3,7 +3,7 @@ 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;
|
||||
use ruffle_test_framework::vfs::{PhysicalFS, VfsPath};
|
||||
|
||||
pub fn shared_object_avm1(environment: &impl Environment) -> Result<(), libtest_mimic::Failed> {
|
||||
set_logger();
|
||||
|
@ -19,7 +19,7 @@ pub fn shared_object_avm1(environment: &impl Environment) -> Result<(), libtest_
|
|||
output_path: "output1.txt".into(),
|
||||
..Default::default()
|
||||
},
|
||||
Path::new("tests/swfs/avm1/shared_object/"),
|
||||
VfsPath::new(PhysicalFS::new("tests/swfs/avm1/shared_object/")),
|
||||
"shared_object_avm1".to_string(),
|
||||
)?
|
||||
.run(
|
||||
|
@ -49,7 +49,7 @@ pub fn shared_object_avm1(environment: &impl Environment) -> Result<(), libtest_
|
|||
output_path: "output2.txt".into(),
|
||||
..Default::default()
|
||||
},
|
||||
Path::new("tests/swfs/avm1/shared_object/"),
|
||||
VfsPath::new(PhysicalFS::new("tests/swfs/avm1/shared_object/")),
|
||||
"shared_object_avm1".to_string(),
|
||||
)?
|
||||
.run(
|
||||
|
@ -82,7 +82,7 @@ pub fn shared_object_self_ref_avm1(
|
|||
output_path: "output1.txt".into(),
|
||||
..Default::default()
|
||||
},
|
||||
Path::new("tests/swfs/avm1/shared_object_self_ref/"),
|
||||
VfsPath::new(PhysicalFS::new("tests/swfs/avm1/shared_object_self_ref/")),
|
||||
"shared_object_self_ref_avm1".to_string(),
|
||||
)?
|
||||
.run(
|
||||
|
@ -112,7 +112,7 @@ pub fn shared_object_self_ref_avm1(
|
|||
output_path: "output2.txt".into(),
|
||||
..Default::default()
|
||||
},
|
||||
Path::new("tests/swfs/avm1/shared_object_self_ref/"),
|
||||
VfsPath::new(PhysicalFS::new("tests/swfs/avm1/shared_object_self_ref/")),
|
||||
"shared_object_self_ref_avm1".to_string(),
|
||||
)?
|
||||
.run(
|
||||
|
@ -143,7 +143,7 @@ pub fn shared_object_avm2(environment: &impl Environment) -> Result<(), libtest_
|
|||
output_path: "output1.txt".into(),
|
||||
..Default::default()
|
||||
},
|
||||
Path::new("tests/swfs/avm2/shared_object/"),
|
||||
VfsPath::new(PhysicalFS::new("tests/swfs/avm2/shared_object/")),
|
||||
"shared_object_avm2".to_string(),
|
||||
)?
|
||||
.run(
|
||||
|
@ -173,7 +173,7 @@ pub fn shared_object_avm2(environment: &impl Environment) -> Result<(), libtest_
|
|||
output_path: "output2.txt".into(),
|
||||
..Default::default()
|
||||
},
|
||||
Path::new("tests/swfs/avm2/shared_object/"),
|
||||
VfsPath::new(PhysicalFS::new("tests/swfs/avm2/shared_object/")),
|
||||
"shared_object_avm2".to_string(),
|
||||
)?
|
||||
.run(
|
||||
|
|
|
@ -44,7 +44,7 @@ transform.matrix = (a=1, b=0, c=0, d=1, tx=0, ty=0)
|
|||
transform.concatenatedMatrix = (a=1, b=0, c=0, d=1, tx=0, ty=0)
|
||||
transform.pixelBounds = (x=0, y=0, w=0, h=0)
|
||||
useHandCursor = true
|
||||
_url = movieclip_default_state/test.swf
|
||||
_url = /test.swf
|
||||
getBounds(this).xMin = 6710886.4
|
||||
getBounds(this).xMax = 6710886.4
|
||||
getBounds(this).yMin = 6710886.4
|
||||
|
|
|
@ -1,26 +1,552 @@
|
|||
// SWF Version 10
|
||||
|
||||
/*
|
||||
* This test tests the default state of a MovieClip.
|
||||
* The default state consists of the values of all properties and the results of some getter functions
|
||||
* after the MovieClip has been created with createEmptyMovieClip.
|
||||
* This test tests the different states of a MovieClip.
|
||||
* A state of a MovieClip consists of the values of all properties and the results of some getter
|
||||
* functions of the MovieClip.
|
||||
*
|
||||
* The other MovieClip states are tested in movieclip_state_values.
|
||||
* The states a MovieClip can be in are the following:
|
||||
* - Default State
|
||||
* This is the default state that a MovieClip is in after it's created with createEmptyMovieClip.
|
||||
* It is the only one not tested in this test. Instead, it gets tested in movieclip_default_state.
|
||||
* - Initial Loading State
|
||||
* This state is entered when FP / Ruffle try to load the MovieClip. As soon as FP / Ruffle
|
||||
* either load the first frame of the SWF or realise the movie can't be loaded, a different state
|
||||
* is entered.
|
||||
* Therefore, if FP / Ruffle are too fast to determine whether the file exists or not, the state
|
||||
* can directly change after one frame from the default state to a different state.
|
||||
* The initial loading state is different, depending on whether the SWF file which is loading is an
|
||||
* online file or a local file.
|
||||
* The local initial loading state is tested in this test.
|
||||
* - Error state
|
||||
* This state is entered if no file could be loaded or if the loaded content is no valid supported
|
||||
* content.
|
||||
* It is tested in this test.
|
||||
* - Image state
|
||||
* This state is entered if an image has been loaded.
|
||||
* It is tested in this test.
|
||||
* - Success state
|
||||
* This state is entered if the first frame of a valid SWF file has been loaded.
|
||||
* It is tested in this test.
|
||||
* - Unloaded state
|
||||
* This state is entered on the next frame after the movie has been unloaded.
|
||||
* It is tested in this test.
|
||||
*
|
||||
* This test consists of several tests which each having a MovieClip go through these states and testing
|
||||
* if the values are correct.
|
||||
* More information about the details of each test is given as documentation of the function executing
|
||||
* the respective test.
|
||||
*
|
||||
* This test currently only tests the MovieClip states when trying to load a local file.
|
||||
* It still contains the test cases for remote URLs, however, they are not executed since Ruffle tests
|
||||
* currently don't support tests using remote URLs.
|
||||
* If Ruffle tests start supporting remote URLs (e.g. with a local server) at some point, these tests
|
||||
* can still be used.
|
||||
*/
|
||||
|
||||
var mc = createEmptyMovieClip("testMovieClip", getNextHighestDepth());
|
||||
printMcProps(getMcPropsArray(mc));
|
||||
startNextTest(0);
|
||||
|
||||
/*
|
||||
* This function is called by each test and calls the next one.
|
||||
*/
|
||||
function startNextTest(currentTest:Number) {
|
||||
switch (currentTest) {
|
||||
case 0: testOfflineErrorCase(1); break;
|
||||
case 1: testOfflineErrorCase(2); break
|
||||
case 2: testOfflineSuccessCase(3); break;
|
||||
case 3: testOfflineSuccessCase(4); break
|
||||
case 4: trace("\nFinished all tests."); break;
|
||||
default: trace("\nError: Test number " + currentTest + " does not exist."); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* This traces all elements of an McProps-Array returned by getMcPropsArray with their respective names.
|
||||
* Flash behaves the same if a file doesn't exist or isn't a valid file, so this tests both.
|
||||
* In this test (in FP), the MovieClip has four states:
|
||||
* The first (default) state is entered when creating an empty movie clip => _framesloaded == 1
|
||||
* The second (initial loading) state is entered when FP tries to load the MovieClip => _framesloaded == -1
|
||||
* The third (error) state is entered when FP realises the movie can't be loaded => _framesloaded == -1
|
||||
* The fourth (unloaded) state is entered on the next frame after the movie has been unloaded.
|
||||
* Ruffle is too fast to determine whether the file exists or not to (reliably) still be in the initial loading
|
||||
* state after one frame (Flash player often is too fast as well).
|
||||
*/
|
||||
function printMcProps(mcPropsArray:Array) {
|
||||
for (var propIterator = 0; propIterator < mcPropsArray.length; propIterator++) {
|
||||
var propName = mcPropsArray[propIterator][0];
|
||||
var propValue = mcPropsArray[propIterator][1];
|
||||
trace(propName + " = " + propValue);
|
||||
function testOfflineErrorCase(currentTest:Number) {
|
||||
var mc: MovieClip;
|
||||
var mcPropsArray1: Array;
|
||||
var mcPropsArray2: Array;
|
||||
var mcPropsArray3: Array;
|
||||
var mcPropsArray4: Array;
|
||||
var mcPropsArray5: Array;
|
||||
var mcPropsArray6: Array;
|
||||
|
||||
switch (currentTest) {
|
||||
case 1:
|
||||
trace("Test 1 (Offline; Link doesn't exist)");
|
||||
mc = createEmptyMovieClip("testMovieClip1", getNextHighestDepth());
|
||||
mcPropsArray1 = getMcPropsArray(mc);
|
||||
|
||||
loadMovie("no existing file.swf", mc);
|
||||
break;
|
||||
case 2:
|
||||
trace("\nTest 2 (Offline; Link is a text file)");
|
||||
mc = createEmptyMovieClip("testMovieClip2", getNextHighestDepth());
|
||||
mcPropsArray1 = getMcPropsArray(mc);
|
||||
|
||||
loadMovie("no correct file (text).swf", mc);
|
||||
break;
|
||||
default:
|
||||
trace("Error: Test number " + currentTest + " is invalid in this context.");
|
||||
return;
|
||||
}
|
||||
|
||||
trace("loadMovie command issued.");
|
||||
mcPropsArray2 = getMcPropsArray(mc);
|
||||
compareMcProps(mcPropsArray1, mcPropsArray2, true);
|
||||
|
||||
var frameCount = 0;
|
||||
var initialOneFinished = false;
|
||||
var errorCount = 0;
|
||||
var unloaded = false;
|
||||
this.onEnterFrame = function() {
|
||||
frameCount++;
|
||||
|
||||
if (!unloaded) {
|
||||
if (mc._framesloaded == 1) {
|
||||
// Sometimes in Flash, the MovieClip is still in the first state for one frame.
|
||||
if (!initialOneFinished) {
|
||||
var changes = compareMcProps(mcPropsArray2, getMcPropsArray(mc), false);
|
||||
if (changes.length != 0) {
|
||||
trace("Error: _framesloaded must be -1.")
|
||||
}
|
||||
initialOneFinished = true;
|
||||
} else {
|
||||
trace("Error: _framesloaded must be -1.");
|
||||
}
|
||||
} else if (mc._framesloaded == -1) {
|
||||
// Flash uses _framesloaded == -1 both in the initial loading and in the error state for
|
||||
// offline files (which are identical).
|
||||
// Therefore, we wait 10 frames to be sure we're in the error state.
|
||||
errorCount++;
|
||||
if (errorCount == 1) {
|
||||
// This might be the initial loading state or (if Ruffle was too fast) the error state.
|
||||
trace("Frames loaded: -1 (1st time)");
|
||||
mcPropsArray3 = getMcPropsArray(mc);
|
||||
compareMcProps(mcPropsArray2, mcPropsArray3, true);
|
||||
} else if (errorCount >= 10) {
|
||||
// This is the error state. No matter which state was previously entered, nothing should
|
||||
// have changed.
|
||||
trace ("Frames loaded: -1 (10th time)")
|
||||
mcPropsArray4 = getMcPropsArray(mc);
|
||||
compareMcProps(mcPropsArray3, mcPropsArray4, true);
|
||||
|
||||
// The MovieClip is unloaded and will enter the unloaded state on the next frame.
|
||||
unloadMovie(mc);
|
||||
unloaded = true;
|
||||
trace("unloadMovie command issued.")
|
||||
mcPropsArray5 = getMcPropsArray(mc);
|
||||
compareMcProps(mcPropsArray4, mcPropsArray5, true);
|
||||
}
|
||||
} else {
|
||||
trace("Error: _framesloaded must be -1.");
|
||||
}
|
||||
} else {
|
||||
mcPropsArray6 = getMcPropsArray(mc);
|
||||
compareMcProps(mcPropsArray5, mcPropsArray6, true);
|
||||
|
||||
this.onEnterFrame = null;
|
||||
startNextTest(currentTest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (frameCount >= 15) {
|
||||
trace("Error: No error has occurred.");
|
||||
this.onEnterFrame = null;
|
||||
startNextTest(currentTest);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Flash behaves mostly the same if a file is an image or a valid SWF file, so this tests both.
|
||||
* In this test (in FP), the MovieClip has four states:
|
||||
* The first (default) state is entered when creating an empty movie clip => _framesloaded == 1
|
||||
* The second (initial loading) state is entered when FP tries to load the MovieClip => _framesloaded == -1
|
||||
* The third (success / image) state is entered after FP loaded the first frame => _framesloaded == 1
|
||||
* (With real SWF files, _framesloaded doesn't go to one but jumps from 0 to a number and rises until
|
||||
* the full file is loaded, but an image only counts as one frame and the target.swf file only has one frame.)
|
||||
* The fourth (unloaded) state is entered on the next frame after the movie has been unloaded.
|
||||
* Ruffle is too fast to determine whether the file exists or not to (reliably) still be in the initial loading
|
||||
* state after one frame (Flash player often is too fast as well).
|
||||
*/
|
||||
function testOfflineSuccessCase(currentTest:Number) {
|
||||
var mc: MovieClip;
|
||||
var mcPropsArray1: Array;
|
||||
var mcPropsArray2: Array;
|
||||
var mcPropsArray3: Array;
|
||||
var mcPropsArray4: Array;
|
||||
var mcPropsArray5: Array;
|
||||
|
||||
switch (currentTest) {
|
||||
case 3:
|
||||
trace("\nTest 3 (Offline; Link is an image)");
|
||||
mc = createEmptyMovieClip("testMovieClip3", getNextHighestDepth());
|
||||
mcPropsArray1 = getMcPropsArray(mc);
|
||||
|
||||
loadMovie("no correct file (image).swf", mc);
|
||||
break;
|
||||
case 4:
|
||||
trace("\nTest 4 (Offline; Link is a valid file)");
|
||||
mc = createEmptyMovieClip("testMovieClip4", getNextHighestDepth());
|
||||
mcPropsArray1 = getMcPropsArray(mc);
|
||||
|
||||
loadMovie("target.swf", mc);
|
||||
break;
|
||||
default:
|
||||
trace("Error: Test number " + currentTest + " is invalid in this context.");
|
||||
return;
|
||||
}
|
||||
|
||||
trace("loadMovie command issued.");
|
||||
mcPropsArray2 = getMcPropsArray(mc);
|
||||
compareMcProps(mcPropsArray1, mcPropsArray2, true);
|
||||
|
||||
var frameCount = 0;
|
||||
var initialOneFinished = false;
|
||||
var loadingStateStarted = false;
|
||||
var unloaded = false;
|
||||
this.onEnterFrame = function() {
|
||||
frameCount++;
|
||||
|
||||
if (!unloaded) {
|
||||
if (mc._framesloaded == 1 && !initialOneFinished) {
|
||||
// Sometimes in Flash, the MovieClip is still in the first state for one frame.
|
||||
var changes = compareMcProps(mcPropsArray2, getMcPropsArray(mc), false);
|
||||
if (changes.length != 0) {
|
||||
// It's not the first state => We test if it's the success state.
|
||||
initialOneFinished = true;
|
||||
}
|
||||
} else if (mc._framesloaded == -1) {
|
||||
// Ruffle is (almost always) too fast to determine whether the file exists or not (and Flash often
|
||||
// is as well); therefore this won't trace anything if the state is correct.
|
||||
if (!loadingStateStarted) {
|
||||
var mcPropsArrayLoadingState = getMcPropsArray(mc);
|
||||
var differenceArray = compareMcProps(mcPropsArray2, mcPropsArrayLoadingState, false);
|
||||
var correctDifferenceArray = [["_framesloaded", 1, -1], ["_totalframes", 1, 0],
|
||||
["_url", "movieclip_state_values/test.swf", "movieclip_state_values/target.swf"],
|
||||
["getBytesTotal()", 0, -1], ["getSWFVersion()", 10, -1]];
|
||||
compareArrays(differenceArray, correctDifferenceArray, 2);
|
||||
loadingStateStarted = true;
|
||||
}
|
||||
} else if (mc._framesloaded != 1) {
|
||||
trace("Error: _framesloaded must be -1 or 1.");
|
||||
}
|
||||
if (mc._framesloaded == 1 && initialOneFinished) {
|
||||
trace("Frames loaded: 1");
|
||||
mcPropsArray3 = getMcPropsArray(mc);
|
||||
compareMcProps(mcPropsArray2, mcPropsArray3, true);
|
||||
|
||||
// The MovieClip is unloaded and will enter the unloaded state on the next frame.
|
||||
unloadMovie(mc);
|
||||
unloaded = true;
|
||||
trace("unloadMovie command issued.")
|
||||
mcPropsArray4 = getMcPropsArray(mc);
|
||||
compareMcProps(mcPropsArray3, mcPropsArray4, true);
|
||||
}
|
||||
initialOneFinished = true;
|
||||
} else {
|
||||
mcPropsArray5 = getMcPropsArray(mc);
|
||||
compareMcProps(mcPropsArray4, mcPropsArray5, true);
|
||||
|
||||
this.onEnterFrame = null;
|
||||
startNextTest(currentTest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (frameCount >= 10) {
|
||||
trace("Error: The MovieClip has not loaded.");
|
||||
this.onEnterFrame = null;
|
||||
startNextTest(currentTest);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* The following code is unused since Ruffle tests currently don't support tests using remote URLs.
|
||||
* It is kept here because Flash behaves differently with online SWFs and offline SWFs.
|
||||
* If Ruffle tests start supporting remote URLs (e.g. with a local server) at some point, these tests
|
||||
* can still be used.
|
||||
*/
|
||||
|
||||
// TODO: Replace these with the valid domain / URL to the folder with the files.
|
||||
var base_domain = "";
|
||||
var base_url = "";
|
||||
|
||||
/*
|
||||
* Flash behaves the same if a file doesn't exist or isn't a valid file, so this tests both.
|
||||
* In this test (In FP), the MovieClip has four states:
|
||||
* The first (default) state is entered when creating an empty movie clip => _framesloaded == 1
|
||||
* The second (initial loading) state is entered when FP tries to load the MovieClip => _framesloaded == 0
|
||||
* The third (error) state is entered when FP realises the movie can't be loaded => _framesloaded == -1
|
||||
* The fourth (unloaded) state is entered on the next frame after the movie has been unloaded.
|
||||
* Flash often is too fast to determine whether the file exists or not to still be in the initial loading
|
||||
* state after one frame (Ruffle sometimes is too fast as well).
|
||||
*/
|
||||
function testOnlineErrorCase(currentTest:Number) {
|
||||
var mc: MovieClip
|
||||
var mcPropsArray1: Array;
|
||||
var mcPropsArray2: Array;
|
||||
var mcPropsArray3: Array;
|
||||
var mcPropsArray4: Array;
|
||||
var mcPropsArray5: Array;
|
||||
|
||||
System.security.allowDomain(base_domain);
|
||||
|
||||
switch (currentTest) {
|
||||
case 5:
|
||||
trace("Test 5 (Online; Link doesn't exist)");
|
||||
mc = createEmptyMovieClip("testMovieClip5", getNextHighestDepth());
|
||||
mcPropsArray1 = getMcPropsArray(mc);
|
||||
|
||||
loadMovie(base_url + "/no existing file.swf", mc);
|
||||
break;
|
||||
case 6:
|
||||
trace("\nTest 6 (Online; Link is a text file)");
|
||||
mc = createEmptyMovieClip("testMovieClip6", getNextHighestDepth());
|
||||
mcPropsArray1 = getMcPropsArray(mc);
|
||||
|
||||
loadMovie(base_url + "/no correct file (text).swf", mc);
|
||||
break;
|
||||
default:
|
||||
trace("Error: Test number " + currentTest + " invalid in this context.");
|
||||
return;
|
||||
}
|
||||
|
||||
trace("loadMovie command issued.");
|
||||
mcPropsArray2 = getMcPropsArray(mc);
|
||||
compareMcProps(mcPropsArray1, mcPropsArray2, true);
|
||||
|
||||
var frameCount = 0;
|
||||
var initialOneFinished = false;
|
||||
var loadingStateStarted = false;
|
||||
var unloaded = false;
|
||||
this.onEnterFrame = function() {
|
||||
frameCount++;
|
||||
|
||||
if (!unloaded) {
|
||||
if (mc._framesloaded == 1) {
|
||||
// Sometimes in Flash, the MovieClip is still in the first state for one frame.
|
||||
if (!initialOneFinished) {
|
||||
var changes = compareMcProps(mcPropsArray2, getMcPropsArray(mc), false);
|
||||
if (changes.length != 0) {
|
||||
trace("Error: _framesloaded must be between -1 and 0.");
|
||||
}
|
||||
initialOneFinished = true;
|
||||
} else {
|
||||
trace("Error: _framesloaded must be between -1 and 0.");
|
||||
}
|
||||
} else if (mc._framesloaded == 0) {
|
||||
// Flash often is too fast to determine whether the file exists or not (and Ruffle sometimes
|
||||
// is as well); therefore this won't trace anything if the state is correct.
|
||||
if (!loadingStateStarted) {
|
||||
var mcPropsArrayLoadingState = getMcPropsArray(mc);
|
||||
var differenceArray = compareMcProps(mcPropsArray2, mcPropsArrayLoadingState, false);
|
||||
var correctDifferenceArray = [["_framesloaded", 1, 0], ["_totalframes", 1, 0]];
|
||||
compareArrays(differenceArray, correctDifferenceArray, 2);
|
||||
loadingStateStarted = true;
|
||||
}
|
||||
} else if (mc._framesloaded == -1) {
|
||||
trace("Frames loaded: -1");
|
||||
mcPropsArray3 = getMcPropsArray(mc);
|
||||
compareMcProps(mcPropsArray2, mcPropsArray3, true);
|
||||
|
||||
// The MovieClip is unloaded and will enter the unloaded state on the next frame.
|
||||
unloadMovie(mc);
|
||||
unloaded = true;
|
||||
trace("unloadMovie command issued.")
|
||||
mcPropsArray5 = getMcPropsArray(mc);
|
||||
compareMcProps(mcPropsArray3, mcPropsArray4, true);
|
||||
} else {
|
||||
trace("Error: _framesloaded must be between -1 and 0.");
|
||||
}
|
||||
} else {
|
||||
mcPropsArray5 = getMcPropsArray(mc);
|
||||
compareMcProps(mcPropsArray4, mcPropsArray5, true);
|
||||
|
||||
this.onEnterFrame = null;
|
||||
startNextTest(currentTest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (frameCount >= 10) {
|
||||
trace("Error: No error has occurred.");
|
||||
this.onEnterFrame = null;
|
||||
startNextTest(currentTest);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Flash behaves mostly the same if a file is an image or a valid SWF file, so this tests both.
|
||||
* In this test (in FP), the MovieClip has three states:
|
||||
* The first (default) state is entered when creating an empty movie clip => _framesloaded == 1
|
||||
* The second (initial loading) state is entered when FP tries to load the MovieClip => _framesloaded == 0
|
||||
* The third (success) state is entered after FP loaded the first frame => _framesloaded == 1
|
||||
* (With real SWF files, _framesloaded doesn't go to one but jumps from 0 to a number and rises until
|
||||
* the full file is loaded, but an image only counts as one frame and the target.swf file only has one frame.)
|
||||
* Flash often is too fast to determine whether the file exists or not to still be in the initial loading
|
||||
* state after one frame (Ruffle sometimes is too fast as well).
|
||||
*/
|
||||
function testOnlineSuccessCase(currentTest:Number) {
|
||||
var mc: MovieClip;
|
||||
var mcPropsArray1: Array;
|
||||
var mcPropsArray2: Array;
|
||||
var mcPropsArray3: Array;
|
||||
var mcPropsArray4: Array;
|
||||
var mcPropsArray5: Array;
|
||||
|
||||
System.security.allowDomain(base_domain);
|
||||
|
||||
switch (currentTest) {
|
||||
case 7:
|
||||
trace("\nTest 7 (Online; Link is an image)");
|
||||
mc = createEmptyMovieClip("testMovieClip7", getNextHighestDepth());
|
||||
mcPropsArray1 = getMcPropsArray(mc);
|
||||
|
||||
loadMovie(base_url + "/no correct file (image).swf", mc);
|
||||
break;
|
||||
case 8:
|
||||
trace("\nTest 8 (Online; Link is a valid file)");
|
||||
mc = createEmptyMovieClip("testMovieClip8", getNextHighestDepth());
|
||||
mcPropsArray1 = getMcPropsArray(mc);
|
||||
|
||||
loadMovie(base_url + "/target.swf", mc);
|
||||
break;
|
||||
default:
|
||||
trace("Error: Test number " + currentTest + " invalid in this context.");
|
||||
return;
|
||||
}
|
||||
|
||||
trace("loadMovie command issued.");
|
||||
mcPropsArray2 = getMcPropsArray(mc);
|
||||
compareMcProps(mcPropsArray1, mcPropsArray2, true);
|
||||
|
||||
var frameCount = 0;
|
||||
var initialOneFinished = false;
|
||||
var loadingStateStarted = false;
|
||||
var unloaded = false;
|
||||
this.onEnterFrame = function() {
|
||||
frameCount++;
|
||||
|
||||
if (!unloaded) {
|
||||
if (mc._framesloaded == 1 && !initialOneFinished) {
|
||||
// Sometimes in Flash, the MovieClip is still in the first state for one frame.
|
||||
var changes = compareMcProps(mcPropsArray2, getMcPropsArray(mc), false);
|
||||
if (changes.length != 0) {
|
||||
// It's not the first state => We test if it's the success state.
|
||||
initialOneFinished = true;
|
||||
}
|
||||
} else if (mc._framesloaded == 0) {
|
||||
// Flash often is too fast to determine whether the file exists or not (and Ruffle sometimes
|
||||
// is as well); therefore this won't trace anything if the state is correct.
|
||||
if (!loadingStateStarted) {
|
||||
var mcPropsArrayLoadingState = getMcPropsArray(mc);
|
||||
var differenceArray = compareMcProps(mcPropsArray2, mcPropsArrayLoadingState, false);
|
||||
var correctDifferenceArray = [["_framesloaded", 1, 0], ["_totalframes", 1, 0]];
|
||||
compareArrays(differenceArray, correctDifferenceArray, 2);
|
||||
loadingStateStarted = true;
|
||||
}
|
||||
} else if (mc._framesloaded != 1) {
|
||||
trace("Error: _framesloaded must be between 0 and 1.");
|
||||
}
|
||||
if (mc._framesloaded == 1 && initialOneFinished) {
|
||||
trace("Frames loaded: 1");
|
||||
mcPropsArray3 = getMcPropsArray(mc);
|
||||
compareMcProps(mcPropsArray2, mcPropsArray3, true);
|
||||
|
||||
// The MovieClip is unloaded and will enter the unloaded state on the next frame.
|
||||
unloadMovie(mc);
|
||||
unloaded = true;
|
||||
trace("unloadMovie command issued.")
|
||||
mcPropsArray4 = getMcPropsArray(mc);
|
||||
compareMcProps(mcPropsArray3, mcPropsArray4, true);
|
||||
}
|
||||
initialOneFinished = true;
|
||||
} else {
|
||||
mcPropsArray5 = getMcPropsArray(mc);
|
||||
compareMcProps(mcPropsArray4, mcPropsArray5, true);
|
||||
|
||||
this.onEnterFrame = null;
|
||||
startNextTest(currentTest);
|
||||
return;
|
||||
}
|
||||
|
||||
if (frameCount >= 10) {
|
||||
trace("Error: The MovieClip has not loaded.");
|
||||
this.onEnterFrame = null;
|
||||
startNextTest(currentTest);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* This compares different arrays with a fixed dimension and traces an error if they are different.
|
||||
*/
|
||||
function compareArrays(array1:Array, array2:Array, arrayDimension:Number) {
|
||||
if (array1.length != array2.length) {
|
||||
trace("Error: The array lengths are not equal.")
|
||||
return;
|
||||
}
|
||||
|
||||
for (var elementIterator = 0; elementIterator < array1.length; elementIterator++) {
|
||||
if (arrayDimension == 1) {
|
||||
if (array1[elementIterator] != array2[elementIterator]) {
|
||||
trace("Error: The arrays are not equal.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
compareArrays(array1[elementIterator], array2[elementIterator], arrayDimension - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This compares two McProps-Arrays returned by getMcPropsArray.
|
||||
* If traceDifferences is true, all differences will be traced.
|
||||
* It returns the diverging values with their respective names in a two-dimensional array.
|
||||
*/
|
||||
function compareMcProps(mcPropsArray1:Array, mcPropsArray2:Array, traceDifferences:Boolean) {
|
||||
if (mcPropsArray1 == undefined || mcPropsArray2 == undefined) {
|
||||
trace("Error: An mcPropsArray is undefined.");
|
||||
return;
|
||||
}
|
||||
|
||||
var differenceArray = [];
|
||||
for (var propIterator = 0; propIterator < mcPropsArray1.length; propIterator++) {
|
||||
if (mcPropsArray1[propIterator][1].toString() != mcPropsArray2[propIterator][1].toString()) {
|
||||
var value1 = mcPropsArray1[propIterator][1];
|
||||
var value2 = mcPropsArray2[propIterator][1];
|
||||
var propName = mcPropsArray1[propIterator][0];
|
||||
differenceArray.push([propName, value1, value2])
|
||||
if (traceDifferences) {
|
||||
trace("Change: Prop " + propName + " is \"" + value1 + "\" on the first, but \"" + value2 +
|
||||
"\" on the second target.");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (traceDifferences && differenceArray.length == 0) {
|
||||
trace("Both targets have the same props.");
|
||||
}
|
||||
|
||||
return differenceArray;
|
||||
}
|
||||
|
||||
|
||||
|
@ -77,9 +603,7 @@ function getMcPropsArray(mc:MovieClip) {
|
|||
mcProps.push(["getRect(mc).yMin", getRectMc.yMin]);
|
||||
mcProps.push(["getRect(mc).yMax", getRectMc.yMax]);
|
||||
mcProps.push(["getSWFVersion()", mc.getSWFVersion()]);
|
||||
// mcProps.push(["getTextSnapshot().getCount()", mc.getTextSnapshot().getCount()]);
|
||||
|
||||
// TODO: Re-add getTextSnapshot().getCount() to this test after it has been implemented / stubbed for AVM1.
|
||||
mcProps.push(["getTextSnapshot().getCount()", mc.getTextSnapshot().getCount()]);
|
||||
|
||||
return mcProps;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
ruffle.getBytesTotal(): 2685
|
||||
ruffle.getBytesLoaded(): 2685
|
||||
ruffle.getSWFVersion(): -1
|
||||
ruffle._url (unescaped & split): movieclip_methods_with_loaded_image/target.png
|
||||
ruffle._url (unescaped & split): target.png
|
||||
|
|
|
@ -8,7 +8,7 @@ _root.onEnterFrame = function() {
|
|||
trace("ruffle.getBytesLoaded(): " + bytesLoaded);
|
||||
trace("ruffle.getSWFVersion(): " + ruffle.getSWFVersion());
|
||||
_root.url = unescape(ruffle._url).split("/");
|
||||
url = url[url.length - 2] + "/" + url[url.length - 1];
|
||||
url = url[url.length - 1];
|
||||
trace("ruffle._url (unescaped & split): " + url);
|
||||
delete onEnterFrame;
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -4,7 +4,7 @@ Both targets have the same props.
|
|||
Frames loaded: -1 (1st time)
|
||||
Change: Prop _framesloaded is "1" on the first, but "-1" on the second target.
|
||||
Change: Prop _totalframes is "1" on the first, but "0" on the second target.
|
||||
Change: Prop _url is "movieclip_state_values/test.swf" on the first, but "movieclip_state_values/no existing file.swf" on the second target.
|
||||
Change: Prop _url is "test.swf" on the first, but "no existing file.swf" on the second target.
|
||||
Change: Prop getBytesTotal() is "0" on the first, but "-1" on the second target.
|
||||
Change: Prop getSWFVersion() is "10" on the first, but "-1" on the second target.
|
||||
Frames loaded: -1 (10th time)
|
||||
|
@ -12,7 +12,7 @@ Both targets have the same props.
|
|||
unloadMovie command issued.
|
||||
Both targets have the same props.
|
||||
Change: Prop _framesloaded is "-1" on the first, but "0" on the second target.
|
||||
Change: Prop _url is "movieclip_state_values/no existing file.swf" on the first, but "movieclip_state_values/test.swf" on the second target.
|
||||
Change: Prop _url is "no existing file.swf" on the first, but "test.swf" on the second target.
|
||||
Change: Prop getBytesTotal() is "-1" on the first, but "0" on the second target.
|
||||
Change: Prop getSWFVersion() is "-1" on the first, but "10" on the second target.
|
||||
|
||||
|
@ -22,7 +22,7 @@ Both targets have the same props.
|
|||
Frames loaded: -1 (1st time)
|
||||
Change: Prop _framesloaded is "1" on the first, but "-1" on the second target.
|
||||
Change: Prop _totalframes is "1" on the first, but "0" on the second target.
|
||||
Change: Prop _url is "movieclip_state_values/test.swf" on the first, but "movieclip_state_values/no correct file (text).swf" on the second target.
|
||||
Change: Prop _url is "test.swf" on the first, but "no correct file (text).swf" on the second target.
|
||||
Change: Prop getBytesTotal() is "0" on the first, but "-1" on the second target.
|
||||
Change: Prop getSWFVersion() is "10" on the first, but "-1" on the second target.
|
||||
Frames loaded: -1 (10th time)
|
||||
|
@ -30,7 +30,7 @@ Both targets have the same props.
|
|||
unloadMovie command issued.
|
||||
Both targets have the same props.
|
||||
Change: Prop _framesloaded is "-1" on the first, but "0" on the second target.
|
||||
Change: Prop _url is "movieclip_state_values/no correct file (text).swf" on the first, but "movieclip_state_values/test.swf" on the second target.
|
||||
Change: Prop _url is "no correct file (text).swf" on the first, but "test.swf" on the second target.
|
||||
Change: Prop getBytesTotal() is "-1" on the first, but "0" on the second target.
|
||||
Change: Prop getSWFVersion() is "-1" on the first, but "10" on the second target.
|
||||
|
||||
|
@ -42,7 +42,7 @@ Change: Prop _currentframe is "0" on the first, but "1" on the second target.
|
|||
Change: Prop _height is "0" on the first, but "985" on the second target.
|
||||
Change: Prop _width is "0" on the first, but "1280" on the second target.
|
||||
Change: Prop transform.pixelBounds is "(x=0, y=0, w=0, h=0)" on the first, but "(x=0, y=0, w=1280, h=985)" on the second target.
|
||||
Change: Prop _url is "movieclip_state_values/test.swf" on the first, but "movieclip_state_values/no correct file (image).swf" on the second target.
|
||||
Change: Prop _url is "test.swf" on the first, but "no correct file (image).swf" on the second target.
|
||||
Change: Prop getBounds(this).xMin is "6710886.4" on the first, but "0" on the second target.
|
||||
Change: Prop getBounds(this).xMax is "6710886.4" on the first, but "1280" on the second target.
|
||||
Change: Prop getBounds(this).yMin is "6710886.4" on the first, but "0" on the second target.
|
||||
|
@ -70,7 +70,7 @@ Change: Prop _height is "985" on the first, but "0" on the second target.
|
|||
Change: Prop _totalframes is "1" on the first, but "0" on the second target.
|
||||
Change: Prop _width is "1280" on the first, but "0" on the second target.
|
||||
Change: Prop transform.pixelBounds is "(x=0, y=0, w=1280, h=985)" on the first, but "(x=0, y=0, w=0, h=0)" on the second target.
|
||||
Change: Prop _url is "movieclip_state_values/no correct file (image).swf" on the first, but "movieclip_state_values/test.swf" on the second target.
|
||||
Change: Prop _url is "no correct file (image).swf" on the first, but "test.swf" on the second target.
|
||||
Change: Prop getBounds(this).xMin is "0" on the first, but "6710886.4" on the second target.
|
||||
Change: Prop getBounds(this).xMax is "1280" on the first, but "6710886.4" on the second target.
|
||||
Change: Prop getBounds(this).yMin is "0" on the first, but "6710886.4" on the second target.
|
||||
|
@ -97,7 +97,7 @@ Both targets have the same props.
|
|||
Child movie loaded!
|
||||
Frames loaded: 1
|
||||
Change: Prop _currentframe is "0" on the first, but "1" on the second target.
|
||||
Change: Prop _url is "movieclip_state_values/test.swf" on the first, but "movieclip_state_values/target.swf" on the second target.
|
||||
Change: Prop _url is "test.swf" on the first, but "target.swf" on the second target.
|
||||
Change: Prop getBytesLoaded() is "0" on the first, but "68" on the second target.
|
||||
Change: Prop getBytesTotal() is "0" on the first, but "68" on the second target.
|
||||
Change: Prop getSWFVersion() is "10" on the first, but "8" on the second target.
|
||||
|
@ -106,7 +106,7 @@ Both targets have the same props.
|
|||
Change: Prop _currentframe is "1" on the first, but "0" on the second target.
|
||||
Change: Prop _framesloaded is "1" on the first, but "0" on the second target.
|
||||
Change: Prop _totalframes is "1" on the first, but "0" on the second target.
|
||||
Change: Prop _url is "movieclip_state_values/target.swf" on the first, but "movieclip_state_values/test.swf" on the second target.
|
||||
Change: Prop _url is "target.swf" on the first, but "test.swf" on the second target.
|
||||
Change: Prop getBytesLoaded() is "68" on the first, but "0" on the second target.
|
||||
Change: Prop getBytesTotal() is "68" on the first, but "0" on the second target.
|
||||
Change: Prop getSWFVersion() is "8" on the first, but "10" on the second target.
|
||||
|
|
|
@ -572,7 +572,7 @@ function getMcPropsArray(mc:MovieClip) {
|
|||
var url = unescape(mc._url);
|
||||
if (url.indexOf("file:///") == 0) {
|
||||
var urlSplit = url.split("/");
|
||||
mcProps.push(["_url", urlSplit[urlSplit.length - 2] + "/" + urlSplit[urlSplit.length - 1]]);
|
||||
mcProps.push(["_url", urlSplit[urlSplit.length - 1]]);
|
||||
} else {
|
||||
mcProps.push(["_url", url]);
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue