#![allow( unsafe_code, clippy::borrow_as_ptr, clippy::cast_possible_truncation, clippy::items_after_statements )] use std::ffi::c_void; use std::mem::{offset_of, size_of}; use std::ptr; use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering}; use std::sync::{Condvar, Mutex}; use std::thread; use std::time::{Duration, Instant}; const ABI_V1: u32 = 1; const OK: u32 = 0; const INVALID_ARGUMENT: u32 = 1; const INVALID_STATE: u32 = 2; const UNSUPPORTED_ABI: u32 = 3; const AUTHORITY_REJECTED: u32 = 4; const TLS: u32 = 5; const QUEUE_FULL: u32 = 9; const CANCELLED: u32 = 10; const REENTRANT: u32 = 11; const BUSY: u32 = 12; const INTERNAL: u32 = 13; const STATE_CONNECTING: u32 = 1; const STATE_CONNECTED: u32 = 2; const STATE_CANCELLED: u32 = 3; const INPUT_KEYBOARD: u32 = 1; #[repr(C)] struct Core { _private: [u8; 0], } #[repr(C)] #[derive(Clone, Copy)] struct BytesView { data: *const u8, length: usize, } #[repr(C)] struct StateEvent { struct_size: u32, abi_version: u32, state: u32, reason: u32, } #[repr(C)] struct ErrorEvent { struct_size: u32, abi_version: u32, code: u32, retryable: u32, phase: u32, reserved: u32, } #[repr(C)] struct StatsEvent { struct_size: u32, abi_version: u32, dropped_callbacks: u64, dropped_media_units: u64, dropped_input_events: u64, } #[repr(C)] struct MediaEvent { struct_size: u32, abi_version: u32, channel: u32, sequence: u32, timestamp_ms: u64, encoded_unit: BytesView, } #[repr(C)] struct ControlEvent { struct_size: u32, abi_version: u32, kind: u32, reserved: u32, payload: BytesView, } type SignFn = unsafe extern "C" fn(*mut c_void, BytesView, *mut u8) -> u32; type StateFn = unsafe extern "C" fn(*mut c_void, *const StateEvent); type ErrorFn = unsafe extern "C" fn(*mut c_void, *const ErrorEvent); type StatsFn = unsafe extern "C" fn(*mut c_void, *const StatsEvent); type MediaFn = unsafe extern "C" fn(*mut c_void, *const MediaEvent); type ControlFn = unsafe extern "C" fn(*mut c_void, *const ControlEvent); #[repr(C)] struct Config { struct_size: u32, abi_version: u32, context: *mut c_void, sign_admission: Option, sign_tls_ed25519: Option, on_state: Option, on_error: Option, on_stats: Option, on_media: Option, on_control: Option, } #[repr(C)] struct ConnectRequest { struct_size: u32, abi_version: u32, manifest_json: BytesView, tunnel_credential_json: BytesView, } #[repr(C)] struct InputEvent { struct_size: u32, abi_version: u32, kind: u32, flags: u32, values: [i32; 12], } unsafe extern "C" { fn verse_core_abi_version() -> u32; fn verse_core_create_v1(config: *const Config, out_core: *mut *mut Core) -> u32; fn verse_core_connect_v1(core: *mut Core, request: *const ConnectRequest) -> u32; fn verse_core_send_input_v1(core: *mut Core, event: *const InputEvent) -> u32; fn verse_core_request_idr_v1(core: *mut Core) -> u32; fn verse_core_cancel_v1(core: *mut Core) -> u32; fn verse_core_destroy_v1(core: *mut Core, timeout_ms: u32) -> u32; } const MANIFEST: &[u8] = br#"{ "version":"1","purpose":"launch","session_id":"session","reconnect_sequence":0, "gateway":{"id":"gateway","addresses":["gateway.test:443"],"public_identity":"gateway.test"}, "tunnel":{"versions":["verse-gateway-v1/1"],"features":["control.v1","input.absolute.v1","input.scroll.v1"]}, "profile":{"id":"standard","bounds":{"minimum_kbps":1000,"target_kbps":5000,"maximum_kbps":10000},"display_mode":{"resolution_width":1920,"resolution_height":1080,"fps":60}}, "grant":{"opaque_value":"ggggggggggggggggggggggggggggggggggggggggggg","expires_at":"2099-01-01T00:00:00Z","audience":"audience"}, "correlation_id":"correlation" }"#; const CREDENTIAL: &[u8] = br#"{"client_device_id":"device","device_key_id":"key","certificate_chain_pem":"-----BEGIN CERTIFICATE-----\nAQID\n-----END CERTIFICATE-----","trust_bundle_pem":"-----BEGIN CERTIFICATE-----\nAQID\n-----END CERTIFICATE-----","expires_at":"2099-01-01T00:00:00Z"}"#; struct Context { core: AtomicUsize, admission_calls: AtomicUsize, tls_calls: AtomicUsize, admission_status: AtomicU32, tls_status: AtomicU32, admission_input: Mutex>, tls_input: Mutex>, states: Mutex>, wake: Condvar, reentry_cancel: AtomicU32, reentry_send: AtomicU32, reentry_destroy: AtomicU32, block_callbacks: AtomicBool, release_callbacks: AtomicBool, callback_active: AtomicUsize, callback_max: AtomicUsize, cancel_on_connecting: AtomicBool, reentry_target: AtomicUsize, reentry_results: Mutex>, } impl Default for Context { fn default() -> Self { Self { core: AtomicUsize::new(0), admission_calls: AtomicUsize::new(0), tls_calls: AtomicUsize::new(0), admission_status: AtomicU32::new(OK), tls_status: AtomicU32::new(OK), admission_input: Mutex::new(Vec::new()), tls_input: Mutex::new(Vec::new()), states: Mutex::new(Vec::new()), wake: Condvar::new(), reentry_cancel: AtomicU32::new(u32::MAX), reentry_send: AtomicU32::new(u32::MAX), reentry_destroy: AtomicU32::new(u32::MAX), block_callbacks: AtomicBool::new(false), release_callbacks: AtomicBool::new(false), callback_active: AtomicUsize::new(0), callback_max: AtomicUsize::new(0), cancel_on_connecting: AtomicBool::new(false), reentry_target: AtomicUsize::new(0), reentry_results: Mutex::new(Vec::new()), } } } unsafe fn context<'a>(raw: *mut c_void) -> &'a Context { // Test invariant: every callback receives the live Box supplied at create. unsafe { &*raw.cast::() } } unsafe extern "C" fn sign_admission(raw: *mut c_void, input: BytesView, output: *mut u8) -> u32 { // Test invariant: ABI promises input is readable for input.length during this callback. let bytes = unsafe { std::slice::from_raw_parts(input.data, input.length) }; let ctx = unsafe { context(raw) }; ctx.admission_calls.fetch_add(1, Ordering::SeqCst); *ctx.admission_input.lock().expect("admission lock") = bytes.to_vec(); // Test invariant: ABI promises a writable 64-byte Rust-owned signature buffer. unsafe { ptr::write_bytes(output, 0xA5, 64) }; ctx.admission_status.load(Ordering::SeqCst) } unsafe extern "C" fn sign_tls(raw: *mut c_void, input: BytesView, output: *mut u8) -> u32 { // Test invariant: ABI promises input is readable for input.length during this callback. let bytes = unsafe { std::slice::from_raw_parts(input.data, input.length) }; let ctx = unsafe { context(raw) }; ctx.tls_calls.fetch_add(1, Ordering::SeqCst); *ctx.tls_input.lock().expect("tls lock") = bytes.to_vec(); // Test invariant: ABI promises a writable 64-byte Rust-owned signature buffer. unsafe { ptr::write_bytes(output, 0x5A, 64) }; ctx.tls_status.load(Ordering::SeqCst) } unsafe extern "C" fn sign_admission_probes_global_reentry( raw: *mut c_void, input: BytesView, output: *mut u8, ) -> u32 { let ctx = unsafe { context(raw) }; let target = ctx.reentry_target.load(Ordering::SeqCst) as *mut Core; let results = [ unsafe { verse_core_abi_version() }, unsafe { verse_core_create_v1(ptr::null(), ptr::null_mut()) }, unsafe { verse_core_connect_v1(target, ptr::null()) }, unsafe { verse_core_send_input_v1(target, ptr::null()) }, unsafe { verse_core_request_idr_v1(target) }, unsafe { verse_core_cancel_v1(target) }, unsafe { verse_core_destroy_v1(target, 0) }, ]; ctx.reentry_results .lock() .expect("signer reentry results") .extend(results); unsafe { sign_admission(raw, input, output) } } unsafe extern "C" fn on_state_probes_global_reentry(raw: *mut c_void, event: *const StateEvent) { let ctx = unsafe { context(raw) }; // Test invariant: ABI promises a readable state record for the callback duration. let state = unsafe { (*event).state }; ctx.states.lock().expect("states lock").push(state); ctx.wake.notify_all(); if state != STATE_CONNECTED { return; } let origin = ctx.core.load(Ordering::SeqCst) as *mut Core; let other = ctx.reentry_target.load(Ordering::SeqCst) as *mut Core; let results = [ unsafe { verse_core_abi_version() }, unsafe { verse_core_create_v1(ptr::null(), ptr::null_mut()) }, unsafe { verse_core_connect_v1(origin, ptr::null()) }, unsafe { verse_core_send_input_v1(origin, ptr::null()) }, unsafe { verse_core_request_idr_v1(origin) }, unsafe { verse_core_cancel_v1(other) }, unsafe { verse_core_destroy_v1(origin, 0) }, unsafe { verse_core_cancel_v1(origin) }, ]; ctx.reentry_results .lock() .expect("event reentry results") .extend(results); } unsafe extern "C" fn on_state(raw: *mut c_void, event: *const StateEvent) { let ctx = unsafe { context(raw) }; let active = ctx.callback_active.fetch_add(1, Ordering::SeqCst) + 1; ctx.callback_max.fetch_max(active, Ordering::SeqCst); // Test invariant: ABI promises a readable state record for the callback duration. let state = unsafe { (*event).state }; ctx.states.lock().expect("states lock").push(state); ctx.wake.notify_all(); if state == STATE_CONNECTING && ctx.cancel_on_connecting.load(Ordering::SeqCst) { let core = ctx.core.load(Ordering::SeqCst) as *mut Core; ctx.reentry_cancel .store(unsafe { verse_core_cancel_v1(core) }, Ordering::SeqCst); } if ctx.block_callbacks.load(Ordering::SeqCst) && !ctx.release_callbacks.load(Ordering::SeqCst) { let mut states = ctx.states.lock().expect("states lock"); while !ctx.release_callbacks.load(Ordering::SeqCst) { states = ctx.wake.wait(states).expect("callback wait"); } } if state == STATE_CONNECTED && ctx.reentry_cancel.load(Ordering::SeqCst) == u32::MAX { let core = ctx.core.load(Ordering::SeqCst) as *mut Core; // Test invariant: the stored handle is live until this callback and its destroy complete. ctx.reentry_cancel .store(unsafe { verse_core_cancel_v1(core) }, Ordering::SeqCst); let event = keyboard_event(); // Test invariant: event and handle remain valid for the synchronous call. ctx.reentry_send.store( unsafe { verse_core_send_input_v1(core, &event) }, Ordering::SeqCst, ); // Test invariant: the callback intentionally probes the documented reentry rejection. ctx.reentry_destroy .store(unsafe { verse_core_destroy_v1(core, 1) }, Ordering::SeqCst); } ctx.callback_active.fetch_sub(1, Ordering::SeqCst); } unsafe extern "C" fn sign_admission_reenters( raw: *mut c_void, input: BytesView, output: *mut u8, ) -> u32 { let ctx = unsafe { context(raw) }; ctx.reentry_cancel.store( unsafe { verse_core_cancel_v1(ctx.core.load(Ordering::SeqCst) as *mut Core) }, Ordering::SeqCst, ); unsafe { sign_admission(raw, input, output) } } unsafe extern "C" fn sign_admission_waits_for_cancel( raw: *mut c_void, input: BytesView, output: *mut u8, ) -> u32 { let ctx = unsafe { context(raw) }; let deadline = Instant::now() + Duration::from_secs(2); while ctx.reentry_cancel.load(Ordering::SeqCst) == u32::MAX { assert!( Instant::now() < deadline, "connecting callback did not cancel" ); thread::yield_now(); } unsafe { sign_admission(raw, input, output) } } fn config(ctx: &mut Context) -> Config { Config { struct_size: size_of::() as u32, abi_version: ABI_V1, context: ptr::from_mut(ctx).cast(), sign_admission: Some(sign_admission), sign_tls_ed25519: Some(sign_tls), on_state: Some(on_state), on_error: None, on_stats: None, on_media: None, on_control: None, } } fn request(manifest: &[u8], credential: &[u8]) -> ConnectRequest { ConnectRequest { struct_size: size_of::() as u32, abi_version: ABI_V1, manifest_json: BytesView { data: manifest.as_ptr(), length: manifest.len(), }, tunnel_credential_json: BytesView { data: credential.as_ptr(), length: credential.len(), }, } } fn keyboard_event() -> InputEvent { let mut values = [0; 12]; values[0] = 1; values[2] = 30; InputEvent { struct_size: size_of::() as u32, abi_version: ABI_V1, kind: INPUT_KEYBOARD, flags: 0, values, } } fn create(ctx: &mut Context) -> *mut Core { let mut core = ptr::null_mut(); let config = config(ctx); // Test invariant: config/out pointers remain valid for the synchronous create call. assert_eq!(unsafe { verse_core_create_v1(&config, &mut core) }, OK); assert!(!core.is_null()); ctx.core.store(core as usize, Ordering::SeqCst); core } fn connect(core: *mut Core, manifest: &[u8], credential: &[u8]) -> u32 { let request = request(manifest, credential); // Test invariant: request and backing byte slices remain valid for the synchronous call. unsafe { verse_core_connect_v1(core, &request) } } fn wait_for(ctx: &Context, predicate: impl Fn(&[u32]) -> bool) { let deadline = Instant::now() + Duration::from_secs(2); let mut states = ctx.states.lock().expect("states lock"); while !predicate(&states) { let remaining = deadline.saturating_duration_since(Instant::now()); assert!( !remaining.is_zero(), "callback deadline exceeded: {states:?}" ); (states, _) = ctx .wake .wait_timeout(states, remaining) .expect("callback wait"); } } fn destroy(core: *mut Core) -> u32 { // Test invariant: caller retains the handle until destroy reports success. unsafe { verse_core_destroy_v1(core, 2_000) } } #[test] fn arm64_c_layout_is_exact_and_version_is_fixed() { assert_eq!(versevdi_core::session::INPUT_QUEUE_CAPACITY, 64); assert_eq!(size_of::(), 16); assert_eq!(size_of::(), 72); assert_eq!(offset_of!(Config, sign_admission), 16); assert_eq!(size_of::(), 40); assert_eq!(offset_of!(ConnectRequest, manifest_json), 8); assert_eq!(size_of::(), 64); assert_eq!(size_of::(), 16); assert_eq!(size_of::(), 24); assert_eq!(size_of::(), 32); assert_eq!(size_of::(), 40); assert_eq!(size_of::(), 32); // Test invariant: no pointer arguments are involved. assert_eq!(unsafe { verse_core_abi_version() }, ABI_V1); } #[test] fn create_validates_prefix_callbacks_output_and_trailing_bytes() { let mut ctx = Context::default(); let mut core = ptr::dangling_mut::(); let mut cfg = config(&mut ctx); cfg.struct_size = 7; // Test invariant: config/out are readable/writable for this call. assert_eq!( unsafe { verse_core_create_v1(&cfg, &mut core) }, INVALID_ARGUMENT ); assert!(core.is_null()); cfg = config(&mut ctx); cfg.abi_version = 2; assert_eq!( unsafe { verse_core_create_v1(&cfg, &mut core) }, UNSUPPORTED_ABI ); assert!(core.is_null()); cfg = config(&mut ctx); cfg.sign_tls_ed25519 = None; assert_eq!( unsafe { verse_core_create_v1(&cfg, &mut core) }, INVALID_ARGUMENT ); assert!(core.is_null()); #[repr(C)] struct Extended { base: Config, ignored: [u8; 32], } let extended = Extended { base: config(&mut ctx), ignored: [0xEE; 32], }; let mut extended = extended; extended.base.struct_size = size_of::() as u32; assert_eq!( unsafe { verse_core_create_v1(&extended.base, &mut core) }, OK ); assert_eq!(destroy(core), OK); } #[test] fn connect_copies_inputs_and_calls_purpose_specific_signers_once() { let mut ctx = Context::default(); let core = create(&mut ctx); let mut manifest = MANIFEST.to_vec(); let mut credential = CREDENTIAL.to_vec(); assert_eq!(connect(core, &manifest, &credential), OK); manifest.fill(b'x'); credential.fill(b'y'); assert_eq!(ctx.admission_calls.load(Ordering::SeqCst), 1); assert_eq!(ctx.tls_calls.load(Ordering::SeqCst), 1); assert_eq!(&*ctx.admission_input.lock().expect("admission"), MANIFEST); assert_eq!(&*ctx.tls_input.lock().expect("tls"), CREDENTIAL); assert_eq!(destroy(core), OK); } #[test] fn tables_and_slices_reject_short_unsupported_null_and_oversized_inputs() { let mut ctx = Context::default(); ctx.reentry_cancel.store(OK, Ordering::SeqCst); let core = create(&mut ctx); let mut req = request(MANIFEST, CREDENTIAL); req.struct_size = 7; assert_eq!( unsafe { verse_core_connect_v1(core, &req) }, INVALID_ARGUMENT ); req.struct_size = size_of::() as u32; req.abi_version = 2; assert_eq!( unsafe { verse_core_connect_v1(core, &req) }, UNSUPPORTED_ABI ); req.abi_version = ABI_V1; req.manifest_json = BytesView { data: ptr::null(), length: 1, }; assert_eq!( unsafe { verse_core_connect_v1(core, &req) }, INVALID_ARGUMENT ); req.manifest_json = BytesView { data: ptr::null(), length: 0, }; assert_eq!( unsafe { verse_core_connect_v1(core, &req) }, INVALID_ARGUMENT ); req.manifest_json.length = 1_048_577; assert_eq!( unsafe { verse_core_connect_v1(core, &req) }, INVALID_ARGUMENT ); let mut input = keyboard_event(); input.struct_size = 7; assert_eq!( unsafe { verse_core_send_input_v1(core, &input) }, INVALID_ARGUMENT ); input.struct_size = size_of::() as u32; input.abi_version = 2; assert_eq!( unsafe { verse_core_send_input_v1(core, &input) }, UNSUPPORTED_ABI ); #[repr(C)] struct ExtendedRequest { base: ConnectRequest, ignored: [u8; 24], } let mut trailing_request = ExtendedRequest { base: request(MANIFEST, CREDENTIAL), ignored: [0xEE; 24], }; trailing_request.base.struct_size = size_of::() as u32; assert_eq!( unsafe { verse_core_connect_v1(core, &trailing_request.base) }, OK ); #[repr(C)] struct ExtendedInput { base: InputEvent, ignored: [u8; 24], } let mut trailing_input = ExtendedInput { base: keyboard_event(), ignored: [0xEE; 24], }; trailing_input.base.struct_size = size_of::() as u32; assert_eq!( unsafe { verse_core_send_input_v1(core, &trailing_input.base) }, OK ); assert_eq!(destroy(core), OK); } #[test] fn callback_order_is_serial_and_only_cancel_is_reentrant() { let mut ctx = Context::default(); let core = create(&mut ctx); assert_eq!(connect(core, MANIFEST, CREDENTIAL), OK); wait_for(&ctx, |states| states.contains(&STATE_CANCELLED)); assert_eq!( ctx.states.lock().expect("states").as_slice(), [STATE_CONNECTING, STATE_CONNECTED, STATE_CANCELLED] ); assert_eq!(ctx.reentry_cancel.load(Ordering::SeqCst), OK); assert_eq!(ctx.reentry_send.load(Ordering::SeqCst), REENTRANT); assert_eq!(ctx.reentry_destroy.load(Ordering::SeqCst), REENTRANT); assert_eq!(ctx.callback_max.load(Ordering::SeqCst), 1); assert_eq!(destroy(core), OK); } #[test] fn signer_callbacks_cannot_reenter_even_cancel() { let mut ctx = Context::default(); let mut cfg = config(&mut ctx); cfg.sign_admission = Some(sign_admission_reenters); let mut core = ptr::null_mut(); assert_eq!(unsafe { verse_core_create_v1(&cfg, &mut core) }, OK); ctx.core.store(core as usize, Ordering::SeqCst); assert_eq!(connect(core, MANIFEST, CREDENTIAL), OK); assert_eq!(ctx.reentry_cancel.load(Ordering::SeqCst), REENTRANT); assert_eq!(destroy(core), OK); } #[test] fn signer_callback_rejects_every_stateful_api_across_handles() { let mut other_ctx = Context::default(); other_ctx.reentry_cancel.store(OK, Ordering::SeqCst); let other = create(&mut other_ctx); let mut ctx = Context::default(); ctx.reentry_target.store(other as usize, Ordering::SeqCst); let mut cfg = config(&mut ctx); cfg.sign_admission = Some(sign_admission_probes_global_reentry); let mut core = ptr::null_mut(); assert_eq!(unsafe { verse_core_create_v1(&cfg, &mut core) }, OK); ctx.core.store(core as usize, Ordering::SeqCst); assert_eq!(connect(core, MANIFEST, CREDENTIAL), OK); assert_eq!( ctx.reentry_results .lock() .expect("signer results") .as_slice(), [ABI_V1, REENTRANT, REENTRANT, REENTRANT, REENTRANT, REENTRANT, REENTRANT] ); assert_eq!(connect(other, MANIFEST, CREDENTIAL), OK); assert_eq!(destroy(core), OK); assert_eq!(destroy(other), OK); } #[test] fn event_callback_allows_only_originating_handle_cancel() { let mut other_ctx = Context::default(); other_ctx.reentry_cancel.store(OK, Ordering::SeqCst); let other = create(&mut other_ctx); let mut ctx = Context::default(); ctx.reentry_target.store(other as usize, Ordering::SeqCst); let mut cfg = config(&mut ctx); cfg.on_state = Some(on_state_probes_global_reentry); let mut core = ptr::null_mut(); assert_eq!(unsafe { verse_core_create_v1(&cfg, &mut core) }, OK); ctx.core.store(core as usize, Ordering::SeqCst); assert_eq!(connect(core, MANIFEST, CREDENTIAL), OK); wait_for(&ctx, |states| states.contains(&STATE_CANCELLED)); assert_eq!( ctx.reentry_results .lock() .expect("event results") .as_slice(), [ABI_V1, REENTRANT, REENTRANT, REENTRANT, REENTRANT, REENTRANT, REENTRANT, OK] ); assert_eq!(connect(other, MANIFEST, CREDENTIAL), OK); assert_eq!(destroy(core), OK); assert_eq!(destroy(other), OK); } #[test] fn signer_statuses_are_purpose_specific_and_unknown_values_are_internal() { for (admission, expected) in [ (OK, OK), (AUTHORITY_REJECTED, AUTHORITY_REJECTED), (CANCELLED, CANCELLED), (INTERNAL, INTERNAL), (TLS, INTERNAL), (u32::MAX, INTERNAL), ] { let mut ctx = Context::default(); ctx.reentry_cancel.store(OK, Ordering::SeqCst); ctx.admission_status.store(admission, Ordering::SeqCst); let core = create(&mut ctx); assert_eq!(connect(core, MANIFEST, CREDENTIAL), expected); assert_eq!(destroy(core), OK); } for (tls, expected) in [ (OK, OK), (TLS, TLS), (CANCELLED, CANCELLED), (INTERNAL, INTERNAL), (AUTHORITY_REJECTED, INTERNAL), (u32::MAX, INTERNAL), ] { let mut ctx = Context::default(); ctx.reentry_cancel.store(OK, Ordering::SeqCst); ctx.tls_status.store(tls, Ordering::SeqCst); let core = create(&mut ctx); assert_eq!(connect(core, MANIFEST, CREDENTIAL), expected); assert_eq!(destroy(core), OK); } } #[test] fn cancel_during_connect_preserves_state_order_and_stops_before_tls_signing() { let mut ctx = Context::default(); ctx.cancel_on_connecting.store(true, Ordering::SeqCst); let mut cfg = config(&mut ctx); cfg.sign_admission = Some(sign_admission_waits_for_cancel); let mut core = ptr::null_mut(); assert_eq!(unsafe { verse_core_create_v1(&cfg, &mut core) }, OK); ctx.core.store(core as usize, Ordering::SeqCst); assert_eq!(connect(core, MANIFEST, CREDENTIAL), CANCELLED); wait_for(&ctx, |states| states.contains(&STATE_CANCELLED)); assert_eq!( ctx.states.lock().expect("states").as_slice(), [STATE_CONNECTING, STATE_CANCELLED] ); assert_eq!(ctx.tls_calls.load(Ordering::SeqCst), 0); assert_eq!(destroy(core), OK); } #[test] fn send_input_is_nonblocking_bounded_and_cancel_is_idempotent() { let mut ctx = Context::default(); ctx.reentry_cancel.store(OK, Ordering::SeqCst); let core = create(&mut ctx); assert_eq!(connect(core, MANIFEST, CREDENTIAL), OK); let event = keyboard_event(); for _ in 0..64 { assert_eq!(unsafe { verse_core_send_input_v1(core, &event) }, OK); } assert_eq!( unsafe { verse_core_send_input_v1(core, &event) }, QUEUE_FULL ); assert_eq!(unsafe { verse_core_request_idr_v1(core) }, OK); assert_eq!(unsafe { verse_core_cancel_v1(core) }, OK); assert_eq!(unsafe { verse_core_cancel_v1(core) }, OK); assert_eq!(unsafe { verse_core_send_input_v1(core, &event) }, CANCELLED); assert_eq!(destroy(core), OK); } #[test] fn destroy_timeout_keeps_ownership_suppresses_late_callbacks_and_allows_retry() { let mut ctx = Context::default(); ctx.reentry_cancel.store(OK, Ordering::SeqCst); ctx.block_callbacks.store(true, Ordering::SeqCst); let core = create(&mut ctx); assert_eq!(connect(core, MANIFEST, CREDENTIAL), OK); wait_for(&ctx, |states| !states.is_empty()); assert_eq!(unsafe { verse_core_destroy_v1(core, 1) }, BUSY); let count_at_timeout = ctx.states.lock().expect("states").len(); ctx.release_callbacks.store(true, Ordering::SeqCst); ctx.wake.notify_all(); assert_eq!(destroy(core), OK); assert_eq!(ctx.states.lock().expect("states").len(), count_at_timeout); } #[test] fn preconnect_and_postcancel_state_checks_are_stable() { let mut ctx = Context::default(); ctx.reentry_cancel.store(OK, Ordering::SeqCst); let core = create(&mut ctx); let event = keyboard_event(); assert_eq!( unsafe { verse_core_send_input_v1(core, &event) }, INVALID_STATE ); assert_eq!(unsafe { verse_core_request_idr_v1(core) }, INVALID_STATE); assert_eq!(unsafe { verse_core_cancel_v1(core) }, OK); assert_eq!(connect(core, MANIFEST, CREDENTIAL), CANCELLED); assert_eq!(destroy(core), OK); }