Ereigniszustellung und Hostgrenzen korrigieren und Change archivieren
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
## Why
|
||||
|
||||
Ereignisse gehen verloren, laufen in falscher Reihenfolge oder umgehen Menüsperren. F04–F08, F18 und F20 zeigen außerdem Zeitabfragen ohne Bedarf und eine unerwünschte Terminalabhängigkeit der VM. Siehe [Review vom 05.09.2026](../../../docs/reviews/2026-09-05-openspec-code-review.md).
|
||||
Ereignisse gehen verloren, laufen in falscher Reihenfolge oder umgehen Menüsperren. F04–F08, F18 und F20 zeigen außerdem Zeitabfragen ohne Bedarf und eine unerwünschte Terminalabhängigkeit der VM. Siehe [Review vom 05.09.2026](../../../../docs/reviews/2026-09-05-openspec-code-review.md).
|
||||
|
||||
## What Changes
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
//! Ursprüngliche Reproduktionen: fünf behobene Befunde und eine Kontrollprobe.
|
||||
//! Separater Cargo-Treiber; siehe review.md. Alle sechs Prüfungen müssen bestehen.
|
||||
use tb_frontend::forms::{FormCatalog, ObjectClass};
|
||||
use tb_runtime::host::{CaptureHost, Ereignis, Host, MausArt, MausEreignis};
|
||||
use tb_runtime::screen::TextScreen;
|
||||
use tb_runtime::value::Value;
|
||||
use tb_vm::interp::{RunEvent, Vm};
|
||||
|
||||
fn vm(src: &str) -> Vm {
|
||||
let mut catalog = FormCatalog::default();
|
||||
catalog.add("Form1", ObjectClass::Form, None, false);
|
||||
catalog.add("Text1", ObjectClass::TextBox, Some("Form1"), false);
|
||||
catalog.add("Text2", ObjectClass::TextBox, Some("Form1"), false);
|
||||
catalog.add("Timer1", ObjectClass::Timer, Some("Form1"), false);
|
||||
catalog.add("Menu1", ObjectClass::Menu, Some("Form1"), false);
|
||||
catalog.add("Item1", ObjectClass::Menu, Some("Menu1"), false);
|
||||
Vm::new(
|
||||
tb_vm::compile_source_with_forms("FORM1", src, &catalog)
|
||||
.unwrap_or_else(|d| panic!("{d:?}")),
|
||||
)
|
||||
}
|
||||
|
||||
fn stop(vm: &mut Vm, host: &mut dyn Host) {
|
||||
assert!(matches!(vm.run(host), RunEvent::Stopped { .. }));
|
||||
}
|
||||
|
||||
fn string(vm: &Vm, name: &str) -> String {
|
||||
match vm.inspect(name) {
|
||||
Some(Value::Str(s)) => s.to_string(),
|
||||
other => panic!("{other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn key(s: &str) -> Ereignis {
|
||||
Ereignis::Taste(s.into(), 0)
|
||||
}
|
||||
fn mouse() -> Ereignis {
|
||||
Ereignis::Maus(MausEreignis {
|
||||
art: MausArt::Druck,
|
||||
taste: 1,
|
||||
shift: 0,
|
||||
zeile: 1,
|
||||
spalte: 1,
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn review_signal_waehrend_blockierender_eingabe() {
|
||||
struct Input {
|
||||
reads: usize,
|
||||
}
|
||||
impl Host for Input {
|
||||
fn present(&mut self, _: &TextScreen) {}
|
||||
fn next_event(&mut self, block: bool) -> Option<Ereignis> {
|
||||
if !block {
|
||||
return None;
|
||||
}
|
||||
self.reads += 1;
|
||||
Some(if self.reads == 1 {
|
||||
Ereignis::Signal(2)
|
||||
} else {
|
||||
Ereignis::Ende
|
||||
})
|
||||
}
|
||||
fn jetzt_ms(&mut self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn warten(&mut self, _: Option<u64>) -> Option<Ereignis> {
|
||||
self.next_event(true)
|
||||
}
|
||||
}
|
||||
let mut results = vec![];
|
||||
for statement in ["INPUT s$", "LINE INPUT s$", "s$ = INPUT$(1)"] {
|
||||
let src =
|
||||
format!("ON SIGNAL(2) GOSUB H\nSIGNAL(2) ON\n{statement}\nEND\nH:\nPRINT \"H\"\nEND");
|
||||
let mut vm = Vm::new(tb_vm::compile_source("TEST", &src).unwrap());
|
||||
let result = vm.run(&mut Input { reads: 0 });
|
||||
println!(
|
||||
"{statement}: {result:?}; Ausgabe={:?}",
|
||||
tb_runtime::snapshot::text(&vm.rt.screen)
|
||||
);
|
||||
results.push(result);
|
||||
}
|
||||
assert!(results.iter().all(|result| *result == RunEvent::Ended));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn review_objektargument_darf_gotfocus_nicht_vorziehen() {
|
||||
let mut vm = vm("DIM SHARED s$\nText1.TabIndex = 0\nText2.TabIndex = 1\nForm1.Show\nText1.SetFocus\nSTOP\nDOEVENTS\nEND\nSUB Text1_LostFocus()\nSHARED s$\nCALL Mark(Text1)\ns$ = s$ + \"l\"\nEND SUB\nSUB Mark(c AS CONTROL)\nSHARED s$\ns$ = s$ + \"L\"\nEND SUB\nSUB Text2_GotFocus()\nSHARED s$\ns$ = s$ + \"G\"\ns$ = s$ + \"g\"\nEND SUB");
|
||||
let mut host = CaptureHost::default();
|
||||
stop(&mut vm, &mut host);
|
||||
host.ereignis(key("\t"));
|
||||
assert_eq!(vm.run(&mut host), RunEvent::Ended);
|
||||
assert_eq!(string(&vm, "s"), "LlGg");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn review_gleichzeitig_key_und_uevent_behalten_prioritaet() {
|
||||
let mut vm = vm("ON KEY(15) GOSUB K\nKEY 15, CHR$(0) + CHR$(20)\nKEY(15) ON\nON UEVENT GOSUB U\nUEVENT ON\nSTOP\nDOEVENTS\nEND\nK:\ns$ = s$ + \"K\"\nRETURN\nU:\ns$ = s$ + \"U\"\nRETURN");
|
||||
let mut host = CaptureHost::default();
|
||||
stop(&mut vm, &mut host);
|
||||
host.ereignis(key("t"));
|
||||
vm.rt.pump(&mut host, false);
|
||||
vm.rt.traps.melden(tb_runtime::traps::Quelle::UEvent);
|
||||
assert_eq!(vm.run(&mut host), RunEvent::Ended);
|
||||
assert_eq!(string(&vm, "s"), "KU");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn review_modale_eingabe_vor_eof_wird_zugestellt() {
|
||||
let mut vm = vm("DIM SHARED s$\nForm1.Show 1\nEND\nSUB Form_MouseDown(Button AS INTEGER, Shift AS INTEGER, X AS SINGLE, Y AS SINGLE)\nSHARED s$\ns$ = \"M\"\nUNLOAD Form1\nEND SUB");
|
||||
let mut host = CaptureHost::default();
|
||||
host.ereignis_nach(2, mouse());
|
||||
host.ereignis_nach(2, Ereignis::Ende);
|
||||
assert_eq!(vm.run(&mut host), RunEvent::Ended);
|
||||
assert_eq!(string(&vm, "s"), "M");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn review_menu_schliessen_stellt_pending_trap_vor_erneutem_warten_zu() {
|
||||
struct MenuHost {
|
||||
time: u64,
|
||||
waits: usize,
|
||||
}
|
||||
impl Host for MenuHost {
|
||||
fn present(&mut self, _: &TextScreen) {}
|
||||
fn next_event(&mut self, _: bool) -> Option<Ereignis> {
|
||||
None
|
||||
}
|
||||
fn jetzt_ms(&mut self) -> u64 {
|
||||
self.time
|
||||
}
|
||||
fn warten(&mut self, deadline: Option<u64>) -> Option<Ereignis> {
|
||||
self.waits += 1;
|
||||
if self.waits == 1 {
|
||||
return Some(key("\u{1b}"));
|
||||
}
|
||||
self.time = deadline.unwrap();
|
||||
None
|
||||
}
|
||||
}
|
||||
let mut vm = vm("ON UEVENT GOSUB H\nUEVENT ON\nMenu1.Caption = \"&Datei\"\nItem1.Caption = \"&Ende\"\nForm1.Show\nSTOP\nSLEEP 60\nEND\nH:\ns$ = \"U\"\nRETURN");
|
||||
let mut host = CaptureHost::default();
|
||||
stop(&mut vm, &mut host);
|
||||
vm.forms.handle_key("d", 4);
|
||||
vm.rt.traps.melden(tb_runtime::traps::Quelle::UEvent);
|
||||
let mut host = MenuHost { time: 0, waits: 0 };
|
||||
assert_eq!(vm.run(&mut host), RunEvent::Ended);
|
||||
println!(
|
||||
"Menü/SLEEP: Zeit={}, Warteaufrufe={}, Handler={}",
|
||||
host.time,
|
||||
host.waits,
|
||||
string(&vm, "s")
|
||||
);
|
||||
assert_eq!(host.waits, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn review_maus_vor_getrappter_taste() {
|
||||
let mut vm = vm("DIM SHARED s$\nON KEY(15) GOSUB H\nKEY 15, CHR$(0) + CHR$(20)\nKEY(15) ON\nForm1.Show\nSTOP\nDOEVENTS\nDOEVENTS\nEND\nH:\ns$ = s$ + \"K\"\nRETURN\nSUB Form_MouseDown(Button AS INTEGER, Shift AS INTEGER, X AS SINGLE, Y AS SINGLE)\nSHARED s$\ns$ = s$ + \"M\"\nEND SUB");
|
||||
let mut host = CaptureHost::default();
|
||||
stop(&mut vm, &mut host);
|
||||
host.ereignis(mouse());
|
||||
host.ereignis(key("t"));
|
||||
assert_eq!(vm.run(&mut host), RunEvent::Ended);
|
||||
assert_eq!(string(&vm, "s"), "MK");
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
# Verifikation: ereigniszustellung-und-hostgrenzen
|
||||
|
||||
Stand nach Behebung: 05.09.2026, aktueller Arbeitsbaum auf `57d6386`.
|
||||
**Alle fünf Verifikationsbefunde sind behoben.** Die ursprünglichen sechs
|
||||
Review-Proben bestehen unverändert in ihren Erwartungen; zusätzlich wurden
|
||||
sie in die Workspace-Regressionen übernommen und um verwandte Pfade ergänzt.
|
||||
|
||||
| Dimension | Ergebnis |
|
||||
| --- | --- |
|
||||
| Vollständigkeit | 12/12 Tasks erledigt; ursprüngliche Zuordnung der 9 Delta-Requirements und 33 Szenarien um Korrektur-Nachweise ergänzt. |
|
||||
| Korrektheit | 419 Workspace-Tests und alle sechs separaten Review-Proben bestanden; keine offenen V1–V5-Befunde. |
|
||||
| Kohärenz | Gemeinsame Zustellung auch während Eingabe und nach Menüschluss; synchrone LOAD-Wirkungen getrennt, klassische Quellpriorität erhalten. |
|
||||
|
||||
## Korrekturen und Regressionen
|
||||
|
||||
| Befund | Behebung | Nachweis in `crates/tb-vm/tests/events.rs` |
|
||||
| --- | --- | --- |
|
||||
| V1 | Fortsetzbarer Konsolenleser für INPUT, LINE INPUT, INPUT$ und RANDOMIZE ohne Argument. Frame hält Teiltext/Restlänge, Stack hält Operanden und Referenzen. Trap-Unwinding erhält gemeinsam genutzte Locals; Fehler und RETURN label verwerfen abgebrochene Eingaben. | Ursprüngliche Signalprobe; Teiltext/BYREF/Ausdruck, verschachtelte Eingaben mit STOP/CONT, Timer, Redo, lokaler Fehlerhandler, RANDOMIZE und RETURN label. |
|
||||
| V2 | Implizite Objektaufrufe stellen ausschließlich notwendige LOAD-Handler zu. Deren Rückkehr zieht keine unabhängigen Forms-Ereignisse vor. | LlGg mit Control-Argument, Property-Lesen/-Schreiben, Refresh und LOAD; bestehende Load-vor-Show- und Unload-Tests. |
|
||||
| V3 | Nach dem Schließen des Menüs wird die gemeinsame Auswahl vor jedem erneuten Warten wiederholt. | Esc bei Zeit 0, UEVENT zugestellt nach genau einem Warteaufruf; bestehende Menüsperrtests einschließlich Forms-Timer. |
|
||||
| V4 | Modale und modellose Schleifen beenden bei EOF erst, wenn kein Handler mehr zugestellt wurde. | Ursprüngliche MouseDown/Unload-Probe; jeweils zwei Mausereignisse vor EOF ergeben MM in beiden Schleifen. |
|
||||
| V5 | Getrappte Tasten am Queue-Anfang werden vor der klassischen Quellenauswahl gemeldet. | Gleichzeitiges KEY/UEVENT ergibt KU; Maus vor getrappter Taste bleibt MK. |
|
||||
|
||||
## Ursprüngliche Befunde — historischer Stand vor der Behebung
|
||||
|
||||
Die folgenden Ist-Beobachtungen und Ursachen dokumentieren den fehlgeschlagenen
|
||||
Review vor diesen Korrekturen. Alle fünf sind durch die Nachweise oben geschlossen.
|
||||
|
||||
### V1 — Während blockierender Eingabe werden eintreffende Traps nicht zugestellt (hoch)
|
||||
|
||||
**Reproduktion:** `ON SIGNAL(2) GOSUB H`, `SIGNAL(2) ON`, danach INPUT.
|
||||
Der Host liefert erst im blockierenden `next_event(true)` Signal(2), beim
|
||||
nächsten Aufruf Eingabeende. Der Handler H würde `PRINT "H"` und END ausführen.
|
||||
|
||||
**Ist:** INPUT, LINE INPUT und INPUT$(1) enden jeweils mit Fehler 62, H läuft
|
||||
nicht. Ohne nachfolgende Eingabe kann die Laufzeit stattdessen erneut
|
||||
blockieren, obwohl der Signal-Trap schon ansteht.
|
||||
|
||||
**Soll:** Vor dem erneuten Blockieren muss der erlaubte Zustellpunkt genutzt
|
||||
werden; H beendet das Programm, Ausgabe H, kein Fehler 62.
|
||||
|
||||
**Ursache:** Der einmalige VM-Check vor der Eingabeinstruktion genügt nicht.
|
||||
[RtState::naechste_taste](../../../../crates/tb-runtime/src/builtins.rs#L209)
|
||||
pumpt innerhalb seiner Schleife weitere Ereignisse und blockiert erneut,
|
||||
ohne zur VM-Zustellung zurückzukehren. Dasselbe betrifft die drei genannten
|
||||
Eingabefunktionen über ihren gemeinsamen Leser.
|
||||
|
||||
**Vertrag:** `ereignissteuerung` / Abschließende Liste der Zustellpunkte,
|
||||
Signal-Trap auf Betriebssystemsignalen; Design D2; Task 2.1.
|
||||
**Korrektur:** Blockierende Eingabe so fortsetzbar machen, dass nach einem
|
||||
Nicht-Tasten-Ereignis und vor dem nächsten Host-Warten die gemeinsame
|
||||
VM-Zustellung laufen kann. Eingabepuffer, Referenzen und Operanden erhalten.
|
||||
|
||||
### V2 — Control-Argumente stellen wartende Forms-Handler mitten in einer Anweisung zu (hoch)
|
||||
|
||||
**Reproduktion:** Fokuswechsel Text1 → Text2. Text1_LostFocus ruft als erste
|
||||
wirksame Anweisung `CALL Mark(Text1)` auf; Mark hängt L an eine gemeinsame
|
||||
Variable, LostFocus danach l. Text2_GotFocus hängt G und g an.
|
||||
|
||||
**Ist:** `GgLl`. **Soll:** `LlGg`.
|
||||
|
||||
**Ursache:** [Instr::PushObject](../../../../crates/tb-vm/src/interp.rs#L1344)
|
||||
ruft nach dem Ablegen eines bereits geladenen Control-Objekts bedingungslos
|
||||
`dispatch_next_form_event` auf. Dadurch läuft GotFocus noch vor dem eigentlichen
|
||||
CALL, obwohl weder DOEVENTS noch modales Warten vorliegt. Der Schutz am ersten
|
||||
Stmt-Eintritt und `forms_frei` werden damit umgangen.
|
||||
|
||||
**Vertrag:** Fokus, Tabreihenfolge und Access-Keys / Handlerwirkungen in
|
||||
Fokusreihenfolge; Abschließende Liste der Zustellpunkte; Design D1/D2;
|
||||
Tasks 1.2 und 2.1.
|
||||
**Korrektur:** Unabhängig wartende Ereignisse aus Objekt-Opcodes heraus nicht
|
||||
zustellen. Notwendige synchrone Load-/Unload-Wirkungen gezielt behandeln;
|
||||
alle Aufrufer von `dispatch_next_form_event` gegen dieselben Zustellregeln prüfen.
|
||||
|
||||
### V3 — Menüschließen während SLEEP führt zu erneutem Warten trotz bereitem Trap (hoch)
|
||||
|
||||
**Reproduktion:** Menü offen, UEVENT anstehend, `SLEEP 60`. Der erste
|
||||
Host-Warteaufruf liefert Esc bei unveränderter virtueller Zeit 0.
|
||||
|
||||
**Ist:** Esc schließt das Menü; die VM wartet ein zweites Mal bis Hostzeit
|
||||
60.000 ms. Erst dann läuft der UEVENT-Handler.
|
||||
**Soll:** Nach Esc läuft der freigegebene Handler ohne zweiten Warteaufruf
|
||||
und beendet SLEEP sofort. Bei einer Wartephase ohne eigene Deadline droht
|
||||
entsprechend Warten auf ein weiteres externes Ereignis.
|
||||
|
||||
**Ursache:** Die klassische Trap-Auswahl in
|
||||
[Vm::zustellen](../../../../crates/tb-vm/src/interp.rs#L625) erfolgt vor der
|
||||
Eingabeverarbeitung. Nachdem Esc die Menüsperre aufgehoben hat, kehrt die
|
||||
Methode ohne erneute Auswahl zurück;
|
||||
[SLEEP](../../../../crates/tb-vm/src/interp.rs#L707) ruft unmittelbar wieder
|
||||
`warten` auf.
|
||||
|
||||
**Vertrag:** Menüsystem / Traps ruhen im geöffneten Menü und werden danach
|
||||
fortgesetzt; Abschließende Liste der Zustellpunkte / SLEEP endet durch
|
||||
Ereignis; Design D2/D3; Tasks 2.1/2.2.
|
||||
**Korrektur:** Nach einer Eingabe, die die Zustellbarkeit verändert, bereite
|
||||
Ereignisse vor jedem weiteren blockierenden Warten erneut auswählen.
|
||||
|
||||
### V4 — Modale Schleife verliert bereits angenommenes Ereignis unmittelbar vor EOF (hoch)
|
||||
|
||||
**Reproduktion:** `Form1.Show 1`; nach der zweiten Anzeige liefert der Host
|
||||
MouseDown und unmittelbar danach Eingabeende. Der MouseDown-Handler hängt M
|
||||
an und entlädt das Formular.
|
||||
|
||||
**Ist:** VM meldet Ended, die gemeinsame Variable bleibt leer; der Handler
|
||||
wurde nicht ausgeführt. **Soll:** Das vor EOF eingetroffene Mausereignis wird
|
||||
noch wirksam, Ergebnis M, Formular entladen.
|
||||
|
||||
**Ursache:** In der [modalen Run-Schleife](../../../../crates/tb-vm/src/interp.rs#L890)
|
||||
setzt `zustellen` einen Handlerframe auf. Direkt danach wird ungeachtet von
|
||||
`dispatched` wegen `rt.ende` zurückgekehrt. Das Ereignis ist bereits aus der
|
||||
Queue entfernt und wird stillschweigend verloren.
|
||||
|
||||
**Vertrag:** Mausereignisse in Zellenkoordinaten / Reihenfolge bis zur
|
||||
beobachtbaren Handlerausführung; Design D1/D2; Tasks 1.2/2.1.
|
||||
**Korrektur:** EOF als Ende der Eingabequelle behandeln und bereits angenommene
|
||||
Ereignisse/Handler geordnet abarbeiten, bevor die modale Ausführung endet.
|
||||
|
||||
## Ursprüngliche weitere Designabweichung
|
||||
|
||||
### V5 — Gleichzeitig wartende klassische Quellen erhalten eine andere Priorität (mittel)
|
||||
|
||||
**Reproduktion:** KEY(15) ON und UEVENT ON. Vor demselben Zustellpunkt liegen
|
||||
eine passende Taste in der Eingabequeue und ein gemeldeter UEVENT vor.
|
||||
Die Handler hängen K bzw. U an.
|
||||
|
||||
**Ist:** `UK`. **Soll:** `KU` gemäß der bestehenden klassischen Quellpriorität.
|
||||
|
||||
**Ursache:** [Vm::zustellen](../../../../crates/tb-vm/src/interp.rs#L631) wählt
|
||||
bereits gemeldete Traps aus, bevor die wartende Taste durch `taste_trappen`
|
||||
gemeldet wird. Die Prioritätsauswahl in Traps sieht die Taste deshalb noch
|
||||
nicht. Früher erfolgte die Tastenprüfung vor der gemeinsamen Trap-Auswahl.
|
||||
|
||||
**Vertrag:** Design / Non-Goals: keine Änderung der festgelegten Priorität
|
||||
gleichzeitig fälliger klassischer Quellen; D1/D2; Task 2.1.
|
||||
**Korrektur:** Fälligkeitserfassung und Trap-Auswahl so koordinieren, dass die
|
||||
bestehende Quellpriorität erhalten bleibt, ohne die Reihenfolge zwischen
|
||||
Maus- und Tasteneingaben wieder nach Quellarten umzusortieren.
|
||||
|
||||
## Abdeckung und positive Nachweise
|
||||
|
||||
| Delta-Requirement | Nachweis / Einschränkung |
|
||||
| --- | --- |
|
||||
| Ereignisquellen mit Zeit vom Host | Host-Warteschnittstelle, eingefrorene virtuelle Zeit, klassischer Timer und Forms-Timer-SLEEP getestet. Keine reale Rückfalluhr in der VM; Zustellung nach Menüschluss durch V3-Regression abgesichert. |
|
||||
| Mausereignisse in Zellenkoordinaten | Mapper-/Bounds-Tests, leere Verbraucher, KM/MK-Korpus und gemischte Eingaben. V4-Regressionen sichern EOF in modalen und modellosen Schleifen ab. |
|
||||
| Abschließende Liste der Zustellpunkte | DOEVENTS-Wert, SLEEP, vorbereitete und erst beim Warten eintreffende INPUT-Ereignisse getestet; V1–V3 geschlossen. |
|
||||
| Getrappte Tasten verlassen den Eingabestrom | ON/OFF/STOP × INKEY$/INPUT/LINE INPUT/INPUT$, mehrfaches STOP-Merken und Dialogrestpuffer getestet. |
|
||||
| Signal-Trap auf Betriebssystemsignalen | Beide Kennungen, Maskierung und ungültige Werte getestet. V1-Regression sichert Signalzustellung während blockierender Eingabe ab. |
|
||||
| Fokus, Tabreihenfolge und Access-Keys | Tab/Shift-Tab, BackTab-Mapper, Access-Keys und einfacher LostFocus→GotFocus-Lauf belegt. V2-Regressionen sichern Control-Argumente und verwandte Objektaufrufe ab. |
|
||||
| Menüsystem | Menüeigenschaften/-auswahl und Sperre an normalen Grenzen getestet. V3-Regression sichert Freigabe innerhalb von SLEEP ab. |
|
||||
| Zellenpuffer ohne Terminalabhängigkeit | Separater Workspace kompiliert FRM/VM und liefert KM; 16 Pakete im normalen Dependency-Graph, keines davon crossterm/ratatui/signal-hook. CLI schaltet das Feature ausdrücklich ein. |
|
||||
| Ereigniszustellung an Anweisungsgrenzen | Bestehende SUB-/GOSUB-/BYREF-/RESUME-Tests und Host ohne erlaubte Uhrabfragen grün. V2/V5-Regressionen sichern zulässige Handlerbeginn-Reihenfolge und klassische Priorität ab. |
|
||||
|
||||
Erneut ausgeführt:
|
||||
|
||||
- `cargo test --workspace`: **419 bestanden, 0 fehlgeschlagen, 1 ignoriert**.
|
||||
- `openspec validate ereigniszustellung-und-hostgrenzen --strict`: gültig.
|
||||
- Isolierter Einbetter mit CaptureHost: **KM**, Dependency-Prüfung erfolgreich.
|
||||
- Separate [Review-Proben](review-probes.rs): **6 bestanden, 0 fehlgeschlagen**.
|
||||
Die Kontrollprobe prüft Maus unmittelbar vor getrappter Taste.
|
||||
- Workspace-Ereignistests: **25 bestanden**, einschließlich Frist-Watchdog.
|
||||
- Keine bestehenden Sollausgaben neu aufgezeichnet oder geändert.
|
||||
|
||||
Warnings-denied Clippy, Formatprüfungen und Compile-/VM-Benchmarks wurden
|
||||
nach der Behebung erneut erfolgreich ausgeführt; Zahlen siehe
|
||||
[verification.md](verification.md). Kein interaktiver Terminaltest und keine
|
||||
Windows-/Linux-Matrix. Die Proben speisen Ereignisse
|
||||
gezielt über Host ein und benötigen weder echte Signale noch reale Wartefristen.
|
||||
|
||||
## Review-Proben reproduzieren
|
||||
|
||||
Im Repository-Wurzelverzeichnis ausführen. Das erzeugt ausschließlich einen
|
||||
separaten temporären Cargo-Treiber und referenziert den aktuellen Produktcode:
|
||||
|
||||
```sh
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
import json, subprocess, tempfile
|
||||
root = Path.cwd()
|
||||
p = Path(tempfile.mkdtemp(prefix='tb-review-'))
|
||||
manifest = '[package]\nname="tb-review"\nversion="0.0.0"\nedition="2021"\n[workspace]\n'
|
||||
manifest += '[[test]]\nname="befunde"\npath=' + json.dumps(str(root / 'openspec/changes/archive/2026-09-05-ereigniszustellung-und-hostgrenzen/review-probes.rs')) + '\n[dependencies]\n'
|
||||
for name in ['tb-vm', 'tb-runtime', 'tb-ui', 'tb-frontend']:
|
||||
manifest += name + ' = { path = ' + json.dumps(str(root / 'crates' / name)) + ' }\n'
|
||||
(p / 'Cargo.toml').write_text(manifest)
|
||||
subprocess.run(['cargo', 'test', '--manifest-path', str(p / 'Cargo.toml'), '--test', 'befunde', '--', '--nocapture'], check=True, timeout=120)
|
||||
PY
|
||||
```
|
||||
|
||||
Alle sechs Proben müssen auf dem korrigierten Stand bestehen. Ihre Erwartungen
|
||||
wurden auch in den regulären Workspace-Testlauf übernommen.
|
||||
@@ -0,0 +1,25 @@
|
||||
## 1. Hosteingabe und Maskierung
|
||||
|
||||
- [x] 1.1 SIGNAL(2), KEY STOP und BackTab am gemeinsamen Eingang berichtigen; verifizieren durch Tests für beide Signale, alle drei KEY-Zustände mit INKEY$/INPUT und den Terminal-Key-Mapper.
|
||||
- [x] 1.2 Geordnete Tasten-/Mausannahme implementieren; verifizieren mit K→M, M→K und gemischten Folgen einschließlich ungetrappter und getrappter Tasten.
|
||||
|
||||
## 2. Dispatch und Warten
|
||||
|
||||
- [x] 2.1 Handlerbeginn/Rückkehr und Menüsperre an allen Zustellpunkten vereinheitlichen; verifizieren mit LostFocus→GotFocus, gleichzeitig wartenden Handlern, UEVENT bei offenem Menü, DOEVENTS und den bestehenden Fehler-/Modalitätstests.
|
||||
- [x] 2.2 Hostgesteuertes Warten und bedarfsgerechte Zeitabfrage implementieren; verifizieren mit einem Host, der unerwartete Uhrabfragen zählt/abweist, eingefrorener und vorrückender Zeit sowie einem Forms-Timer, der SLEEP beendet.
|
||||
- [x] 2.3 Terminal-Feature in tb-ui abgrenzen und CLI ausdrücklich anschließen; verifizieren durch isolierten terminalfreien VM-Einbetter, Dependency-Graph ohne crossterm/ratatui/signal-hook und normalen CLI-Build.
|
||||
|
||||
## 3. Abnahme
|
||||
|
||||
- [x] 3.1 Gezielte geordnete Ereignisprogramme zum Korpus ergänzen; verifizieren mit cargo test --workspace und Vergleich der bestehenden Snapshots ohne automatisches Neuaufzeichnen.
|
||||
- [x] 3.2 Zeit-/Zustellverträge und Backend-Feature dokumentieren; verifizieren mit Compile-/VM-Benchmarks und openspec validate ereigniszustellung-und-hostgrenzen --strict.
|
||||
|
||||
Abnahmenachweise: [verification.md](verification.md).
|
||||
|
||||
## 4. Verifikationsbefunde
|
||||
|
||||
- [x] 4.1 V1: Blockierende Konsoleneingabe fortsetzbar machen; Signale, Timer, Teiltexte und Handler-Rückkehr absichern.
|
||||
- [x] 4.2 V2: Synchrone Load-Wirkungen von wartenden Forms-Ereignissen trennen; Control-Argumente und Geschwisterpfade prüfen.
|
||||
- [x] 4.3 V3: Nach Menüschluss vor erneutem Warten bereit gewordene Ereignisse zustellen.
|
||||
- [x] 4.4 V4: Vor EOF angenommene Ereignisse im modalen Lauf vollständig abarbeiten.
|
||||
- [x] 4.5 V5: Klassische Quellpriorität bei gleichzeitig wartenden Tasten und UEVENT erhalten.
|
||||
@@ -0,0 +1,82 @@
|
||||
# Implementierungsnachweis — 05.09.2026
|
||||
|
||||
Change: `ereigniszustellung-und-hostgrenzen`, Ausgangscommit `57d6386`.
|
||||
Alle zwölf Implementierungs-/Abnahmetasks einschließlich der fünf
|
||||
[Verifikationsbefunde](review.md) sind abgeschlossen. Die vier betroffenen
|
||||
Hauptspezifikationen sind synchronisiert; der Change wurde am 05.09.2026 archiviert.
|
||||
|
||||
## Befunde und Umsetzung
|
||||
|
||||
| Befund / Tasks | Umsetzung | Ausführbarer Nachweis |
|
||||
| --- | --- | --- |
|
||||
| F04 / 1.1 | SIGNAL(1) und SIGNAL(2) werden im gemeinsamen RtState-Eingang gemeldet; nur ungetrapptes SIGINT wird Abbruch. Zusätzlich erkennt der Parser SIGNAL(n) ON/OFF/STOP. | VM-Test `beide_signale_werden_zugestellt_und_beginnen_in_quellreihenfolge` einschließlich ungültiger konstanter/dynamischer Kennungen; Runtime-Tests für STOP/OFF beider Quellen und SIGINT ohne Trap. |
|
||||
| F05 / 1.1 | ON verbraucht die Taste; STOP merkt den Trap einmal und belässt die Eingabe auch nach erneutem ON lesbar. Die Markierung bleibt beim Dialogaufruf erhalten. | VM-Test `key_on_off_stop_gelten_fuer_alle_konsoleneingaben`: drei Zustände × INKEY$, INPUT, LINE INPUT, INPUT$. Runtime-Test mit drei STOP-Tasten; VM-Regression für verbleibende Dialogeingaben. |
|
||||
| F06 / 1.2, 2.1 | Eine Eingabequeue für Tasten/Maus bis zur Annahme. Forms-Eingaben und ihre Handler werden geordnet verarbeitet; erster Handler-Statement-Eintritt geschützt, gleiche Forms-Prozedur nicht rekursiv zugestellt. | VM-Tests für aM, Ma, aMbTM einschließlich KEY-Trap; Fokusfolge LlGg; explizites DOEVENTS ergibt arbr; UNLOAD im eigenen Handler ergibt ur. |
|
||||
| F07 / 2.1 | Gemeinsame Zustellung; separate Menüsperre in Traps ohne Änderung der ON/OFF/STOP-Zustände. Bereits wartende Forms-Timer bleiben gesperrt, Menübedienung kann fortfahren. | VM-Menütest über gewöhnliche Zuweisung, DOEVENTS, SLEEP und LINE INPUT: Zähler bleibt 1, nach Esc werden UEVENT und Forms-Timer nachgeholt, Ergebnis 111. |
|
||||
| F08 / 2.2 | Host besitzt Zeit und Deadline-Warten; keine Instant-/Sleep-/Yield-Rückfallebene in der VM. Auch Forms-SLEEP, modales Warten und sichtbare Forms nach Programmende nutzen den Host. Zeitabfragen nur bei tatsächlichem Zeitbedarf. | Host, der jede unerwartete Uhrabfrage/Warteoperation ablehnt; eingefrorene Uhr über reale 10 ms mit zwei identischen Hostdeadlines; Forms-Timer beendet SLEEP 60 bei Hostzeit 100 ms. Bestehender klassischer SLEEP-/TIMER-Test. |
|
||||
| F18 / 1.1 | BackTab wird als TAB mit SHIFT normalisiert, auch wenn crossterm kein separates SHIFT-Flag liefert. | Terminal-Mapper-Test `backtab_ist_tab_mit_shift_auch_ohne_modifier`; bestehende Forms-Tests für Rückwärtstab, übersprungene Controls und Access-Keys. |
|
||||
| F20 / 2.3 | tb-ui ist standardmäßig terminalfrei; `terminal` aktiviert Backendmodule und optionale Dependencies. CLI aktiviert dieses Feature ausdrücklich. | Separater Workspace `tests/terminalfrei`: FRM laden/kompilieren, Forms ausführen, CaptureHost-Ausgabe KM prüfen. Normaler Dependency-Graph: 16 Pakete, keines davon crossterm, ratatui oder signal-hook. CLI-Kompilation und CLI-Prozesstests erfolgreich. |
|
||||
| 3.1 | Zwei kleine geordnete Ereignisprogramme mit ausdrücklich festgelegten Ausgaben ergänzt. | `eingabe-taste-maus.frm` → KM; `eingabe-maus-taste.frm` → MK. Beide laufen zweimal im bestehenden Korpusharness. Keine bestehende .out-Datei verändert. |
|
||||
| 3.2 | Zeit-, Zustell-, Eingabequeue- und Backend-Verträge einschließlich Einbetter-Migration dokumentiert. | [VM-Design](../../../../docs/tbvm-design.md#ausführungsmodell--unterbrechbarkeit), Checks und Benchmarks unten. |
|
||||
|
||||
Die neuen VM-Regressionen liegen in
|
||||
[crates/tb-vm/tests/events.rs](../../../../crates/tb-vm/tests/events.rs).
|
||||
Bestehende Trap-Rückkehr-, GOSUB-, Fehler-/RESUME-, BYREF- und Modalitätstests
|
||||
wurden im vollständigen Workspace-Testlauf mit ausgeführt.
|
||||
|
||||
## Behebung der Verifikationsbefunde
|
||||
|
||||
V1–V5 sind geschlossen. Der [aktualisierte Report](review.md) ordnet jede
|
||||
Korrektur den ursprünglichen Beobachtungen und ihren Regressionen zu.
|
||||
Die 25 Tests in `events.rs` decken nun auch fortsetzbare Eingabe samt Teiltext,
|
||||
Ausdrucksoperanden, BYREF, verschachtelten Handlern, STOP/CONT, Timer,
|
||||
Fehler-Unwinding und RETURN label ab. Objektaufrufe bewahren die Fokusfolge,
|
||||
Menüschluss gibt Traps sofort frei, beide Formularschleifen arbeiten Eingaben
|
||||
vor EOF ab und die klassische KEY-/UEVENT-Priorität bleibt erhalten.
|
||||
|
||||
## Ausgeführte Prüfungen
|
||||
|
||||
- `cargo test --workspace`: **419 bestanden, 0 fehlgeschlagen, 1 ignoriert**.
|
||||
Ignoriert bleibt ausschließlich `erzeuge_sollausgaben`; keine automatische
|
||||
Neuaufzeichnung von Golden Files.
|
||||
- Separater Treiber der ursprünglichen `review-probes.rs`: **6 bestanden, 0 fehlgeschlagen**.
|
||||
- `cargo clippy -p tb-runtime -p tb-ui -p tb-vm -p tb-cli --all-targets --all-features -- -D warnings`: erfolgreich, ohne Warnungen.
|
||||
- `cargo fmt --all -- --check` und Formatprüfung des separaten Einbetters: erfolgreich.
|
||||
- `cargo run --manifest-path tests/terminalfrei/Cargo.toml --offline`: erfolgreich,
|
||||
`Terminalfreier FRM-/VM-Lauf: KM`.
|
||||
- `cargo tree --manifest-path tests/terminalfrei/Cargo.toml --offline --edges normal --prefix none --format '{p}'`:
|
||||
mit expliziter Prüfung auf ausgeschlossene Terminalpakete, erfolgreich.
|
||||
- `openspec validate ereigniszustellung-und-hostgrenzen --strict`: erfolgreich.
|
||||
- `git diff --check`: erfolgreich.
|
||||
|
||||
## Release-Benchmarks
|
||||
|
||||
`cargo bench -p tb-vm --bench compile --bench vm`, auf diesem Rechner:
|
||||
|
||||
| Last | Ergebnis |
|
||||
| --- | --- |
|
||||
| Compile: 508 Zeilen | 0,83 ms, Budget 50 ms eingehalten |
|
||||
| Compile: 49.760 Zeilen in 20 Modulen | 58 ms, Budget 1.000 ms eingehalten |
|
||||
| INTEGER: 10 Mio. Iterationen | 1.048 ms, 9,5 Mio. Iterationen/s |
|
||||
| DOUBLE: 5 Mio. Iterationen | 504 ms, 9,9 Mio. Iterationen/s |
|
||||
| SUB/BYREF: 1 Mio. Aufrufe | 123 ms, 8,2 Mio. Aufrufe/s |
|
||||
| String-Funktionen: 200.000 Runden | 99 ms, 2,0 Mio. Runden/s |
|
||||
|
||||
Der Compile-Projektbenchmark übersetzt 20 Module einzeln; er prüft keinen
|
||||
Mehrmodul-Linker. Die VM-Zahlen sind lokale Durchsatzmessungen, keine
|
||||
zugesicherte Performance auf anderer Hardware.
|
||||
|
||||
## Synchronisation und Archivierung
|
||||
|
||||
Neun bestehende Requirements in vier Hauptspezifikationen wurden um elf
|
||||
Szenarien ergänzt; alle bisherigen Szenarien bleiben erhalten. Vollständiger
|
||||
Abgleich aller Delta-Requirements erfolgreich; `openspec validate --specs
|
||||
--strict`: 15 bestanden, 0 fehlgeschlagen.
|
||||
|
||||
## Grenzen
|
||||
|
||||
Terminaleingaben wurden über den Mapper und vorbereitete Hostereignisse
|
||||
geprüft. Kein interaktiver Terminal-/Fremdprogrammvergleich und keine
|
||||
Windows-/Linux-Ausführung. Der neue Vertrag verlangt von Einbettern eine
|
||||
Implementierung von Host::warten; CaptureHost modelliert eine endliche
|
||||
Ereignisquelle und explizites virtuelles Fortschreiten bis zur Deadline.
|
||||
@@ -1,15 +0,0 @@
|
||||
## 1. Hosteingabe und Maskierung
|
||||
|
||||
- [ ] 1.1 SIGNAL(2), KEY STOP und BackTab am gemeinsamen Eingang berichtigen; verifizieren durch Tests für beide Signale, alle drei KEY-Zustände mit INKEY$/INPUT und den Terminal-Key-Mapper.
|
||||
- [ ] 1.2 Geordnete Tasten-/Mausannahme implementieren; verifizieren mit K→M, M→K und gemischten Folgen einschließlich ungetrappter und getrappter Tasten.
|
||||
|
||||
## 2. Dispatch und Warten
|
||||
|
||||
- [ ] 2.1 Handlerbeginn/Rückkehr und Menüsperre an allen Zustellpunkten vereinheitlichen; verifizieren mit LostFocus→GotFocus, gleichzeitig wartenden Handlern, UEVENT bei offenem Menü, DOEVENTS und den bestehenden Fehler-/Modalitätstests.
|
||||
- [ ] 2.2 Hostgesteuertes Warten und bedarfsgerechte Zeitabfrage implementieren; verifizieren mit einem Host, der unerwartete Uhrabfragen zählt/abweist, eingefrorener und vorrückender Zeit sowie einem Forms-Timer, der SLEEP beendet.
|
||||
- [ ] 2.3 Terminal-Feature in tb-ui abgrenzen und CLI ausdrücklich anschließen; verifizieren durch isolierten terminalfreien VM-Einbetter, Dependency-Graph ohne crossterm/ratatui/signal-hook und normalen CLI-Build.
|
||||
|
||||
## 3. Abnahme
|
||||
|
||||
- [ ] 3.1 Gezielte geordnete Ereignisprogramme zum Korpus ergänzen; verifizieren mit cargo test --workspace und Vergleich der bestehenden Snapshots ohne automatisches Neuaufzeichnen.
|
||||
- [ ] 3.2 Zeit-/Zustellverträge und Backend-Feature dokumentieren; verifizieren mit Compile-/VM-Benchmarks und openspec validate ereigniszustellung-und-hostgrenzen --strict.
|
||||
@@ -21,6 +21,8 @@ Systemuhr zugreifen. Ein Host ohne Terminal MUST die Zeit frei stellen
|
||||
können, sodass zeitgesteuerte Programme ohne Wartezeit und mit
|
||||
reproduzierbarem Ergebnis prüfbar sind.
|
||||
|
||||
Auch Wartefristen der VM SHALL ausschließlich von der Hostzeit abhängen. Ein Host mit stehender virtueller Zeit MUST NOT durch reale verstrichene Zeit eine Frist scheinbar erreichen.
|
||||
|
||||
#### Scenario: Zeitgesteuerter Trap ohne echte Wartezeit
|
||||
- **WHEN** ein Testhost die Zeit um 5 Sekunden vorstellt und ein Programm `ON TIMER(5) GOSUB Marke` mit `TIMER ON` aktiv hat
|
||||
- **THEN** wird der Trap zugestellt, ohne dass der Test tatsächlich wartet
|
||||
@@ -29,6 +31,10 @@ reproduzierbarem Ergebnis prüfbar sind.
|
||||
- **WHEN** dasselbe zeitgesteuerte Korpusprogramm zweimal mit demselben Zeitverlauf ausgeführt wird
|
||||
- **THEN** ist die Ausgabe beide Male identisch
|
||||
|
||||
#### Scenario: Stehende virtuelle Zeit
|
||||
- **WHEN** ein Host während SLEEP seine Zeit unverändert hält
|
||||
- **THEN** endet SLEEP nicht wegen realer Zeit; eine vom Host gemeldete Frist oder ein zugestelltes Ereignis beendet das Warten
|
||||
|
||||
### Requirement: Mausereignisse in Zellenkoordinaten
|
||||
Die Darstellungsschicht SHALL Mausereignisse der Ausführungsumgebung an
|
||||
die Laufzeit weitergeben: Drücken, Loslassen und Bewegung, jeweils mit
|
||||
@@ -40,6 +46,8 @@ SHALL der Reihenfolge ihres Eintreffens entsprechen. Solange kein
|
||||
Verbraucher ein Mausereignis annimmt, MUST es am Zustellpunkt verworfen
|
||||
werden, damit die Warteschlange nicht unbegrenzt wächst.
|
||||
|
||||
Die Reihenfolge SHALL bis zur beobachtbaren Ausführung der zugehörigen Handler erhalten bleiben; getrennte Quellarten MUST NOT nachträglich nach ihrer Art umgeordnet werden.
|
||||
|
||||
#### Scenario: Position als Zelle
|
||||
- **WHEN** in der linken oberen Ecke der Darstellungsfläche die linke Maustaste gedrückt wird
|
||||
- **THEN** trägt das Ereignis Zeile 1 und Spalte 1
|
||||
@@ -56,6 +64,14 @@ werden, damit die Warteschlange nicht unbegrenzt wächst.
|
||||
- **WHEN** ein Programm ohne Verbraucher für Mausereignisse läuft und die Maus dauerhaft bewegt wird
|
||||
- **THEN** wächst die Warteschlange nicht über ihre Grenze und das Programm läuft unverändert weiter
|
||||
|
||||
#### Scenario: Taste vor Maus bis zum Handler
|
||||
- **WHEN** eine Taste vor einem Mausklick eintrifft und beide Handler eine Kennung anhängen
|
||||
- **THEN** steht die Tastenkennung vor der Mauskennung
|
||||
|
||||
#### Scenario: Maus vor Taste bis zum Handler
|
||||
- **WHEN** ein Mausklick vor einer Taste eintrifft
|
||||
- **THEN** wird zuerst der Maushandler wirksam
|
||||
|
||||
### Requirement: Abschließende Liste der Zustellpunkte
|
||||
Ereignisse SHALL ausschließlich an folgenden Punkten zugestellt werden
|
||||
— das Vorbild prüft die Ereignismarke „before executing the next
|
||||
@@ -66,6 +82,8 @@ Trap-Handler beginnen; insbesondere MUST NOT eine Anweisung in ihrer
|
||||
Mitte unterbrochen werden. Ein zugestelltes Ereignis SHALL ein laufendes
|
||||
`SLEEP` beenden.
|
||||
|
||||
Forms-Ereignisse SHALL auch während SLEEP an dessen bestehenden Zustellpunkten verarbeitet werden. Eine für das Menü geltende Zustellsperre SHALL an jedem Zustellpunkt gleichermaßen gelten.
|
||||
|
||||
#### Scenario: Keine Zustellung innerhalb einer Anweisung
|
||||
- **WHEN** ein Ereignis fällig wird, während eine mehrteilige Anweisung ausgewertet wird
|
||||
- **THEN** läuft die Anweisung zu Ende und der Handler beginnt erst an der folgenden Anweisungsgrenze
|
||||
@@ -78,6 +96,10 @@ Mitte unterbrochen werden. Ein zugestelltes Ereignis SHALL ein laufendes
|
||||
- **WHEN** ein anstehendes Ereignis vorliegt und `DOEVENTS` ausgewertet wird
|
||||
- **THEN** läuft der zugehörige Handler, bevor `DOEVENTS` einen Wert liefert
|
||||
|
||||
#### Scenario: Forms-Timer beendet SLEEP
|
||||
- **WHEN** während SLEEP ein aktiver Forms-Timer fällig wird
|
||||
- **THEN** läuft dessen Handler und SLEEP kehrt danach zurück
|
||||
|
||||
### Requirement: Maskierung je Quelle mit drei Zuständen
|
||||
Jede Ereignisquelle SHALL drei Zustände kennen. `ON` stellt Ereignisse
|
||||
zu. `OFF` verwirft sie; ein während `OFF` eingetretenes Ereignis MUST
|
||||
@@ -146,6 +168,8 @@ zugestellt werden und MUST NOT zusätzlich über `INKEY$`, `INPUT`,
|
||||
`LINE INPUT` oder `INPUT$` sichtbar werden. Bei `OFF` oder `STOP` der
|
||||
Quelle SHALL die Taste dem normalen Eingabestrom erhalten bleiben.
|
||||
|
||||
Unter STOP SHALL die Taste normal lesbar bleiben und zugleich höchstens ein Ereignis für das spätere ON gemerkt werden.
|
||||
|
||||
#### Scenario: Getrappte Taste erscheint nicht bei INKEY$
|
||||
- **WHEN** `ON KEY(1) GOSUB Marke` mit `KEY(1) ON` gilt und F1 gedrückt wird
|
||||
- **THEN** läuft der Handler und ein anschließendes `INKEY$` liefert den leeren String
|
||||
@@ -154,6 +178,10 @@ Quelle SHALL die Taste dem normalen Eingabestrom erhalten bleiben.
|
||||
- **WHEN** derselbe Trap mit `KEY(1) OFF` gilt und F1 gedrückt wird
|
||||
- **THEN** liefert `INKEY$` die Sondertastenfolge für F1
|
||||
|
||||
#### Scenario: STOP merkt und erhält Eingabe
|
||||
- **WHEN** unter KEY(1) STOP F1 gedrückt, per INKEY$ gelesen und danach KEY(1) ON ausgeführt wird
|
||||
- **THEN** liefert INKEY$ die Sondertastenfolge und der Handler läuft nach ON genau einmal
|
||||
|
||||
### Requirement: Benutzerdefiniertes Ereignis
|
||||
`SetUEvent` SHALL ein benutzerdefiniertes Ereignis auslösen, das über
|
||||
`ON UEVENT GOSUB` und die Steueranweisung `UEVENT` denselben Regeln für
|
||||
@@ -208,3 +236,7 @@ ausgewiesen sein.
|
||||
#### Scenario: Signal ohne Trap bleibt Abbruch
|
||||
- **WHEN** ein `SIGINT` eintrifft, ohne dass ein `SIGNAL(1)`-Trap aktiv ist
|
||||
- **THEN** bleibt es beim bisherigen Abbruchverhalten
|
||||
|
||||
#### Scenario: SIGTERM wird zugestellt
|
||||
- **WHEN** SIGNAL(2) ON gilt und ein SIGTERM über den Host eintrifft
|
||||
- **THEN** wird das Ziel von ON SIGNAL(2) GOSUB am nächsten erlaubten Zustellpunkt ausgeführt
|
||||
|
||||
@@ -36,6 +36,8 @@ auslöst oder ihm den Fokus gibt. Enter SHALL die `Default`-Schaltfläche
|
||||
auslösen, Esc die `Cancel`-Schaltfläche. Fokuswechsel MUST `LostFocus`
|
||||
am alten und `GotFocus` am neuen Element auslösen, in dieser Reihenfolge.
|
||||
|
||||
Die über das Terminal gelieferte Rückwärtstab-Taste SHALL denselben Fokuswechsel wie Tab mit Umschalt auslösen. Die Reihenfolge LostFocus vor GotFocus SHALL auch an den Wirkungen der BASIC-Handler sichtbar bleiben.
|
||||
|
||||
#### Scenario: Tab überspringt
|
||||
- **WHEN** das mittlere von drei Elementen `TabStop = 0` trägt und Tab gedrückt wird
|
||||
- **THEN** erhält das dritte Element den Fokus
|
||||
@@ -48,6 +50,14 @@ am alten und `GotFocus` am neuen Element auslösen, in dieser Reihenfolge.
|
||||
- **WHEN** der Fokus von `Text1` auf `Text2` wechselt
|
||||
- **THEN** läuft erst `Text1_LostFocus`, danach `Text2_GotFocus`
|
||||
|
||||
#### Scenario: Handlerwirkungen in Fokusreihenfolge
|
||||
- **WHEN** LostFocus den Text L und GotFocus den Text G an dieselbe Variable anhängen
|
||||
- **THEN** lautet das Ergebnis LG
|
||||
|
||||
#### Scenario: Rückwärtstab vom Terminal
|
||||
- **WHEN** das Terminal eine Rückwärtstab-Taste liefert
|
||||
- **THEN** wechselt der Fokus zum vorherigen zulässigen TabIndex
|
||||
|
||||
### Requirement: Maussteuerung mit Trefferprüfung
|
||||
Ein Mausereignis SHALL dem obersten Steuerelement an seiner Position
|
||||
zugestellt werden; liegt dort keines, dem Formular. Klick, Doppelklick
|
||||
@@ -116,6 +126,10 @@ fortgesetzt werden.
|
||||
- **WHEN** ein Menü geöffnet ist und ein Zeit-Trap fällig wird
|
||||
- **THEN** läuft sein Handler erst, nachdem das Menü geschlossen wurde
|
||||
|
||||
#### Scenario: UEVENT an gewöhnlicher Anweisungsgrenze
|
||||
- **WHEN** ein Menü offen ist und UEVENT vor einer Zuweisung ansteht
|
||||
- **THEN** läuft dessen Handler erst nach dem Schließen des Menüs
|
||||
|
||||
### Requirement: Vordefinierte Dialoge
|
||||
`MSGBOX text$ [, typ% [, titel$]]` SHALL als Anweisung und als Funktion
|
||||
verfügbar sein; die Funktion SHALL die gedrückte Schaltfläche als
|
||||
|
||||
@@ -98,10 +98,16 @@ Darstellungsschicht liegen. Damit MUST jedes Bildschirmverhalten in
|
||||
automatischen Tests ohne Terminal prüfbar sein, und die Ausführungsschicht
|
||||
MUST NOT von einer Terminal-Bibliothek abhängen.
|
||||
|
||||
Ein separater Einbetter SHALL die VM einschließlich Forms ohne Terminalbackend bauen und ausführen können. Der CLI-Runner SHALL das Backend ausdrücklich zuschalten.
|
||||
|
||||
#### Scenario: Bildschirmverhalten im Test ohne Terminal
|
||||
- **WHEN** ein Testprogramm in einer Umgebung ohne Terminal `LOCATE 5, 10 : PRINT "x"` ausführt
|
||||
- **THEN** trägt die Zelle (5, 10) das Zeichen `x` und der Test benötigt kein Terminal
|
||||
|
||||
#### Scenario: Terminalfreier Einbetter
|
||||
- **WHEN** ein separates Programm ausschließlich die VM mit einem Capture-Host einbindet
|
||||
- **THEN** enthält sein aufgelöster Abhängigkeitsbaum keine Terminalbibliothek und die Forms-Tests können darin laufen
|
||||
|
||||
### Requirement: Bildschirmanweisungen des Dialekts
|
||||
`CLS`, `COLOR`, `LOCATE`, `WIDTH`, `VIEW PRINT` und die Anweisungsform von
|
||||
`SCREEN` SHALL auf dem Zellenpuffer wirken. `CLS` SHALL den Scrollbereich
|
||||
|
||||
@@ -124,6 +124,8 @@ ausführen. Der unterbrochene Zustand — Aufrufkette, Schleifen, Locals,
|
||||
Die Prüfung MUST NOT pro Opcode erfolgen; solange kein Trap definiert ist,
|
||||
MUST sie auf einen einzelnen Test hinauslaufen und MUST NOT die Uhr lesen.
|
||||
|
||||
Ohne klassische Traps, aktive Forms-Timer oder zeitabhängige Mausauswertung SHALL auch die Forms-Integration keine Hostzeit abfragen.
|
||||
|
||||
#### Scenario: Trap aus einer Prozedur heraus
|
||||
- **WHEN** ein Ereignis zugestellt wird, während eine `SUB` läuft, und das Trap-Ziel im Modulrumpf liegt
|
||||
- **THEN** läuft der Handler und nach `RETURN` setzt die `SUB` an der unterbrochenen Anweisung fort
|
||||
@@ -132,6 +134,10 @@ MUST sie auf einen einzelnen Test hinauslaufen und MUST NOT die Uhr lesen.
|
||||
- **WHEN** ein Programm ohne jede `ON …`-Trap-Definition ausgeführt wird
|
||||
- **THEN** fragt die Ausführung an keiner Anweisungsgrenze die Zeit ab, und der Durchsatz bleibt im Rahmen der Messgenauigkeit unverändert
|
||||
|
||||
#### Scenario: Einfaches Programm liest keine Ereigniszeit
|
||||
- **WHEN** ein Programm nur Zuweisungen und END ausführt und ein Testhost Zeitabfragen zählt
|
||||
- **THEN** ist der Zähler am Ende 0
|
||||
|
||||
### Requirement: Jede Schleife trägt eine Anweisungsgrenze im Kreis
|
||||
Der zurückspringende Zweig jeder Schleife (`FOR`/`NEXT`, `WHILE`/`WEND`,
|
||||
`DO`/`LOOP` in allen Bedingungsformen) MUST über mindestens eine
|
||||
|
||||
Reference in New Issue
Block a user