Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 28 additions & 5 deletions crates/workshop-rs/tests/audit_regressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,30 +27,51 @@ fn rule_name_backslash_round_trips() {
emitted.contains("\"a\\\\nb\""),
"backslash must re-escape, got:\n{emitted}"
);
internal::assert_emit_round_trip(&program);
}

/// W3: fractional and out-of-range declaration indexes must be rejected
/// rather than truncated/saturated by `as u32`.
#[test]
fn non_integer_variable_index_is_rejected() {
assert!(internal::try_parse("variables\n{\n global:\n 1.9: b\n}").is_err());
assert!(internal::try_parse("variables\n{\n global:\n 4294967296: b\n}").is_err());
for source in [
"variables\n{\n global:\n 1.9: b\n}",
"variables\n{\n global:\n 4294967296: b\n}",
] {
let error = internal::try_parse(source).unwrap_err();
assert!(
error.to_string().contains("integer index"),
"the diagnostic must name the index, got: {error}"
);
}
assert!(internal::try_parse("variables\n{\n player:\n -1: b\n}").is_err());
}

/// W4: a declaration name a reference can never express must be rejected.
#[test]
fn unreferenceable_variable_name_is_rejected() {
assert!(internal::try_parse("variables\n{\n global:\n 0: a.b\n}").is_err());
assert!(internal::try_parse("variables\n{\n global:\n 0: a % b\n}").is_err());
for source in [
"variables\n{\n global:\n 0: a.b\n}",
"variables\n{\n global:\n 0: a % b\n}",
] {
let error = internal::try_parse(source).unwrap_err();
assert!(
error.to_string().contains("cannot be referenced"),
"the diagnostic must name the unreferenceable name, got: {error}"
);
}
}

/// W7: a second `settings {}` block must be rejected, not silently
/// overwrite the first.
#[test]
fn duplicate_settings_block_is_rejected() {
let source = "settings\n{\n main\n {\n Description: \"first\"\n }\n}\nsettings\n{\n main\n {\n Description: \"second\"\n }\n}";
assert!(internal::try_parse(source).is_err());
let error = internal::try_parse(source).unwrap_err();
assert!(
error.to_string().contains("duplicate 'settings' block"),
"the diagnostic must name the duplicate block, got: {error}"
);
}

/// W9: opaque settings string values must re-emit decoded control
Expand All @@ -65,6 +86,7 @@ fn opaque_settings_string_reescapes_controls() {
emitted.contains("Custom Key: \"a\\nb\\rc\\\\d\""),
"control characters must re-escape, got:\n{emitted}"
);
internal::assert_emit_round_trip(&program);
}

/// W10: a forward variable reference must reuse the real declaration's
Expand All @@ -85,6 +107,7 @@ fn forward_reference_binds_to_later_declaration() {
let reparsed = internal::parse(&emitted);
let var = reparsed.global_variables.iter().next().unwrap();
assert_eq!(var.index, 3);
internal::assert_emit_round_trip(&program);
}

/// W10 companion: two declarations of the same name must be rejected.
Expand Down
Loading