use crate::platform::harmony::HarmonyPlatform;
use crate::platform::Platform;
use crate::WidgetTriggerKind;
use alloc::string::ToString;
#[cfg(feature = "xcomponent")]
#[test]
fn the_sdk_structs_keep_their_declared_layout() {
use core::mem::{align_of, size_of};
macro_rules! off {
($ty:ty, $field:ident) => {{
let value = core::mem::MaybeUninit::<$ty>::zeroed();
let base = value.as_ptr() as usize;
#[allow(unused_unsafe)]
let field = unsafe { &(*value.as_ptr()).$field } as *const _ as usize;
field - base
}};
}
assert_eq!(size_of::<crate::platform::harmony::xcomponent::TouchPoint>(), 56);
assert_eq!(align_of::<crate::platform::harmony::xcomponent::TouchPoint>(), 8);
assert_eq!(off!(crate::platform::harmony::xcomponent::TouchPoint, id), 0);
assert_eq!(off!(crate::platform::harmony::xcomponent::TouchPoint, screen_x), 4);
assert_eq!(off!(crate::platform::harmony::xcomponent::TouchPoint, screen_y), 8);
assert_eq!(off!(crate::platform::harmony::xcomponent::TouchPoint, x), 12);
assert_eq!(off!(crate::platform::harmony::xcomponent::TouchPoint, y), 16);
assert_eq!(off!(crate::platform::harmony::xcomponent::TouchPoint, size), 24);
assert_eq!(off!(crate::platform::harmony::xcomponent::TouchPoint, force), 32);
assert_eq!(off!(crate::platform::harmony::xcomponent::TouchPoint, time_stamp), 40);
assert_eq!(off!(crate::platform::harmony::xcomponent::TouchPoint, is_pressed), 48);
assert_eq!(off!(crate::platform::harmony::xcomponent::MouseEvent, x), 0);
assert_eq!(off!(crate::platform::harmony::xcomponent::MouseEvent, y), 4);
assert_eq!(off!(crate::platform::harmony::xcomponent::MouseEvent, screen_x), 8);
assert_eq!(off!(crate::platform::harmony::xcomponent::MouseEvent, screen_y), 12);
assert_eq!(off!(crate::platform::harmony::xcomponent::MouseEvent, time_stamp), 16);
assert_eq!(off!(crate::platform::harmony::xcomponent::MouseEvent, action), 24);
assert_eq!(off!(crate::platform::harmony::xcomponent::MouseEvent, button), 28);
assert_eq!(off!(crate::platform::harmony::xcomponent::TouchEvent, id), 0);
assert_eq!(off!(crate::platform::harmony::xcomponent::TouchEvent, event_type), 20);
assert_eq!(off!(crate::platform::harmony::xcomponent::TouchEvent, size), 24);
assert_eq!(off!(crate::platform::harmony::xcomponent::TouchEvent, force), 32);
assert_eq!(off!(crate::platform::harmony::xcomponent::TouchEvent, device_id), 40);
assert_eq!(off!(crate::platform::harmony::xcomponent::TouchEvent, time_stamp), 48);
}
#[test]
fn platform_creates_and_runs() {
let backend = HarmonyPlatform::new();
backend.init();
assert_eq!(backend.backend_name(), "harmony-state-backend");
let window = backend.create_window("TestWindow", 100, 200, 640, 480);
assert!(window > 0, "Window should be created");
assert_eq!(backend.get_widget_text(window), "TestWindow");
}
#[test]
fn widget_lifecycle() {
let backend = HarmonyPlatform::new();
backend.init();
let window = backend.create_window("w", 0, 0, 200, 120);
assert!(window > 0, "Window should be created");
backend.show_widget(window);
assert!(backend.is_widget_visible(window), "Widget should be visible after show");
backend.hide_widget(window);
assert!(!backend.is_widget_visible(window), "Widget should be hidden after hide");
backend.show_widget(window);
assert!(backend.is_widget_visible(window), "Widget should be visible after second show");
backend.set_widget_enabled(window, false);
assert!(!backend.is_widget_enabled(window), "Widget should be disabled");
backend.set_widget_enabled(window, true);
assert!(backend.is_widget_enabled(window), "Widget should be enabled");
backend.set_widget_text(window, "updated");
assert_eq!(backend.get_widget_text(window), "updated", "Widget text should update");
backend.set_widget_geometry(window, 20, 20, 100, 30);
assert!(backend.is_widget_ime_enabled(window), "IME should be enabled by default");
assert!(backend.set_widget_ime_enabled(window, false), "set_widget_ime_enabled should succeed");
assert!(
!backend.is_widget_ime_enabled(window),
"IME should be disabled after set_widget_ime_enabled(false)"
);
}
#[test]
fn clipboard_roundtrip() {
let backend = HarmonyPlatform::new();
backend.init();
assert_eq!(backend.get_clipboard_text(), "", "Clipboard should be empty initially");
assert!(backend.set_clipboard_text("hello harmony"), "Should set clipboard text");
assert_eq!(backend.get_clipboard_text(), "hello harmony", "Clipboard text should match");
assert!(backend.set_clipboard_text("updated clipboard"), "Should overwrite clipboard text");
assert_eq!(
backend.get_clipboard_text(),
"updated clipboard",
"Clipboard text should reflect update"
);
assert!(backend.set_clipboard_text(""), "Should set empty clipboard text");
assert_eq!(
backend.get_clipboard_text(),
"",
"Clipboard should be empty after setting empty string"
);
}
#[test]
fn widget_trigger_events() {
let backend = HarmonyPlatform::new();
backend.init();
let window = backend.create_window("w", 0, 0, 200, 120);
assert!(backend.poll_widget_trigger_event().is_none());
assert!(backend.inject_widget_trigger_event(window, WidgetTriggerKind::Clicked));
let event = backend.poll_widget_trigger_event();
assert!(event.is_some(), "Should poll a trigger event");
let event = event.unwrap();
assert_eq!(event.widget_id, window);
assert_eq!(event.kind, WidgetTriggerKind::Clicked);
assert!(backend.poll_widget_trigger_event().is_none());
assert!(backend.inject_widget_trigger_event(window, WidgetTriggerKind::ValueChanged));
let event = backend.poll_widget_trigger_event();
assert!(event.is_some(), "Should poll value-changed event");
let event = event.unwrap();
assert_eq!(event.widget_id, window);
assert_eq!(event.kind, WidgetTriggerKind::ValueChanged);
}
#[test]
fn the_two_trigger_views_share_one_queue() {
let backend = HarmonyPlatform::new();
backend.init();
let window = backend.create_window("w", 0, 0, 200, 120);
assert!(backend.poll_widget_triggered().is_none(), "the queue starts empty");
assert!(backend.inject_widget_trigger_event(window, WidgetTriggerKind::Clicked));
assert_eq!(
backend.poll_widget_triggered(),
Some(window),
"the id view must see what was injected"
);
assert!(
backend.poll_widget_trigger_event().is_none(),
"and the typed view must agree it is gone: one queue, not two"
);
assert!(backend.inject_widget_trigger_event(window, WidgetTriggerKind::ValueChanged));
let event = backend.poll_widget_trigger_event().expect("the typed view sees it");
assert_eq!(event.kind, WidgetTriggerKind::ValueChanged);
assert_eq!(event.widget_id, window);
assert!(backend.poll_widget_triggered().is_none(), "and the id view agrees it is gone");
}
#[test]
fn drag_and_drop() {
let backend = HarmonyPlatform::new();
backend.init();
let window = backend.create_window("w", 0, 0, 200, 120);
assert!(
backend.begin_drag(window, "text/plain", b"payload"),
"begin_drag should succeed for a valid widget"
);
assert!(
backend.poll_drop_event().is_some(),
"poll_drop_event should return Some after begin_drag"
);
assert!(
backend.inject_drop_event(crate::platform::DropEvent {
source_widget_id: window,
target_widget_id: window,
mime: "text/plain".into(),
payload: vec![]
}),
"inject_drop_event should succeed for valid target widget"
);
}
#[test]
fn destroy_widget_reports_existence() {
let backend = HarmonyPlatform::new();
backend.init();
let window = backend.create_window("w", 0, 0, 200, 120);
assert!(backend.destroy_widget(window));
assert!(!backend.destroy_widget(window));
assert!(!backend.destroy_widget(4242));
}
#[test]
fn capabilities_are_explicit_and_honest() {
let backend = HarmonyPlatform::new();
let caps = backend.capabilities();
assert_eq!(backend.family(), crate::core::PlatformFamily::Desktop);
assert!(
!caps.dpi_scaling,
"no `dpi_scale_factor` override exists, so a `true` would promise a method that \
cannot answer"
);
assert!(!caps.ime, "no `ime_bridge` override exists; key input is not an IME client");
assert_eq!(
caps.accessibility,
cfg!(all(feature = "xcomponent", not(alloc_frugal))),
"`accessibility` must track whether `accessibility_bridge()` is compiled in, which is \
exactly when the XComponent bridge is"
);
assert!(!caps.native_menu, "the Harmony menu is an in-process tree, not an OS menu");
assert!(caps.typed_widget_trigger, "typed trigger events are supported");
}
#[test]
fn the_accessibility_flag_matches_its_bridge() {
let backend = HarmonyPlatform::new();
let claimed = backend.capabilities().accessibility;
let answers = backend.accessibility_bridge().is_some();
assert_eq!(
claimed, answers,
"`accessibility: {claimed}` but `accessibility_bridge()` answers \
{answers}; the two are one statement about one fact and must not drift"
);
if answers {
let bridge = backend.accessibility_bridge().expect("just asserted");
bridge.set_accessibility_name(42, "Play");
assert_eq!(bridge.accessibility_name(42), Some("Play".to_string()));
}
}
#[test]
fn mounting_a_surface_also_informs_the_xcomponent_bridge() {
let backend = HarmonyPlatform::new();
let window = backend.create_window("w", 0, 0, 640, 480);
let rect = crate::core::Rect::new(0, 0, 100, 40);
assert!(
backend.mount_surface(window, window, rect),
"the presentation half must record the mount even with no XComponent bound"
);
assert_eq!(backend.state.surface_rect(window), Some(rect));
#[cfg(all(feature = "xcomponent", not(alloc_frugal)))]
{
assert_eq!(
crate::platform::harmony::xcomponent::mounted_widget(),
None,
"an unbound bridge must not record a mounted widget"
);
}
}
#[test]
fn widget_surfaces_are_advertised_and_round_trip() {
let backend = HarmonyPlatform::new();
assert!(
backend.supports_surfaces(),
"the backend records surfaces and queues repaints, so it can host widgets"
);
let window = backend.create_window("w", 0, 0, 640, 480);
let rect = crate::core::Rect::new(0, 0, 100, 40);
assert!(backend.mount_surface(window, window, rect));
assert_eq!(backend.state.surface_rect(window), Some(rect));
assert_eq!(backend.state.mounted_surface_count(), 1);
assert!(backend.invalidate_surface(window));
assert!(backend.invalidate_surface(window), "a second invalidate still reports the mount");
assert_eq!(backend.state.pending_repaint_count(), 1, "repaints are coalesced");
assert_eq!(Platform::take_pending_repaint(&backend), Some(window));
assert_eq!(backend.state.pending_repaint_count(), 0);
let moved = crate::core::Rect::new(10, 10, 200, 80);
assert!(backend.resize_surface(window, moved));
assert_eq!(backend.state.surface_rect(window), Some(moved));
assert!(backend.invalidate_surface(window));
assert!(backend.unmount_surface(window));
assert_eq!(backend.state.surface_rect(window), None);
assert_eq!(backend.state.pending_repaint_count(), 0, "a gone widget must not be repainted");
assert!(!backend.unmount_surface(window), "unmounting twice must report no-op");
}
#[test]
fn mounting_a_surface_for_an_unknown_widget_is_refused() {
let backend = HarmonyPlatform::new();
assert!(!backend.mount_surface(1, 9_999, crate::core::Rect::new(0, 0, 10, 10)));
assert!(!backend.invalidate_surface(9_999));
assert!(!backend.resize_surface(9_999, crate::core::Rect::new(0, 0, 10, 10)));
assert_eq!(backend.state.mounted_surface_count(), 0);
}