use super::*;
#[test]
fn resolve_includes_single_include_inserts_a_unit() {
let reader = fixture_reader(vec![("lib.mms", "OCTA 1\n")]);
let units = MMixAssembler::resolve_includes(
"INCLUDE lib.mms\nOCTA 2\n",
"root.mms",
std::path::Path::new(""),
&reader,
)
.unwrap();
assert_eq!(units.len(), 2);
assert_eq!(units[0].0, "lib.mms");
assert!(units[0].1.contains("OCTA 1"));
assert_eq!(units[1].0, "root.mms");
assert!(units[1].1.contains("OCTA 2"));
}
#[test]
fn resolve_includes_filename_fidelity() {
let reader = fixture_reader(vec![("lib.mms", "OCTA 1\n")]);
let units = MMixAssembler::resolve_includes(
"INCLUDE lib.mms\nOCTA 2\n",
"root.mms",
std::path::Path::new(""),
&reader,
)
.unwrap();
assert_eq!(units[0].0, "lib.mms");
assert_ne!(units[0].0, "root.mms");
}
#[test]
fn resolve_includes_pads_line_numbers_after_an_include() {
let reader = fixture_reader(vec![("lib.mms", "OCTA 1\n")]);
let units = MMixAssembler::resolve_includes(
"% a\nINCLUDE lib.mms\nBYTE 0\n",
"root.mms",
std::path::Path::new(""),
&reader,
)
.unwrap();
let host_segment = &units.last().unwrap().1;
assert_eq!(host_segment.matches('\n').count() - 1, 2);
assert!(host_segment.starts_with("\n\nBYTE 0"));
let mut asm = MMixAssembler::new(&units[0].1, &units[0].0);
for (name, src) in units.iter().skip(1) {
asm.add_source(src, name);
}
asm.parse().unwrap();
assert_eq!(asm.addr_for_line("root.mms", 3), Some(8));
}
#[test]
fn resolve_includes_nested_relative_resolution() {
let reader = fixture_reader(vec![
("a/sub/b.mms", "INCLUDE c.mms\nOCTA 2\n"),
("a/sub/c.mms", "OCTA 3\n"),
]);
let units = MMixAssembler::resolve_includes(
"INCLUDE sub/b.mms\nOCTA 1\n",
"a/root.mms",
std::path::Path::new("a"),
&reader,
)
.unwrap();
let c_unit = units
.iter()
.find(|(name, _)| name.contains("c.mms"))
.expect("c.mms unit present");
assert_eq!(c_unit.0, "a/sub/c.mms");
assert!(c_unit.1.contains("OCTA 3"));
}
#[test]
fn resolve_includes_cycle_is_an_error() {
let reader = fixture_reader(vec![
("a.mms", "INCLUDE b.mms\n"),
("b.mms", "INCLUDE a.mms\n"),
]);
let err = MMixAssembler::resolve_includes(
"INCLUDE a.mms\n",
"driver.mms",
std::path::Path::new(""),
&reader,
)
.unwrap_err();
assert!(err.contains("cycle"));
assert!(err.contains("a.mms"));
assert!(err.contains("b.mms"));
}
#[test]
fn resolve_includes_missing_file_is_an_error_not_a_panic() {
let reader = fixture_reader(vec![]);
let err = MMixAssembler::resolve_includes(
"INCLUDE missing.mms\n",
"root.mms",
std::path::Path::new(""),
&reader,
)
.unwrap_err();
assert!(err.contains("missing.mms"));
}
#[test]
fn resolve_includes_passthrough_preserves_content_with_no_include() {
let reader = fixture_reader(vec![]);
let source = "OCTA 1\nOCTA 2";
let units =
MMixAssembler::resolve_includes(source, "root.mms", std::path::Path::new(""), &reader)
.unwrap();
assert_eq!(units.len(), 1);
assert_eq!(units[0].0, "root.mms");
assert_eq!(units[0].1, source);
}
#[test]
fn resolve_includes_recognizes_comment_case() {
let reader = fixture_reader(vec![("lib.mms", "OCTA 1\n")]);
let lower_with_comment = MMixAssembler::resolve_includes(
"include lib.mms % pull it in\n",
"root.mms",
std::path::Path::new(""),
&reader,
)
.unwrap();
let quoted = MMixAssembler::resolve_includes(
"INCLUDE \"lib.mms\"\n",
"root.mms",
std::path::Path::new(""),
&reader,
)
.unwrap();
assert_eq!(lower_with_comment.len(), 1);
assert_eq!(lower_with_comment[0].1, "include lib.mms % pull it in\n");
assert_eq!(quoted.len(), 1);
assert_eq!(quoted[0].1, "OCTA 1\n");
assert!(quoted[0].1.contains("OCTA 1"));
}