Skip to content

Commit 08ca133

Browse files
committed
tests: Validate known-failure test traces against current Ruffle output
This can be opted-out of with `known_failure.ruffle_check = false`
1 parent 4e9d417 commit 08ca133

File tree

12 files changed

+111
-598
lines changed

12 files changed

+111
-598
lines changed

tests/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ ignore = false
4242
# `known_failure.panic = "panic message"`
4343
# instead (note that 'panicky' tests will be skipped if the test harness is run
4444
# with debug assertions disabled, e.g. with `--release`).
45+
# By default, the test runner will additionally check Ruffle's output against itself
46+
# to detect regressions in failing tests; this can be disabled with
47+
# `known_failure.ruffle_check = false`.
4548
known_failure = false
4649

4750
# Path (relative to the directory containing test.toml) to the expected output

tests/framework/src/options/known_failure.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use serde::{
99
pub enum KnownFailure {
1010
#[default]
1111
None,
12-
TraceOutput,
12+
TraceOutput {
13+
ruffle_check: bool,
14+
},
1315
Panic {
1416
message: String,
1517
},
@@ -27,12 +29,12 @@ impl<'de> de::Visitor<'de> for KnownFailureVisitor {
2729
type Value = KnownFailure;
2830

2931
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
30-
f.write_str("a boolean, or `.panic = 'message'`")
32+
f.write_str("a boolean, `.ruffle_check = false`, or `.panic = 'message'`")
3133
}
3234

3335
fn visit_bool<E: de::Error>(self, v: bool) -> Result<Self::Value, E> {
3436
if v {
35-
Ok(KnownFailure::TraceOutput)
37+
Ok(KnownFailure::TraceOutput { ruffle_check: true })
3638
} else {
3739
Ok(KnownFailure::None)
3840
}
@@ -44,10 +46,13 @@ impl<'de> de::Visitor<'de> for KnownFailureVisitor {
4446
enum Raw {
4547
#[serde(rename = "panic")]
4648
Panic(String),
49+
#[serde(rename = "ruffle_check")]
50+
RuffleCheck(bool),
4751
}
4852

4953
match Raw::deserialize(MapAccessDeserializer::new(map))? {
5054
Raw::Panic(message) => Ok(KnownFailure::Panic { message }),
55+
Raw::RuffleCheck(ruffle_check) => Ok(KnownFailure::TraceOutput { ruffle_check }),
5156
}
5257
}
5358
}

tests/framework/src/runner.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ impl TestRunner {
319319
&self.log,
320320
&self.output_path,
321321
self.options.approximations.as_ref(),
322-
matches!(self.options.known_failure, KnownFailure::TraceOutput),
322+
&self.options.known_failure,
323323
)
324324
}
325325

tests/framework/src/runner/trace.rs

Lines changed: 78 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
use crate::backends::TestLogBackend;
22
use crate::options::approximations::Approximations;
3+
use crate::options::known_failure::KnownFailure;
34
use anyhow::{Error, anyhow};
45
use pretty_assertions::Comparison;
56
use vfs::VfsPath;
67

78
pub fn compare_trace_output(
89
log: &TestLogBackend,
910
expected_path: &VfsPath,
10-
approximations: Option<&Approximations>,
11-
known_failure: bool,
11+
approx: Option<&Approximations>,
12+
known_failure: &KnownFailure,
1213
) -> anyhow::Result<()> {
1314
let expected_trace = expected_path.read_to_string()?.replace("\r\n", "\n");
1415

@@ -17,23 +18,54 @@ pub fn compare_trace_output(
1718
// bytes should explicitly test for them in ActionScript.
1819
let actual_trace = log.trace_output().replace('\0', "");
1920

20-
let result = test(&expected_trace, approximations, &actual_trace);
21-
match (result, known_failure) {
22-
(res, false) => res,
23-
(Ok(()), true) => Err(anyhow!(
24-
"Trace output check was known to be failing, but now passes successfully. \
25-
Please update the test and remove `known_failure = true`!",
26-
)),
27-
(Err(_), true) => Ok(()),
21+
let result = test("flash_expected", approx, &expected_trace, &actual_trace);
22+
23+
match known_failure {
24+
KnownFailure::None | KnownFailure::Panic { .. } => result,
25+
KnownFailure::TraceOutput {
26+
ruffle_check: false,
27+
} => {
28+
if result.is_ok() {
29+
Err(anyhow!(
30+
"Trace output check was known to be failing, but now passes successfully. \
31+
Please update the test and remove `known_failure = true`!"
32+
))
33+
} else {
34+
Ok(())
35+
}
36+
}
37+
KnownFailure::TraceOutput { ruffle_check: true } => {
38+
let path = path_with_suffix(expected_path, "ruffle")?;
39+
40+
if result.is_ok() {
41+
return Err(anyhow!(
42+
"Trace output check was known to be failing, but now passes successfully. \
43+
Please update the test, and remove `known_failure = true` and `{}`!",
44+
path.as_str(),
45+
));
46+
}
47+
48+
let expected_trace = if path.exists()? {
49+
path.read_to_string()?.replace("\r\n", "\n")
50+
} else {
51+
path.create_file()?.write_all(actual_trace.as_bytes())?;
52+
return Err(anyhow!(
53+
"No trace to compare to! Saved actual trace as Ruffle-expected."
54+
));
55+
};
56+
57+
test("ruffle_expected", approx, &expected_trace, &actual_trace)
58+
}
2859
}
2960
}
3061

31-
pub fn test(
62+
fn test(
63+
expected_name: &str,
64+
approx: Option<&Approximations>,
3265
expected_output: &str,
33-
approximations: Option<&Approximations>,
3466
actual_output: &str,
3567
) -> anyhow::Result<()> {
36-
if let Some(approximations) = approximations {
68+
if let Some(approx) = approx {
3769
let add_comparison_to_err = |err: Error| -> Error {
3870
let left_pretty = PrettyString(actual_output);
3971
let right_pretty = PrettyString(expected_output);
@@ -44,7 +76,7 @@ pub fn test(
4476

4577
if actual_output.lines().count() != expected_output.lines().count() {
4678
return Err(anyhow!(
47-
"# of lines of output didn't match (expected {} from Flash, got {} from Ruffle",
79+
"# of lines of output didn't match ({expected_name}: {}, ruffle_actual: {})",
4880
expected_output.lines().count(),
4981
actual_output.lines().count()
5082
));
@@ -58,21 +90,21 @@ pub fn test(
5890
continue;
5991
}
6092

61-
approximations
93+
approx
6294
.compare(actual, expected)
6395
.map_err(add_comparison_to_err)?;
6496
} else {
6597
let mut found = false;
6698

6799
// Check each of the user-provided regexes for a match
68-
for pattern in approximations.number_patterns() {
100+
for pattern in approx.number_patterns() {
69101
if let (Some(actual_captures), Some(expected_captures)) =
70102
(pattern.captures(actual), pattern.captures(expected))
71103
{
72104
found = true;
73105
if expected_captures.len() != actual_captures.len() {
74106
return Err(anyhow!(
75-
"Differing numbers of regex captures (expected {}, actually {})",
107+
"Differing numbers of regex captures ({expected_name}: {}, ruffle_actual: {})",
76108
expected_captures.len(),
77109
actual_captures.len(),
78110
));
@@ -95,25 +127,29 @@ pub fn test(
95127
.as_str()
96128
.parse::<f64>()
97129
.expect("Failed to parse 'expected' capture group as float");
98-
approximations
130+
approx
99131
.compare(actual_num, expected_num)
100132
.map_err(add_comparison_to_err)?;
101133
}
102134
let modified_actual = pattern.replace_all(actual, "");
103135
let modified_expected = pattern.replace_all(expected, "");
104136

105-
assert_text_matches(modified_actual.as_ref(), modified_expected.as_ref())?;
137+
assert_text_matches(
138+
modified_actual.as_ref(),
139+
modified_expected.as_ref(),
140+
expected_name,
141+
)?;
106142
break;
107143
}
108144
}
109145

110146
if !found {
111-
assert_text_matches(actual, expected)?;
147+
assert_text_matches(actual, expected, expected_name)?;
112148
}
113149
}
114150
}
115151
} else {
116-
assert_text_matches(actual_output, expected_output)?;
152+
assert_text_matches(actual_output, expected_output, expected_name)?;
117153
}
118154

119155
Ok(())
@@ -134,14 +170,14 @@ impl std::fmt::Debug for PrettyString<'_> {
134170
}
135171
}
136172

137-
fn assert_text_matches(ruffle: &str, flash: &str) -> anyhow::Result<()> {
138-
if ruffle != flash {
173+
fn assert_text_matches(ruffle: &str, expected: &str, expected_name: &str) -> anyhow::Result<()> {
174+
if ruffle != expected {
139175
let left_pretty = PrettyString(ruffle);
140-
let right_pretty = PrettyString(flash);
176+
let right_pretty = PrettyString(expected);
141177
let comparison = Comparison::new(&left_pretty, &right_pretty);
142178

143179
Err(anyhow!(
144-
"assertion failed: `(ruffle_actual == flash_expected)`\
180+
"assertion failed: `(ruffle_actual == {expected_name})`\
145181
\n\
146182
\n{}\
147183
\n",
@@ -151,3 +187,20 @@ fn assert_text_matches(ruffle: &str, flash: &str) -> anyhow::Result<()> {
151187
Ok(())
152188
}
153189
}
190+
191+
/// Adds a suffix to the filename: e.g. `foo/bar.txt` -> `foo/bar.suffix.txt`
192+
fn path_with_suffix(path: &VfsPath, suffix: &str) -> anyhow::Result<VfsPath> {
193+
// `VfsPath`'s API is less nice than std's `Path`... :(
194+
let mut name = path.filename();
195+
name.insert_str(0, "../");
196+
if let Some(ext) = path.extension() {
197+
name.truncate(name.len() - ext.len());
198+
name.push_str(suffix);
199+
name.push('.');
200+
name.push_str(&ext);
201+
} else {
202+
name.push('.');
203+
name.push_str(suffix);
204+
}
205+
Ok(path.join(&name)?)
206+
}

tests/tests/swfs/from_avmplus/as3/RuntimeErrors/Error1064CannotCallMethodAsConstructor/output.ruffle.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
num_ticks = 1
22
known_failure = true
3+
4+
# Ruffle's (wrong) output is slightly different on macOS.
5+
# Remove this when removing `known_failure`.
6+
[approximations]
7+
number_patterns = [".+ FAILED! expected: ([\\de+.]+) got:.+"]
8+
max_relative = 1e-13

tests/tests/swfs/from_avmplus/regress/bug_638233/output.ruffle.txt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
Asserting for TypeError FAILED! expected: true got: false
2-
1 FAILED! expected: TypeError: Error #1007 got: ReferenceError: Error
3-
Asserting for TypeError FAILED! expected: true got: false
4-
2 FAILED! expected: TypeError: Error #1007 got: ReferenceError: Error
5-
Asserting for TypeError FAILED! expected: true got: false
6-
3 FAILED! expected: TypeError: Error #1007 got: ReferenceError: Error
7-
Asserting for TypeError FAILED! expected: true got: false
8-
4 FAILED! expected: TypeError: Error #1007 got: ReferenceError: Error
1+
Asserting for TypeError PASSED!
2+
1 PASSED!
3+
Asserting for TypeError PASSED!
4+
2 PASSED!
5+
Asserting for TypeError PASSED!
6+
3 PASSED!
7+
Asserting for TypeError PASSED!
8+
4 PASSED!
99
Asserting for TypeError FAILED! expected: true got: false
1010
5 FAILED! expected: TypeError: Error #1007 got: ReferenceError: Error
1111
Asserting for TypeError PASSED!

0 commit comments

Comments
 (0)