use std::collections::BTreeMap;
use std::sync::Mutex;
use std::time::Duration;
use smix_adapter_maestro::AppLike;
use smix_error::ExpectationFailure;
use smix_input::{KeyName, SwipeDirection};
use smix_selector::Selector;
const ONE_PIXEL_PNG: &[u8] = &[
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53,
0xde, 0x00, 0x00, 0x00, 0x0c, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0x60, 0x60, 0x60, 0x00,
0x00, 0x00, 0x04, 0x00, 0x01, 0xf6, 0x17, 0x38, 0x55, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e,
0x44, 0xae, 0x42, 0x60, 0x82,
];
#[derive(Clone, Debug)]
pub enum MockCall {
TapWithMode(Selector, smix_sdk::TapMode),
TapXcui(String),
Tap(Selector),
DoubleTap(Selector),
LongPress(Selector),
Launch(String),
}
#[derive(Default)]
pub struct MockApp {
pub calls: Mutex<Vec<MockCall>>,
}
impl MockCall {
fn describe(&self) -> String {
match self {
MockCall::TapWithMode(s, mode) => format!(
"tapOn {} (dispatch: {})",
smix_sdk::describe_selector(s),
match mode {
smix_sdk::TapMode::Resolve => "resolve",
smix_sdk::TapMode::ResolveAndTap => "xcui",
smix_sdk::TapMode::DaemonProxySynthesize => "daemonProxy",
}
),
MockCall::TapXcui(id) => format!("tap-by-id {id}"),
MockCall::Tap(s) => format!("tapOn {} (host-resolved)", smix_sdk::describe_selector(s)),
MockCall::DoubleTap(s) => format!("doubleTapOn {}", smix_sdk::describe_selector(s)),
MockCall::LongPress(s) => format!("longPressOn {}", smix_sdk::describe_selector(s)),
MockCall::Launch(what) => format!("launch {what}"),
}
}
}
impl MockApp {
fn record(&self, call: MockCall) {
self.calls.lock().expect("mock lock").push(call);
}
}
#[async_trait::async_trait]
impl AppLike for MockApp {
async fn tap(&self, selector: &Selector) -> Result<(), ExpectationFailure> {
self.record(MockCall::Tap(selector.clone()));
Ok(())
}
async fn tap_xcui(&self, id: &str) -> Result<(), ExpectationFailure> {
self.record(MockCall::TapXcui(id.to_string()));
Ok(())
}
async fn tap_with_mode(
&self,
selector: &Selector,
mode: smix_sdk::TapMode,
) -> Result<(), ExpectationFailure> {
self.record(MockCall::TapWithMode(selector.clone(), mode));
Ok(())
}
async fn tap_burst(
&self,
_selector: &Selector,
_times: u32,
_interval_ms: Option<u32>,
_hold_ms: Option<u32>,
) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn double_tap(&self, selector: &Selector) -> Result<(), ExpectationFailure> {
self.record(MockCall::DoubleTap(selector.clone()));
Ok(())
}
async fn long_press(
&self,
selector: &Selector,
_duration: Duration,
) -> Result<(), ExpectationFailure> {
self.record(MockCall::LongPress(selector.clone()));
Ok(())
}
async fn long_press_capturing(
&self,
selector: &Selector,
duration: Duration,
) -> Result<smix_sdk::PressCapture, ExpectationFailure> {
self.record(MockCall::LongPress(selector.clone()));
Ok(smix_sdk::PressCapture {
timing: smix_driver::PressTiming {
sent_ms: 1_000,
received_ms: 1_400 + duration.as_millis() as u64,
latest_down_offset_ms: 300,
earliest_up_offset_ms: 300 + duration.as_millis() as u64,
handler_wall_ms: 400 + duration.as_millis() as u64,
},
frames: vec![smix_sdk::PressFrame {
span: smix_driver::CaptureSpan {
start_ms: 1_400,
end_ms: 1_630,
},
placement: smix_driver::FramePlacement::DuringPress,
png: b"\x89PNG\r\n\x1a\n".to_vec(),
}],
})
}
async fn clear_user_defaults(
&self,
_bundle_id: &str,
_keys: &[String],
) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn tap_at_coord(&self, _nx: f64, _ny: f64) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn find_by_text_ocr(
&self,
_text: &str,
_locales: &[String],
) -> Result<Option<smix_sdk::OcrFrame>, ExpectationFailure> {
Ok(Some(smix_sdk::OcrFrame {
nx: 0.5,
ny: 0.5,
w: 0.1,
h: 0.05,
}))
}
async fn find_norm_coord(
&self,
_selector: &Selector,
) -> Result<Option<(f64, f64)>, ExpectationFailure> {
Ok(Some((0.5, 0.5)))
}
async fn webview_eval(&self, _js: &str) -> Result<serde_json::Value, ExpectationFailure> {
Ok(serde_json::Value::Null)
}
async fn fill(&self, _selector: &Selector, _text: &str) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn press_key(&self, _key: KeyName) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn scroll(
&self,
_selector: &Selector,
_direction: SwipeDirection,
) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn wait_for(
&self,
_selector: &Selector,
_timeout: Duration,
) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn wait_for_not_visible(
&self,
_selector: &Selector,
_timeout: Duration,
) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn assert_visible(&self, _selector: &Selector) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn assert_not_visible(&self, _selector: &Selector) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn find(&self, _selector: &Selector) -> Result<bool, ExpectationFailure> {
Ok(true)
}
async fn launch(&self, bundle_id: &str) -> Result<(), ExpectationFailure> {
self.record(MockCall::Launch(bundle_id.to_string()));
Ok(())
}
async fn terminate(&self, _bundle_id: &str) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn launch_fresh(
&self,
bundle_id: &str,
_clear_state: bool,
_clear_keychain: bool,
app_path: Option<&str>,
launch_arguments: &[String],
) -> Result<Vec<String>, ExpectationFailure> {
self.record(MockCall::Launch(format!(
"{bundle_id} {} {}",
app_path.unwrap_or(""),
launch_arguments.join(" ")
)));
Ok(vec![])
}
async fn open_url(&self, _url: &str) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn system_popups(&self) -> Result<Vec<smix_sdk::SystemPopup>, ExpectationFailure> {
Ok(vec![])
}
async fn system_popup_action(
&self,
_popup_id: &str,
_button_id: &str,
) -> Result<bool, ExpectationFailure> {
Ok(true)
}
async fn foreground(&self, _bundle_id: &str) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn launch_app_with_options(
&self,
opts: &smix_sdk::LaunchAppOptions,
) -> Result<Vec<String>, ExpectationFailure> {
self.record(MockCall::Launch(format!("{opts:?}")));
Ok(vec![])
}
async fn swipe_at_coord(
&self,
_from: (f64, f64),
_to: (f64, f64),
) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn scroll_screen(&self, _direction: SwipeDirection) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn hide_keyboard(&self) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn screenshot(&self) -> Result<Vec<u8>, ExpectationFailure> {
Ok(ONE_PIXEL_PNG.to_vec())
}
async fn set_clipboard(&self, _text: &str) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn get_clipboard(&self) -> Result<String, ExpectationFailure> {
Ok(String::new())
}
async fn paste_text(&self, _text: Option<&str>) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn copy_text_from(&self, _selector: &Selector) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn go_back(&self) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn set_location(
&self,
_latitude: f64,
_longitude: f64,
) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn travel(
&self,
_points: &[(f64, f64)],
_speed_mps: Option<f64>,
) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn set_permissions(
&self,
_bundle_id: &str,
_permissions: &[(smix_sdk::SimctlPermission, smix_sdk::PermissionAction)],
) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn add_media(&self, _paths: &[String]) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn set_orientation(
&self,
_orientation: smix_sdk::MaestroOrientation,
) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn start_recording(&self, _path: &str) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn stop_recording(&self) -> Result<(), ExpectationFailure> {
Ok(())
}
async fn assert_screenshot(
&self,
_baseline_path: &std::path::Path,
_max_hamming: u32,
) -> Result<smix_sdk::AssertScreenshotOutcome, ExpectationFailure> {
Ok(smix_sdk::AssertScreenshotOutcome::Recorded {
path: std::path::PathBuf::new(),
})
}
}
fn guide_pages() -> BTreeMap<&'static str, &'static str> {
BTreeMap::from([
(
"02-yaml-reference",
include_str!("../../../docs/ai-guide/02-yaml-reference.md"),
),
(
"03-selectors",
include_str!("../../../docs/ai-guide/03-selectors.md"),
),
(
"04-actions",
include_str!("../../../docs/ai-guide/04-actions.md"),
),
("05-cli", include_str!("../../../docs/ai-guide/05-cli.md")),
(
"06-fixtures",
include_str!("../../../docs/ai-guide/06-fixtures.md"),
),
(
"07-errors",
include_str!("../../../docs/ai-guide/07-errors.md"),
),
(
"08-cookbook",
include_str!("../../../docs/ai-guide/08-cookbook.md"),
),
(
"10-ai-assertions",
include_str!("../../../docs/ai-guide/10-ai-assertions.md"),
),
(
"12-authoring",
include_str!("../../../docs/ai-guide/12-authoring.md"),
),
])
}
fn yaml_blocks(doc: &str) -> Vec<String> {
let mut out = Vec::new();
let mut in_block = false;
let mut cur = String::new();
for line in doc.lines() {
if in_block {
if line.trim_end() == "```" {
out.push(std::mem::take(&mut cur));
in_block = false;
} else {
cur.push_str(line);
cur.push('\n');
}
} else if line.trim_end() == "```yaml" {
in_block = true;
}
}
out
}
fn as_flow(block: &str) -> String {
let is_separator = |l: &str| {
l.strip_prefix("---")
.is_some_and(|rest| rest.trim_start().is_empty() || rest.trim_start().starts_with('#'))
};
if block.lines().any(is_separator) {
block.to_string()
} else {
format!("appId: com.example.app\n---\n{block}")
}
}
fn looks_like_a_flow(block: &str) -> bool {
if block.lines().any(|l| l.trim_start().starts_with("cmd:")) {
return false;
}
if block.lines().any(|l| l.trim_start().starts_with("host:")) {
return false;
}
block.contains("- ") || block.contains("appId:")
}
fn guide_registry() -> smix_fixture::FixtureRegistry {
let doc = include_str!("../../../docs/ai-guide/06-fixtures.md");
let mut block = None;
let mut cur: Option<String> = None;
for line in doc.lines() {
match &mut cur {
Some(buf) if line.trim_end() == "```" => {
block = Some(std::mem::take(buf));
cur = None;
}
Some(buf) => {
buf.push_str(line);
buf.push('\n');
}
None if line.trim_end() == "```jsonc" => cur = Some(String::new()),
None => {}
}
}
let block = block.expect("06-fixtures no longer prints a jsonc registry block");
static SEQ: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let n = SEQ.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let path = std::env::temp_dir().join(format!(
"smix-guide-gate-fixtures-{}-{n}.json",
std::process::id()
));
std::fs::write(&path, block).expect("write registry");
smix_fixture::FixtureRegistry::load(&path).expect("the registry printed in 06-fixtures loads")
}
fn guide_metro_tail() -> smix_metro_log::MetroLogTail {
let tail = smix_metro_log::MetroLogTail::new();
tail.push(
smix_metro_log::LogLevel::Info,
"[qa] search-history primed count=5".to_string(),
);
tail
}
#[derive(Debug)]
enum Verdict {
Reached(Vec<MockCall>),
Refused(String),
}
fn run_example(page: &str, block: &str) -> Verdict {
let flow = {
smix_adapter_maestro::set_ai_assertions_override(Some(page == "10-ai-assertions"));
let parsed = smix_adapter_maestro::parse_flow_yaml(&as_flow(block));
smix_adapter_maestro::set_ai_assertions_override(None);
match parsed {
Ok(f) => f,
Err(e) => return Verdict::Refused(format!("parse: {e}")),
}
};
let app = MockApp::default();
let base =
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/guide-corpus/flows");
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("runtime");
let outcome = rt.block_on(async {
let mut adapter = smix_adapter_maestro::Adapter::new(&app, base)
.with_fixture_registry(guide_registry())
.with_metro_tail(guide_metro_tail());
adapter.run(&flow).await
});
remove_artifacts(&flow);
match outcome {
Ok(_) => Verdict::Reached(app.calls.lock().expect("mock lock").clone()),
Err(e) => Verdict::Refused(format!("run: {e}")),
}
}
fn remove_artifacts(flow: &smix_adapter_maestro::Flow) {
for step in &flow.steps {
let path = match step {
smix_adapter_maestro::Step::TakeScreenshot { path: Some(p), .. } => p,
smix_adapter_maestro::Step::StartRecording { path } => path,
_ => continue,
};
let _ = std::fs::remove_file(path);
if !std::path::Path::new(path)
.extension()
.is_some_and(|e| e.eq_ignore_ascii_case("png"))
{
let _ = std::fs::remove_file(format!("{path}.png"));
}
}
}
const KNOWN_BROKEN: &[(&str, usize, &str)] = &[
(
"02-yaml-reference",
11,
"repeat.while.visible loops until the element leaves the screen; \
a mock that always says visible never exits, and one that said \
otherwise would be inventing a screen.",
),
(
"10-ai-assertions",
1,
"assertCondition asks a vision model about the screen. The judge \
runs; it is looking at a 1x1 pixel.",
),
(
"10-ai-assertions",
2,
"extractWithAI reads fields off the screen, then asserts on what \
it read. Same 1x1 pixel.",
),
];
fn known_broken(page: &str, block: usize) -> Option<&'static str> {
KNOWN_BROKEN
.iter()
.find(|(p, b, _)| *p == page && *b == block)
.map(|(_, _, why)| *why)
}
#[test]
fn the_unjudged_list_stays_short() {
assert!(
KNOWN_BROKEN.len() <= 4,
"{} examples the gate does not judge — at some point the list \
is the finding",
KNOWN_BROKEN.len()
);
}
#[test]
fn every_yaml_example_reaches_a_route() {
let mut judged = 0usize;
let mut broken: Vec<String> = Vec::new();
let mut fixed: Vec<String> = Vec::new();
for (page, doc) in guide_pages() {
for (i, block) in yaml_blocks(doc).iter().enumerate() {
if !looks_like_a_flow(block) {
continue;
}
judged += 1;
let listed = known_broken(page, i + 1);
match (run_example(page, block), listed) {
(Verdict::Refused(why), None) => {
broken.push(format!("{page} block #{}: {why}\n{block}", i + 1))
}
(Verdict::Reached(_), Some(why)) => fixed.push(format!(
"{page} block #{} now reaches a route — drop its \
KNOWN_BROKEN entry:\n {why}",
i + 1
)),
_ => {}
}
}
}
assert!(
fixed.is_empty(),
"{} listed-as-broken examples work now:\n\n{}",
fixed.len(),
fixed.join("\n\n")
);
assert!(
judged >= 65,
"only {judged} flow examples extracted from the guides — the \
extraction stopped matching and this would pass by knowing \
nothing"
);
assert!(
broken.is_empty(),
"{} documented examples do not reach a route:\n\n{}",
broken.len(),
broken.join("\n\n")
);
}
#[test]
fn every_documented_tap_is_admissible_at_the_driver_boundary() {
let mut checked = 0usize;
let mut refused: Vec<String> = Vec::new();
for (page, doc) in guide_pages() {
for (i, block) in yaml_blocks(doc).iter().enumerate() {
if !looks_like_a_flow(block) {
continue;
}
let Verdict::Reached(calls) = run_example(page, block) else {
continue;
};
for call in calls {
let (selector, route) = match &call {
MockCall::TapWithMode(s, _) => (s, "/tap"),
MockCall::DoubleTap(s) => (s, "/double-tap"),
MockCall::LongPress(s) => (s, "/long-press"),
MockCall::Tap(_) | MockCall::TapXcui(_) | MockCall::Launch(_) => continue,
};
checked += 1;
if let Err(e) = smix_driver::require_runner_resolvable_selector(selector, route) {
refused.push(format!(
"{page} block #{}: {}\n → {}",
i + 1,
call.describe(),
e.message
));
}
}
}
}
assert!(
checked > 0,
"no runner-side tap reached the boundary check — either the \
guides stopped showing one or the recording stopped working, \
and this would pass by knowing nothing"
);
assert!(
refused.is_empty(),
"{} documented taps the driver refuses on every call:\n\n{}",
refused.len(),
refused.join("\n\n")
);
}
#[test]
fn the_gate_catches_a_documented_example_the_driver_would_refuse() {
let block = "- tapOn:\n text: \"Sign In|Log In\"\n dispatch: daemonProxy\n";
let Verdict::Reached(calls) = run_example("synthetic", block) else {
panic!("the injected example did not even reach a route — it was meant to reach one");
};
let taps: Vec<_> = calls
.iter()
.filter_map(|c| match c {
MockCall::TapWithMode(s, _) => Some(s),
_ => None,
})
.collect();
assert_eq!(
taps.len(),
1,
"expected one runner-side tap from the injected example, got {calls:?}"
);
assert!(
smix_driver::require_runner_resolvable_selector(taps[0], "/tap").is_err(),
"the boundary check admitted a regex on a runner-side route — \
the corpus arm is no longer judging anything"
);
}
fn block_containing(page: &str, needle: &str) -> String {
let doc = guide_pages()
.get(page)
.copied()
.unwrap_or_else(|| panic!("{page} is not in the corpus"));
yaml_blocks(doc)
.into_iter()
.find(|b| b.contains(needle))
.unwrap_or_else(|| panic!("{page} no longer prints a block containing {needle:?}"))
}
#[test]
fn every_runner_dialling_command_can_reach_the_registry() {
use clap::CommandFactory;
let cli = crate::Cli::command();
let mut blind: Vec<String> = Vec::new();
let mut checked = 0usize;
fn walk(cmd: &clap::Command, path: &str, checked: &mut usize, out: &mut Vec<String>) {
let here = if path.is_empty() {
cmd.get_name().to_string()
} else {
format!("{path} {}", cmd.get_name())
};
let args: Vec<&clap::Arg> = cmd.get_arguments().collect();
if args.iter().any(|a| a.get_id() == "port") {
*checked += 1;
let indexes_registry = args
.iter()
.any(|a| matches!(a.get_id().as_str(), "device" | "udid" | "alias"));
if !indexes_registry {
out.push(here.clone());
}
}
for sub in cmd.get_subcommands() {
walk(sub, &here, checked, out);
}
}
walk(&cli, "", &mut checked, &mut blind);
assert!(
checked >= 8,
"only {checked} commands take a `--port` — the single-shot verbs \
were renamed or restructured and this would pass by knowing \
nothing"
);
assert!(
blind.is_empty(),
"{} runner-dialling commands cannot name a device, so the \
registry rung of the documented ladder is unreachable from \
them:\n {}",
blind.len(),
blind.join("\n ")
);
let run = cli
.get_subcommands()
.find(|c| c.get_name() == "run")
.expect("`smix run` still exists");
let runner_port = run
.get_arguments()
.find(|a| a.get_id() == "runner_port")
.expect("`smix run --runner-port` still exists");
assert_eq!(
runner_port.get_env().and_then(|e| e.to_str()),
Some("SMIX_RUNNER_PORT"),
"`smix run` lost its env rung — the ladder is short again, at \
the other end"
);
assert!(
run.get_arguments().any(|a| a.get_id() == "device"),
"`smix run --device` is gone — it is what indexes the registry"
);
}
#[test]
fn a_configured_launch_activity_reaches_the_device() {
let cfg = smix_adapter_maestro::AppsConfig::from_yaml(
"apps:\n demoApp:\n android:\n package: com.example.app\n activity: .NotMainActivity\n",
)
.expect("apps config parses");
let mut flow = smix_adapter_maestro::parse_flow_yaml("app: demoApp\n---\n- launchApp\n")
.expect("flow parses");
smix_adapter_maestro::resolve_app_into_flow(&mut flow, &cfg, smix_driver::Platform::Android)
.expect("resolve");
let app = MockApp::default();
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("runtime");
rt.block_on(async {
let mut adapter = smix_adapter_maestro::Adapter::new(
&app,
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")),
);
adapter.run(&flow).await.expect("run");
});
let trace: Vec<String> = app
.calls
.lock()
.expect("mock lock")
.iter()
.map(MockCall::describe)
.collect();
assert!(
trace.iter().any(|c| c.contains(".NotMainActivity")),
"the configured activity reaches nothing — it is read from the \
yaml, defaulted, and dropped before anything is dialled. \
Trace: {trace:?}"
);
}
#[test]
fn the_default_tap_takes_the_route_its_page_names() {
let block = block_containing("04-actions", "home-increment-btn");
let Verdict::Reached(calls) = run_example("04-actions", &block) else {
panic!("the default-tap example no longer reaches a route at all");
};
let by_id = calls
.iter()
.any(|c| matches!(c, MockCall::TapXcui(_) | MockCall::TapWithMode(..)));
assert!(
!by_id,
"the default tap took a runner-side route — the page describes \
a host-side resolve. Trace: {:?}",
calls.iter().map(MockCall::describe).collect::<Vec<_>>()
);
let page = guide_pages()["04-actions"];
let section = page
.split("### Tap with explicit dispatch")
.next()
.expect("04-actions still opens with the default-tap section");
assert!(
section.contains("/tap-at-norm-coord"),
"04-actions describes the default tap without naming the route \
it takes"
);
assert!(
!section.contains("_XCT_synthesizeEvent"),
"04-actions still attributes IOHID synthesis to the default \
tap; that is `dispatch: daemonProxy`"
);
assert!(
!section.contains("Path A") && !section.contains("Path B"),
"04-actions still describes a Path A / Path B fallback between \
the two routes; there is no fallback, `/tap-by-id` is opt-in"
);
}
#[test]
fn the_daemon_proxy_id_example_is_admissible() {
let block = block_containing("04-actions", "dispatch: daemonProxy");
let Verdict::Reached(calls) = run_example("04-actions", &block) else {
panic!("the daemonProxy example no longer reaches a route");
};
let taps: Vec<&Selector> = calls
.iter()
.filter_map(|c| match c {
MockCall::TapWithMode(s, smix_sdk::TapMode::DaemonProxySynthesize) => Some(s),
_ => None,
})
.collect();
assert!(
!taps.is_empty(),
"the example stopped routing through POST /tap — it is the \
daemonProxy path or it is not this example"
);
for s in taps {
smix_driver::require_runner_resolvable_selector(s, "/tap").unwrap_or_else(|e| {
panic!(
"the driver refuses the documented pairing again: {}",
e.message
)
});
}
}
#[test]
fn the_bare_string_form_matches_a_real_tree() {
let tree: smix_sdk::A11yNode = serde_json::from_str(include_str!(
"../tests/fixtures/live-tree-preferences-2026-07-22.json"
))
.expect("fixture tree parses");
let bare = crate::authoring::suggest_selectors(&tree, "General");
assert!(
!bare.is_empty(),
"the bare-string form finds nothing in a real iOS tree again"
);
let text_qualified = crate::authoring::suggest_selectors(&tree, "text:General");
assert!(
text_qualified.is_empty(),
"this tree now has a `text` field carrying \"General\" — the \
fixture changed and this no longer demonstrates anything"
);
}
const LIST: &str = include_str!("../../../docs/guide-executability.md");
const SELF: &str = include_str!("guide_gate.rs");
const LEDGER: &str = include_str!("../../../docs/audit-ledger.md");
struct Row<'a> {
id: &'a str,
status: &'a str,
probe: &'a str,
layer: &'a str,
ledger: &'a str,
reviewed: &'a str,
}
fn rows() -> Vec<Row<'static>> {
let mut out = Vec::new();
for line in LIST.lines() {
let line = line.trim();
if !line.starts_with("| ") {
continue;
}
let cells: Vec<&str> = line
.trim_matches('|')
.split(" | ")
.map(|c| c.trim().trim_matches('`'))
.collect();
let first = cells.first().copied().unwrap_or("");
if first == "id" || first.starts_with("---") {
continue;
}
assert_eq!(
cells.len(),
11,
"row `{first}` has {} cells, not 11 — escape any `|` inside a \
cell as `\\|`. A row this reader cannot split is a row \
nothing checks",
cells.len()
);
out.push(Row {
id: cells[0],
status: cells[3],
probe: cells[4],
layer: cells[6],
ledger: cells[7],
reviewed: cells[8],
});
}
out
}
#[test]
fn the_list_and_the_probes_agree() {
let rows = rows();
assert!(
rows.len() >= 8,
"only {} rows parsed out of the list — the table shape changed \
and this check would pass by knowing nothing",
rows.len()
);
let today = "2026-07-22";
for r in &rows {
assert!(
matches!(r.status, "runs" | "broken" | "unjudged"),
"{}: status `{}` is outside the vocabulary — an open \
vocabulary drifts this column back into prose",
r.id,
r.status
);
assert!(
r.reviewed <= today,
"{}: reviewed {} is in the future",
r.id,
r.reviewed
);
if r.status == "runs" {
assert_eq!(
r.layer, "—",
"{}: a claim that runs has no layer to fix",
r.id
);
} else {
assert_ne!(r.layer, "—", "{}: says what is broken, not where", r.id);
}
if r.status == "unjudged" {
assert_eq!(r.probe, "—", "{}: unjudged rows have no probe", r.id);
} else {
assert!(
SELF.contains(&format!("fn {}(", r.probe)),
"{}: names probe `{}`, which is not a test in this file",
r.id,
r.probe
);
}
if r.ledger != "—" {
assert!(
LEDGER.contains(r.ledger),
"{}: cites ledger row {}, which does not appear in \
docs/audit-ledger.md",
r.id,
r.ledger
);
}
}
for name in [
"every_runner_dialling_command_can_reach_the_registry",
"a_configured_launch_activity_reaches_the_device",
"the_default_tap_takes_the_route_its_page_names",
"the_daemon_proxy_id_example_is_admissible",
"the_bare_string_form_matches_a_real_tree",
"the_documented_regex_examples_are_patterns",
"every_documented_key_name_parses",
] {
assert!(
SELF.contains(&format!("fn {name}(")),
"probe `{name}` is named here but no longer exists"
);
assert!(
rows.iter().any(|r| r.probe == name),
"probe `{name}` runs and no row in the list claims it"
);
}
}
#[test]
fn this_gate_runs_where_it_must() {
let preflight = include_str!("../../../scripts/dev/preflight.sh");
assert!(
preflight.contains("include_str!(\\\"[^\\\"]*$d\\\")"),
"preflight no longer maps a changed doc back to the crates \
whose tests read it — edit only a guide and this gate is \
skipped by the one script meant to predict CI"
);
for (name, text) in [
("ci.yml", include_str!("../../../.github/workflows/ci.yml")),
("ship.sh", include_str!("../../../scripts/release/ship.sh")),
] {
assert!(
text.contains("cargo test --workspace"),
"{name} no longer runs the whole workspace, so nothing there \
runs this gate"
);
}
}
#[test]
fn summary() {
let mut judged = 0usize;
for (_, doc) in guide_pages() {
judged += yaml_blocks(doc)
.iter()
.filter(|b| looks_like_a_flow(b))
.count();
}
let rows = rows();
let count = |s: &str| rows.iter().filter(|r| r.status == s).count();
println!(
"guide-executability: {} claims ({} runs / {} broken / {} unjudged) \
· {judged} yaml blocks judged · {} examples not judged, all \
needing a device",
rows.len(),
count("runs"),
count("broken"),
count("unjudged"),
KNOWN_BROKEN.len(),
);
}
#[test]
fn the_documented_regex_examples_are_patterns() {
use smix_selector::Pattern;
let page = guide_pages()["03-selectors"];
let blocks = yaml_blocks(page);
let regex_section = blocks
.iter()
.find(|b| b.contains("regex:"))
.expect("03-selectors no longer prints an explicit regex example");
for shown in ["^Help$", "Row #[0-9]+"] {
assert!(
regex_section.contains(shown),
"03-selectors stopped printing {shown:?} as a regex example"
);
}
let flow = smix_adapter_maestro::parse_flow_yaml(&as_flow(regex_section))
.expect("the documented regex block parses");
let patterns: Vec<Pattern> = flow
.steps
.iter()
.filter_map(|s| match s {
smix_adapter_maestro::Step::TapOn {
selector: smix_selector::Selector::Text { text, .. },
..
} => Some(text.clone()),
_ => None,
})
.filter(|p| match p {
Pattern::Text(t) => t.contains('[') || t.starts_with('^'),
Pattern::Regex { regex, .. } => regex.contains('[') || regex.starts_with('^'),
})
.collect();
assert_eq!(
patterns.len(),
2,
"expected the two regex examples out of the block, got {patterns:?}"
);
for p in &patterns {
assert!(
matches!(p, Pattern::Regex { .. }),
"a documented regex example parses as a literal: {p:?}"
);
}
assert!(
matches!(
smix_adapter_maestro::text_to_pattern("Delete?"),
Pattern::Text(_)
),
"meta-character detection widened — an ordinary label is a \
pattern now, and matches more than it says"
);
}
#[test]
fn every_documented_key_name_parses() {
let page = guide_pages()["04-actions"];
let line = page
.lines()
.find(|l| l.starts_with("- Available keys:"))
.expect("04-actions still lists the available keys");
let keys: Vec<&str> = line
.trim_start_matches("- Available keys:")
.split('/')
.map(str::trim)
.map(|k| k.trim_end_matches('.'))
.filter(|k| !k.is_empty())
.collect();
assert!(
keys.len() >= 6,
"only {} keys read out of the sentence — the list changed shape \
and this would pass by knowing nothing",
keys.len()
);
let mut rejected = Vec::new();
for key in &keys {
if let Verdict::Refused(why) = run_example("synthetic", &format!("- pressKey: {key}\n")) {
rejected.push(format!("{key}: {why}"));
}
}
assert!(
rejected.is_empty(),
"04-actions lists {} keys that do not exist:\n {}",
rejected.len(),
rejected.join("\n ")
);
}