//! Unabhängige Review-Proben: zwei behobene Vertragslücken, Timer und Mindestmaße. //! Ausführung über den temporären Cargo-Treiber aus review.md. use tb_frontend::forms::{self, FormCatalog, ObjectClass}; use tb_runtime::{ host::{CaptureHost, Host}, screen::TextScreen, }; use tb_ui::forms::{FormsModel, PropertyValue as V}; use tb_vm::interp::{RunEvent, Vm}; fn model(class: ObjectClass) -> FormsModel { let mut c = FormCatalog::default(); c.add("Form1", ObjectClass::Form, None, false); c.add("List1", class, Some("Form1"), false); let mut m = FormsModel::new(c.objects, 80, 25); m.set_initial(0, prop(ObjectClass::Form, "WIDTH"), V::Integer(40)) .unwrap(); m.set_initial(0, prop(ObjectClass::Form, "HEIGHT"), V::Integer(12)) .unwrap(); m } fn prop(class: ObjectClass, name: &str) -> u16 { forms::property(class, name).unwrap().0 } #[test] fn sorted_nach_befuellen_ordnet_liste_und_erhaelt_auswahl() { let mut results = vec![]; for class in [ObjectClass::ListBox, ObjectClass::ComboBox] { let mut m = model(class); m.add_item((1, None), "b".into(), None).unwrap(); m.add_item((1, None), "a".into(), None).unwrap(); m.set(1, prop(class, "LISTINDEX"), V::Integer(0)).unwrap(); m.set(1, prop(class, "SORTED"), V::Boolean(true)).unwrap(); m.add_item((1, None), "c".into(), None).unwrap(); let list: Vec<_> = (0..3) .map(|i| m.get_indexed(1, prop(class, "LIST"), i).unwrap()) .collect(); println!( "{class:?}: {list:?}; Auswahl={:?}", m.get(1, prop(class, "LISTINDEX")).unwrap() ); results.push((list, m.get(1, prop(class, "LISTINDEX")).unwrap())); } for (list, index) in results { assert_eq!( list, vec![ V::String("a".into()), V::String("b".into()), V::String("c".into()) ] ); assert_eq!(index, V::Integer(1)); } } #[test] fn combo_simple_zeigt_seine_listeneintraege() { let class = ObjectClass::ComboBox; let mut m = model(class); for (name, value) in [ ("STYLE", V::Integer(1)), ("WIDTH", V::Integer(15)), ("HEIGHT", V::Integer(5)), ("LEFT", V::Integer(2)), ("TOP", V::Integer(2)), ("TEXT", V::String("frei".into())), ] { m.set_initial(1, prop(class, name), value).unwrap(); } m.add_item((1, None), "apfel".into(), None).unwrap(); m.add_item((1, None), "birne".into(), None).unwrap(); m.show(0, false).unwrap(); let mut screen = TextScreen::new(); m.render(&mut screen); let text = tb_runtime::snapshot::text(&screen); println!("{text}"); assert!( text.contains("frei"), "Kontrolle: das Control muss sichtbar sein" ); assert!(text.contains("apfel") && text.contains("birne")); } #[test] fn timer_array_wird_beim_laden_an_aktueller_hostzeit_aktiviert() { let mut c = FormCatalog::default(); c.add("Form1", ObjectClass::Form, None, false); c.add("Timer1", ObjectClass::Timer, Some("Form1"), true); let src="DIM SHARED s$\nForm1.Show\nTimer1(0).Interval=100\nSTOP\nLOAD Timer1(1)\nTimer1(0).Interval=0\nSTOP\nDOEVENTS\nEND\nSUB Timer1_Timer(Index AS INTEGER)\nSHARED s$\ns$=s$+LTRIM$(STR$(Index))\nEND SUB"; let mut vm = Vm::new(tb_vm::compile_source_with_forms("FORM1", src, &c).unwrap()); let mut h = CaptureHost::default(); assert!(matches!(vm.run(&mut h), RunEvent::Stopped { .. })); h.uhr_setzen(1000); assert!(matches!(vm.run(&mut h), RunEvent::Stopped { .. })); assert_eq!(vm.forms.next_deadline(), Some(1100)); h.uhr_setzen(1100); assert_eq!(vm.run(&mut h), RunEvent::Ended); assert_eq!(h.jetzt_ms(), 1100); } #[test] fn listen_bleiben_auch_bei_minimalmassen_im_controlrechteck() { let mut failures = vec![]; for (class, width, height) in [ (ObjectClass::ComboBox, 15, 5), (ObjectClass::ListBox, 6, 4), (ObjectClass::ComboBox, 1, 5), (ObjectClass::ListBox, 1, 4), (ObjectClass::ListBox, 6, 1), ] { let mut m = model(class); for (name, value) in [ ("WIDTH", width), ("HEIGHT", height), ("LEFT", 2), ("TOP", 2), ] { m.set_initial(1, prop(class, name), V::Integer(value)) .unwrap(); } if class == ObjectClass::ComboBox { m.set_initial(1, prop(class, "STYLE"), V::Integer(1)) .unwrap(); } m.add_item((1, None), "apfel".into(), None).unwrap(); m.set(1, prop(class, "VISIBLE"), V::Boolean(false)).unwrap(); m.show(0, false).unwrap(); let mut screen = TextScreen::new(); m.render(&mut screen); let before = tb_runtime::snapshot::text(&screen); m.set(1, prop(class, "VISIBLE"), V::Boolean(true)).unwrap(); m.render(&mut screen); let after = tb_runtime::snapshot::text(&screen); let mut outside = vec![]; for (row, (a, b)) in before.lines().zip(after.lines()).enumerate() { for (col, (a, b)) in a.chars().zip(b.chars()).enumerate() { if a != b && !(row >= 2 && row < 2 + height as usize && col >= 2 && col < 2 + width as usize) { outside.push((row + 1, col + 1, a, b)); } } } if !outside.is_empty() { failures.push(format!("{class:?} {width}x{height}: {outside:?}")); } } assert!(failures.is_empty(), "{}", failures.join("\n")); }