agent-desktop/crates/ffi/tests/cpp_header_compile.rs
Lahfir 3f322728b4
feat!: implement Playwright-grade foundation contract
Settle the Playwright-grade reliability contract in agent-desktop-core
before the Windows/Linux adapters are built, so they inherit it instead
of redesigning it. Every command now observes, waits, verifies, and
reports honestly instead of firing blindly.

Highlights: capability-supertrait split of PlatformAdapter with
not_supported() defaults; canonical role/state vocabulary with live
`is --property visible`; display enumeration (`list-displays`) and honest
`--screen` with scale factor; truthful Automation permission; `native_id`
identity spine; window-id-first resolution; serializable `LocatorQuery`
with live `find`; default-on auto-wait before every ref action; three-way
`hit_test` occlusion gate; `scroll_into_view` in core; core accessible-name
precedence; typed `ActionStep` delivery tier; `ProcessState` and
`APP_UNRESPONSIVE`; `LaunchOptions`; baseline-diff desktop signals
(`wait --event`); typed clipboard (`Text`/`Image`/`FileUrls`); mouse
modifier chords and `mouse-wheel`. Hardened through a 35-reviewer pass with
independent validation and a green live e2e gate (109/0), plus a
head-vs-main performance comparison harness.

BREAKING CHANGE: default-on auto-wait changes the timing of every
previously-untouched ref-action call (bounded 5000 ms default; `--timeout-ms 0`
restores single-shot). `ENVELOPE_VERSION` is now `2.1` (adds the
`APP_UNRESPONSIVE` code and process state in error details). FFI ABI major
is `3` (append-only struct evolution; `wait --event` is intentionally not
exposed over FFI). The legacy string clipboard API is removed in favor of
typed content. `key-down`/`key-up` fail closed until daemon-owned held input
exists. `close-app` verifies termination and the osascript fallback path is
removed. `--text` matching is subtree containment: `find --text X --first`
returns the outermost matching container.
2026-07-20 00:21:38 -07:00

146 lines
5.6 KiB
Rust

//! Verifies the committed C header is also a valid C++17 interface.
use std::path::PathBuf;
use std::process::Command;
fn system_cxx() -> Option<&'static str> {
["c++", "clang++", "g++"].into_iter().find(|compiler| {
Command::new(compiler)
.arg("--version")
.output()
.is_ok_and(|output| output.status.success())
})
}
fn header_include_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("include")
}
#[test]
fn committed_header_compiles_as_cpp17_with_portable_abi_types() {
let Some(cxx) = system_cxx() else {
eprintln!("skipping: system C++ compiler not found");
return;
};
let stem = format!("agent_desktop_header_cpp17_{}", std::process::id());
let tmp = std::env::temp_dir().join(format!("{stem}.cpp"));
let obj = std::env::temp_dir().join(format!("{stem}.o"));
let src = r#"
#include <cstdint>
#include <limits>
#include <type_traits>
#include "agent_desktop.h"
static void log_callback(std::int32_t level, const char *message) {
(void)level;
(void)message;
}
static_assert(std::is_enum_v<AdModifier>);
static_assert(std::is_same_v<std::underlying_type_t<AdModifier>, std::int32_t>);
static_assert(AD_MODIFIER_META == 0);
static_assert(AD_MODIFIER_CTRL == 1);
static_assert(AD_MODIFIER_ALT == 2);
static_assert(AD_MODIFIER_SHIFT == 3);
static_assert(AD_MODIFIER_CMD == AD_MODIFIER_META);
static_assert(std::is_enum_v<AdSnapshotSurface>);
static_assert(std::is_same_v<std::underlying_type_t<AdSnapshotSurface>, std::int32_t>);
static_assert(AD_SNAPSHOT_SURFACE_WINDOW == 0);
static_assert(AD_SNAPSHOT_SURFACE_FOCUSED == 1);
static_assert(AD_SNAPSHOT_SURFACE_MENU == 2);
static_assert(AD_SNAPSHOT_SURFACE_MENUBAR == 3);
static_assert(AD_SNAPSHOT_SURFACE_SHEET == 4);
static_assert(AD_SNAPSHOT_SURFACE_POPOVER == 5);
static_assert(AD_SNAPSHOT_SURFACE_ALERT == 6);
static_assert(AD_SNAPSHOT_SURFACE_DESKTOP == 7);
static_assert(AD_SNAPSHOT_SURFACE_TASKBAR == 8);
static_assert(AD_SNAPSHOT_SURFACE_SYSTEM_TRAY == 9);
static_assert(AD_SNAPSHOT_SURFACE_QUICK_SETTINGS == 10);
static_assert(AD_SNAPSHOT_SURFACE_NOTIFICATION_CENTER == 11);
static_assert(AD_SNAPSHOT_SURFACE_TOOLBAR == 12);
static_assert(AD_SNAPSHOT_SURFACE_DOCK == 13);
static_assert(AD_SNAPSHOT_SURFACE_SPOTLIGHT == 14);
static_assert(AD_SNAPSHOT_SURFACE_MENU_BAR_EXTRAS == 15);
static_assert(AD_SNAPSHOT_SURFACE_SYSTEM_TRAY_OVERFLOW == 16);
static_assert(AD_SNAPSHOT_SURFACE_START_MENU == 17);
static_assert(AD_SNAPSHOT_SURFACE_ACTION_CENTER == 18);
static_assert(AD_DELIVERY_DISPOSITION_UNKNOWN == 0);
static_assert(AD_DELIVERY_DISPOSITION_NOT_DELIVERED == 1);
static_assert(AD_DELIVERY_DISPOSITION_DELIVERY_UNCERTAIN == 2);
static_assert(AD_DELIVERY_DISPOSITION_DELIVERED_UNVERIFIED == 3);
static_assert(AD_DELIVERY_DISPOSITION_DELIVERED_VERIFIED == 4);
static_assert(AD_FIND_SELECTION_KIND_STRICT == 0);
static_assert(AD_FIND_SELECTION_KIND_FIRST == 1);
static_assert(AD_FIND_SELECTION_KIND_LAST == 2);
static_assert(AD_FIND_SELECTION_KIND_NTH == 3);
static_assert(AD_IDENTIFIER_KIND_AX_IDENTIFIER == 0);
static_assert(AD_IDENTIFIER_KIND_AX_DOM_IDENTIFIER == 1);
static_assert(AD_IDENTIFIER_KIND_AUTOMATION_ID == 2);
static_assert(AD_IDENTIFIER_KIND_RUNTIME_ID == 3);
static_assert(AD_IDENTIFIER_KIND_ATSPI_OBJECT_PATH == 4);
static_assert(AD_POLICY_KIND_HEADLESS == 0);
static_assert(AD_POLICY_KIND_FOCUS_FALLBACK == 1);
static_assert(AD_POLICY_KIND_HEADED == 2);
static_assert(AD_RETRY_DISPOSITION_UNKNOWN == 0);
static_assert(AD_RETRY_DISPOSITION_SAFE == 1);
static_assert(AD_RETRY_DISPOSITION_UNSAFE == 2);
static_assert(AD_STEP_MECHANISM_SEMANTIC_API == 1);
static_assert(AD_STEP_MECHANISM_PHYSICAL_SYNTHETIC == 2);
using AppPid = decltype(AdAppInfo{}.pid);
using WindowPid = decltype(AdWindowInfo{}.pid);
using RefPid = decltype(AdRefProcess{}.pid);
using ScreenshotPid = decltype(AdScreenshotTarget{}.pid);
static_assert(std::is_same_v<AppPid, std::uint32_t>);
static_assert(std::is_same_v<WindowPid, std::uint32_t>);
static_assert(std::is_same_v<RefPid, std::uint32_t>);
static_assert(std::is_same_v<ScreenshotPid, std::uint32_t>);
static_assert(std::numeric_limits<AppPid>::max() == UINT32_MAX);
using ListSurfacesFn = AdResult (*)(const AdAdapter *, std::uint32_t, AdSurfaceList **);
using ListExactSurfacesFn = AdResult (*)(const AdAdapter *, std::uint32_t, AdExactSurfaceList **);
using ListDisplaysFn = AdResult (*)(const AdAdapter *, AdDisplayList **);
static_assert(std::is_same_v<decltype(&ad_list_surfaces), ListSurfacesFn>);
static_assert(std::is_same_v<decltype(&ad_list_surfaces_exact), ListExactSurfacesFn>);
static_assert(std::is_same_v<decltype(&ad_list_displays), ListDisplaysFn>);
static_assert(sizeof(AdDisplayInfo) == AD_DISPLAY_INFO_SIZE);
static_assert(offsetof(AdDisplayInfo, id) == 8);
static_assert(offsetof(AdDisplayInfo, scale) == 56);
int main() {
const auto callback_result = ad_set_log_callback(log_callback);
const auto clear_callback_result = ad_set_log_callback(nullptr);
(void)callback_result;
(void)clear_callback_result;
return 0;
}
"#;
std::fs::write(&tmp, src).expect("write C++17 test translation unit");
let output = Command::new(cxx)
.arg("-std=c++17")
.arg("-pedantic-errors")
.arg("-Wall")
.arg("-Wextra")
.arg("-Werror")
.arg("-I")
.arg(header_include_dir())
.arg("-c")
.arg(&tmp)
.arg("-o")
.arg(&obj)
.output()
.expect("C++ compiler invocation failed");
let _ = std::fs::remove_file(&tmp);
let _ = std::fs::remove_file(&obj);
assert!(
output.status.success(),
"C++17 compile of agent_desktop.h failed:\n{}",
String::from_utf8_lossy(&output.stderr)
);
}