Skip to content

ui::testing::TestNativeHost

Header: winui/testing/TestNativeHost.hpp.

Derives from ui::NativeHost.

NativeHost test double: tracks one Entry per native child instead of creating native windows.

A NativeControl's lifecycle (create once, move on every changed arrange, hide/show, clip, destroy with the control) is plain state, and this host records it so a test can check it without a window. Hand it to Element::sync_native() or install it with Window::set_native_host(). The host must outlive every control synced against it, since a NativeControl calls destroy() from its destructor.

Example:

ui::NativeControlDesc desc;
desc.class_name = "EDIT";

ui::testing::TestNativeHost host;
{
    ui::NativeControl control{desc, ui::Size{120.0f, 24.0f}};
    control.arrange({10, 10, 120, 24});
    control.sync_native(host);

    const ui::testing::TestNativeHost::Entry* entry = host.find(control.visual_id());
    REQUIRE(entry != nullptr);
    REQUIRE(entry->create_count == 1);
    REQUIRE(entry->bounds == ui::Rect{10, 10, 120, 24});
    REQUIRE(entry->owner == &control);
}
REQUIRE(host.count() == 0); // destroyed along with the control

ensure_created

void ensure_created(VisualId id, const NativeControlDesc& desc) override

Creates the entry on the first call for id and stores desc; later calls are no-ops.

set_bounds

void set_bounds(VisualId id, const Rect& bounds) override

Records bounds and bumps Entry::move_count.

set_visible

void set_visible(VisualId id, bool visible) override

Records visible and bumps Entry::visible_count; ignored for an id with no entry.

set_clip

void set_clip(VisualId id, const std::optional<Rect>& visible) override

Records the visible region (nullopt: unclipped) and bumps Entry::clip_count.

destroy

void destroy(VisualId id) override

Erases the entry, so find() returns null and count() drops.

~TestNativeHost

~TestNativeHost() override

Tells every control still registered that its host is gone, as the real host does when its window is closed under a tree that lives on.

set_owner

void set_owner(VisualId id, NativeControl* owner) override

Records owner on the entry for id.

native_handle

NativeHandle native_handle(VisualId) const override

Always null: no real handle exists headlessly.

find

const Entry* find(VisualId id) const

The entry for id, or null if it was never touched or has been destroyed. The pointer is invalidated by the next call that adds or erases an entry.

count

std::size_t count() const

Number of live entries (native children created or touched and not yet destroyed).

struct Entry

Everything recorded about one native child, keyed by its VisualId.

Field Type Default
desc NativeControlDesc The description passed to the first, creating ensure_created() call.
bounds Rect Last bounds from set_bounds(), in window DIPs.
visible bool true Last value from set_visible(); a fresh entry starts visible.
created bool false True once ensure_created() has run for this id.
create_count int 0 How many times ensure_created() created the entry; repeat calls don't count.
move_count int 0 Number of set_bounds() calls; a NativeControl only moves when its bounds changed.
owner NativeControl* nullptr Control registered via set_owner(), which a real host uses to route the native child's events.
clip std::optional<Rect> set_clip()'s last visible region (nullopt: unclipped).
clip_count int 0 Number of set_clip() calls.
visible_count int 0 Number of set_visible() calls.