Files
TerminalBasic/crates/tb-vm/tests/incremental.rs

163 lines
5.6 KiB
Rust

use tb_frontend::{
forms::FormCatalog,
source::{SourceSegment, SourceUnit},
};
use tb_ui::frm::{self, FormFile};
use tb_vm::{compile_project, project::ProjectCompiler};
fn unit(name: &str, text: &str) -> SourceUnit {
SourceUnit::new(name, &format!("{name}.bas"), text)
}
fn compare(cache: &mut ProjectCompiler, units: &[SourceUnit], forms: &[FormFile]) -> bool {
let mut catalog = FormCatalog::default();
for f in forms {
catalog.append(&f.catalog());
}
let a = cache.compile("APP", units, &catalog, forms);
let b = compile_project("APP", units, &catalog, forms);
match (a, b) {
(Ok(a), Ok(b)) => {
assert_eq!(a.to_tbc(), b.to_tbc());
true
}
(Err(a), Err(b)) => {
assert_eq!(
a.iter().map(ToString::to_string).collect::<Vec<_>>(),
b.iter().map(ToString::to_string).collect::<Vec<_>>()
);
false
}
(a, b) => panic!("cache/full mismatch {a:?} / {b:?}"),
}
}
#[test]
fn cached_private_edits_parse_and_compile_only_changed_modules() {
let mut c = ProjectCompiler::default();
let mut units = vec![
unit("A", "CALL Work\nEND"),
unit("B", "SUB Work\nPRINT 1\nEND SUB"),
unit("C", "SUB Independent\nPRINT 3\nEND SUB"),
];
assert!(compare(&mut c, &units, &[]));
assert_eq!(c.stats.compiled, 3);
assert!(compare(&mut c, &units, &[]));
assert_eq!(
(c.stats.parsed, c.stats.compiled, c.stats.reused),
(0, 0, 3)
);
units[1].segments[0].text = units[1].segments[0].text.replace("PRINT 1", "PRINT 2");
assert!(compare(&mut c, &units, &[]));
assert_eq!(
(c.stats.parsed, c.stats.compiled, c.stats.reused),
(1, 1, 2)
);
units[1].segments[0].text = "SUB Work\nPRINT 2\nEND SUB\nSUB Second\nPRINT 4\nEND SUB".into();
assert!(compare(&mut c, &units, &[]));
units[1].segments[0].text = units[1].segments[0]
.text
.replace("PRINT 2", "PRINT 2\nPRINT 3");
assert!(compare(&mut c, &units, &[]));
assert_eq!(
(c.stats.compiled, c.stats.reused),
(1, 2),
"Verschobene fremde Prozedurzeilen ändern keinen Importvertrag"
);
units[1].segments[0].text = "SUB Work\nPRINT )\nEND SUB".into();
assert!(!compare(&mut c, &units, &[]));
units[1].segments[0].text = "SUB Work\nPRINT 5\nEND SUB".into();
assert!(compare(&mut c, &units, &[]));
}
#[test]
fn constants_types_common_removed_symbols_and_order_match_full_compiler() {
let mut c = ProjectCompiler::default();
let mut u = vec![
unit(
"A",
"CONST A=2\nTYPE T\nx AS INTEGER\nEND TYPE\nCOMMON SHARED N%\n",
),
unit("B", "CONST B=A+1\n"),
unit(
"C",
"DIM value AS T\nvalue.x=B\nCOMMON SHARED N%\nN%=B\nPRINT value.x\n",
),
];
assert!(compare(&mut c, &u, &[]));
u[0].segments[0].text = u[0].segments[0].text.replace("A=2", "A=4");
assert!(compare(&mut c, &u, &[]));
u[0].segments[0].text = u[0].segments[0].text.replace("INTEGER", "LONG");
assert!(compare(&mut c, &u, &[]));
u[0].segments[0].text = u[0].segments[0].text.replace("N%", "N$");
compare(&mut c, &u, &[]);
u[0].segments[0].text = u[0].segments[0].text.replace("CONST A=4", "' removed");
assert!(!compare(&mut c, &u, &[]));
u.swap(0, 2);
assert!(!compare(&mut c, &u, &[]));
u.remove(0);
compare(&mut c, &u, &[]);
}
#[test]
fn includes_source_ids_data_and_forms_relink_without_patching_old_products() {
let mut c = ProjectCompiler::default();
let mut u = vec![
unit(
"A",
"DATA 3,\"Hi\"\nREAD a%, b$\nPRINT a%;b$\nCALL Work\nEND",
),
unit("B", "SUB Work\nPRINT 2\nEND SUB"),
];
u[0].segments.insert(
0,
SourceSegment {
file: "shared.bi".into(),
first_line: 7,
text: "CONST N=1\n".into(),
},
);
assert!(compare(&mut c, &u, &[]));
u[0].segments[0].text = "CONST N=2\n".into();
assert!(compare(&mut c, &u, &[]));
u.swap(0, 1);
assert!(compare(&mut c, &u, &[]));
u[0].segments.insert(
0,
SourceSegment {
file: "extra.bi".into(),
first_line: 1,
text: "' extra source\n".into(),
},
);
assert!(compare(&mut c, &u, &[]));
for index in [None, Some(0), Some(1)] {
let property = index.map_or(String::new(), |index| format!(" Index = {index}\n"));
let text = format!(
"VERSION 1.00\nBEGIN Form Main\n BEGIN CommandButton Button\n{property} END\nEND\n"
);
let form = frm::read_text("Main.frm", &text).unwrap();
let units = [unit("Main", "'$FORM\n")];
assert!(compare(&mut c, &units, &[form]));
}
}
#[test]
fn unused_constants_do_not_invalidate_independent_units_and_declare_changes_do() {
let mut c = ProjectCompiler::default();
let mut u = vec![
unit("A", "CONST A=1\n"),
unit("B", "CONST B=A+1\n"),
unit("C", "PRINT B\n"),
unit("D", "PRINT 7\n"),
];
assert!(compare(&mut c, &u, &[]));
u[0].segments[0].text = "CONST A=3\n".into();
assert!(compare(&mut c, &u, &[]));
assert_eq!((c.stats.compiled, c.stats.reused), (3, 1));
let mut u = vec![
unit("A", "DECLARE SUB Work(n%)\nCALL Work(1)\nEND"),
unit("B", "SUB Work(n%)\nPRINT n%\nEND SUB"),
];
assert!(compare(&mut c, &u, &[]));
u[1].segments[0].text = "SUB Work(n$)\nPRINT n$\nEND SUB".into();
assert!(!compare(&mut c, &u, &[]));
u[0].segments[0].text = "DECLARE SUB Work(n$)\nCALL Work(\"ok\")\nEND".into();
assert!(compare(&mut c, &u, &[]));
}