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.
|
||||
Reference in New Issue
Block a user