use std::collections::HashMap;
use noesis_runtime::element_tree::panel_children;
use noesis_runtime::plain_vm::{PlainType, PlainValue, PlainVmBuilder};
use noesis_runtime::view::{FrameworkElement, View};
use noesis_runtime::xaml_provider::XamlProvider;
const HOST_XAML: &str = r##"<?xml version="1.0" encoding="utf-8"?>
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="200" Height="200">
<StackPanel x:Name="Host"/>
</Grid>"##;
const LEAF_XAML: &str = r##"<?xml version="1.0" encoding="utf-8"?>
<Border xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="40">
<TextBlock x:Name="Leaf" Text="{Binding Title}"/>
</Border>"##;
struct InMem(HashMap<String, Vec<u8>>);
impl XamlProvider for InMem {
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn load_xaml(&mut self, uri: &str) -> Option<&[u8]> {
self.0.get(uri).map(Vec::as_slice)
}
}
#[test]
fn spike_mount_two_copies_isolated() {
if let (Ok(name), Ok(key)) = (
std::env::var("NOESIS_LICENSE_NAME"),
std::env::var("NOESIS_LICENSE_KEY"),
) {
noesis_runtime::set_license(&name, &key);
}
noesis_runtime::init();
{
let mut bytes = HashMap::new();
bytes.insert("host.xaml".to_string(), HOST_XAML.as_bytes().to_vec());
bytes.insert("leaf.xaml".to_string(), LEAF_XAML.as_bytes().to_vec());
let _guard = noesis_runtime::xaml_provider::set_xaml_provider(InMem(bytes));
let mut vm_builder = PlainVmBuilder::new("DmSpike.LeafVm");
let title = vm_builder.add_property("Title", PlainType::String);
let vm_class = vm_builder.register().expect("register LeafVm");
let vm_a = vm_class.create_instance().expect("vm A");
let vm_b = vm_class.create_instance().expect("vm B");
assert!(vm_a.set(title, PlainValue::String("AAA".into())));
assert!(vm_b.set(title, PlainValue::String("BBB".into())));
let host_root = FrameworkElement::load("host.xaml").expect("load host.xaml");
let mut view = View::create(host_root);
view.set_size(200, 200);
view.activate();
for i in 1..=4 {
view.update(f64::from(i) * 0.016);
}
let content = view.content().expect("view content");
let host = content.find_name("Host").expect("find Host panel");
let mut leaf_a = FrameworkElement::load("leaf.xaml").expect("load leaf A");
let mut leaf_b = FrameworkElement::load("leaf.xaml").expect("load leaf B");
assert!(vm_a.set_data_context(&mut leaf_a), "set DC on A");
assert!(vm_b.set_data_context(&mut leaf_b), "set DC on B");
let mut children = panel_children(&host).expect("Host is a Panel");
let ia = children.add(&leaf_a).expect("add A");
let ib = children.add(&leaf_b).expect("add B");
assert_eq!((ia, ib), (0, 1), "both fragments mounted into one panel");
for i in 5..=10 {
view.update(f64::from(i) * 0.016);
}
let leaf_a_tb = leaf_a.find_name("Leaf").expect("A/Leaf");
let leaf_b_tb = leaf_b.find_name("Leaf").expect("B/Leaf");
let a_initial = leaf_a_tb.text();
let b_initial = leaf_b_tb.text();
eprintln!("SPIKE-MOUNT: initial A={a_initial:?} B={b_initial:?}");
let distinct_render =
a_initial.as_deref() == Some("AAA") && b_initial.as_deref() == Some("BBB");
assert!(vm_a.set_and_notify(title, "Title", PlainValue::String("ZZZ".into())));
for i in 11..=14 {
view.update(f64::from(i) * 0.016);
}
let a_after = leaf_a_tb.text();
let b_after = leaf_b_tb.text();
eprintln!("SPIKE-MOUNT: after A.notify(ZZZ) A={a_after:?} B={b_after:?}");
let a_updated = a_after.as_deref() == Some("ZZZ");
let b_isolated = b_after.as_deref() == Some("BBB");
let mount_isolation_works = distinct_render && a_updated && b_isolated;
eprintln!(
"SPIKE-MOUNT: distinct_render={distinct_render} a_updated={a_updated} \
b_isolated={b_isolated}; mountIsolationWorks={mount_isolation_works}"
);
assert!(
mount_isolation_works,
"mount isolation failed: distinct={distinct_render} a_updated={a_updated} \
b_isolated={b_isolated}"
);
assert!(children.clear());
drop(children);
drop(leaf_a_tb);
drop(leaf_b_tb);
drop(host);
drop(leaf_a);
drop(leaf_b);
drop(content);
view.deactivate();
drop(view);
drop(vm_a);
drop(vm_b);
drop(vm_class);
}
noesis_runtime::shutdown();
}