//! 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 { 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) -> Option { 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 { None } fn jetzt_ms(&mut self) -> u64 { self.time } fn warten(&mut self, deadline: Option) -> Option { 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"); }