Phase 4: Formularmodell und Objektsprache
This commit is contained in:
@@ -31,6 +31,7 @@ struct Eintrag {
|
||||
art: String,
|
||||
status: String,
|
||||
fundstelle: String,
|
||||
quelle: String,
|
||||
zeile: usize,
|
||||
}
|
||||
|
||||
@@ -61,6 +62,7 @@ fn inventar() -> Vec<Eintrag> {
|
||||
art: spalten[1].to_string(),
|
||||
status: spalten[3].to_string(),
|
||||
fundstelle: spalten[4].to_string(),
|
||||
quelle: spalten[5].to_string(),
|
||||
zeile: i + 1,
|
||||
});
|
||||
}
|
||||
@@ -155,7 +157,10 @@ fn stand() -> Stand {
|
||||
signaturen,
|
||||
verboten: literale(ban),
|
||||
nicht_verfuegbar,
|
||||
syntaktisch: literale(syn).into_iter().map(|s| s.to_uppercase()).collect(),
|
||||
syntaktisch: literale(syn)
|
||||
.into_iter()
|
||||
.map(|s| s.to_uppercase())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +172,15 @@ fn statusvokabular_ist_beschraenkt() {
|
||||
assert!(
|
||||
matches!(
|
||||
e.art.as_str(),
|
||||
"Anweisung" | "Funktion" | "Metabefehl" | "Routine"
|
||||
"Anweisung"
|
||||
| "Funktion"
|
||||
| "Metabefehl"
|
||||
| "Routine"
|
||||
| "Eigenschaft"
|
||||
| "Methode"
|
||||
| "Ereignis"
|
||||
| "Datentyp"
|
||||
| "Operator"
|
||||
),
|
||||
"Zeile {}: `{}` trägt unzulässige Art `{}`",
|
||||
e.zeile,
|
||||
@@ -185,6 +198,84 @@ fn statusvokabular_ist_beschraenkt() {
|
||||
}
|
||||
}
|
||||
|
||||
fn pruefe_forms(inv: &[Eintrag]) -> Vec<String> {
|
||||
use tb_frontend::forms::{events, method_is_implemented, methods, properties, ObjectClass};
|
||||
let mut erwartet = BTreeMap::new();
|
||||
for class in ObjectClass::ALL {
|
||||
for p in properties(class) {
|
||||
erwartet.insert(
|
||||
format!("{}.{}", class.name(), p.name),
|
||||
("Eigenschaft", IMPLEMENTIERT, "tb-ui::forms"),
|
||||
);
|
||||
}
|
||||
for m in methods(class) {
|
||||
erwartet.insert(
|
||||
format!("{}.{m}", class.name()),
|
||||
if method_is_implemented(class, m) {
|
||||
("Methode", IMPLEMENTIERT, "tb-ui::forms")
|
||||
} else {
|
||||
("Methode", OFFEN, "-")
|
||||
},
|
||||
);
|
||||
}
|
||||
for e in events(class) {
|
||||
erwartet.insert(
|
||||
format!("{}_{e}", class.name()),
|
||||
("Ereignis", IMPLEMENTIERT, "tb-vm::interp"),
|
||||
);
|
||||
}
|
||||
}
|
||||
let gefunden: BTreeMap<_, _> = inv
|
||||
.iter()
|
||||
.filter(|e| matches!(e.art.as_str(), "Eigenschaft" | "Methode" | "Ereignis"))
|
||||
.map(|e| (e.name.to_uppercase(), e))
|
||||
.collect();
|
||||
let mut fehler = Vec::new();
|
||||
for (name, (art, status, fundstelle)) in &erwartet {
|
||||
match gefunden.get(name) {
|
||||
None => fehler.push(format!("`{name}` fehlt")),
|
||||
Some(e)
|
||||
if e.art != *art
|
||||
|| e.status != *status
|
||||
|| e.fundstelle != *fundstelle
|
||||
|| e.quelle != "forms-referenz" =>
|
||||
{
|
||||
fehler.push(format!("`{name}`: Art/Status/Fundstelle/Quelle falsch"))
|
||||
}
|
||||
Some(_) => {}
|
||||
}
|
||||
}
|
||||
for name in gefunden.keys() {
|
||||
if !erwartet.contains_key(name) {
|
||||
fehler.push(format!("`{name}` ist nicht in der Klassentabelle"));
|
||||
}
|
||||
}
|
||||
fehler
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forms_inventar_stimmt_mit_der_klassentabelle_ueberein() {
|
||||
let fehler = pruefe_forms(&inventar());
|
||||
assert!(
|
||||
fehler.is_empty(),
|
||||
"Forms-Inventar weicht ab:\n {}",
|
||||
fehler.join("\n ")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn falsch_gesetzte_eigenschaft_wird_erkannt() {
|
||||
let mut inv = inventar();
|
||||
let e = inv
|
||||
.iter_mut()
|
||||
.find(|e| e.name == "TEXTBOX.SELSTART")
|
||||
.unwrap();
|
||||
e.status = OFFEN.into();
|
||||
assert!(pruefe_forms(&inv)
|
||||
.iter()
|
||||
.any(|f| f.contains("TEXTBOX.SELSTART")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_features_nennen_eine_fundstelle_in_der_sprachreferenz() {
|
||||
for e in inventar() {
|
||||
@@ -343,11 +434,7 @@ fn inventar_stimmt_mit_code_ueberein() {
|
||||
fn abdeckungsstand_wird_ausgewiesen() {
|
||||
let inv = inventar();
|
||||
let zaehle = |s: &str| inv.iter().filter(|e| e.status == s).count();
|
||||
let (i, o, n) = (
|
||||
zaehle(IMPLEMENTIERT),
|
||||
zaehle(OFFEN),
|
||||
zaehle(NON_FEATURE),
|
||||
);
|
||||
let (i, o, n) = (zaehle(IMPLEMENTIERT), zaehle(OFFEN), zaehle(NON_FEATURE));
|
||||
println!(
|
||||
"Abdeckung: implementiert {i} · offen {o} · Non-Feature {n} · gesamt {}",
|
||||
inv.len()
|
||||
@@ -384,10 +471,22 @@ fn dokumentierte_elemente_werden_namentlich_abgewiesen() {
|
||||
("CALLS Foo", "CALLS", "Feature unavailable"),
|
||||
// Non-Feature über die Syntax (teilt das Schlüsselwort)
|
||||
("LINE (1, 1)-(2, 2)", "LINE", "Feature unavailable"),
|
||||
("GET (1, 1)-(2, 2), A", "GET (Grafik)", "Feature unavailable"),
|
||||
(r#"OPEN "COM1:9600,N,8,1" AS #1"#, "OPEN COM", "Feature unavailable"),
|
||||
(
|
||||
"GET (1, 1)-(2, 2), A",
|
||||
"GET (Grafik)",
|
||||
"Feature unavailable",
|
||||
),
|
||||
(
|
||||
r#"OPEN "COM1:9600,N,8,1" AS #1"#,
|
||||
"OPEN COM",
|
||||
"Feature unavailable",
|
||||
),
|
||||
// Ereignisgerät als Funktionsform
|
||||
("ON PEN GOSUB Ziel\nZiel:\nRETURN", "ON PEN", "Feature unavailable"),
|
||||
(
|
||||
"ON PEN GOSUB Ziel\nZiel:\nRETURN",
|
||||
"ON PEN",
|
||||
"Feature unavailable",
|
||||
),
|
||||
];
|
||||
|
||||
for (quelle, element, erwartet) in faelle {
|
||||
@@ -411,7 +510,12 @@ fn dokumentierte_elemente_werden_namentlich_abgewiesen() {
|
||||
/// generischen Syntaxfehler.
|
||||
#[test]
|
||||
fn offene_elemente_erzeugen_keinen_syntaxfehler() {
|
||||
for quelle in ["CLS", "LOCATE 5, 10", "COLOR 14, 1", r#"OPEN "d.txt" FOR OUTPUT AS #1"#] {
|
||||
for quelle in [
|
||||
"CLS",
|
||||
"LOCATE 5, 10",
|
||||
"COLOR 14, 1",
|
||||
r#"OPEN "d.txt" FOR OUTPUT AS #1"#,
|
||||
] {
|
||||
let a = tb_frontend::analyze_source("probe.bas", quelle);
|
||||
assert!(
|
||||
a.diagnostics.is_empty(),
|
||||
|
||||
Reference in New Issue
Block a user