blob: e5cf9f1c9329380292f8dc8dd455ffb58d1e5527 [file]
// Copyright 2026 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use helper::{
debug_data_message, init_test_logging, koid, on_each_message, peer_koid, single_stashed_svc,
svc_stash, take_svc_channel,
};
use std::ffi::CStr;
use zx::sys::ZX_KOID_INVALID;
use zx::{Channel, EventPair, Koid, MonotonicInstant, Signals, Vmo, WaitResult};
use zx_libc::sanitizer::publish_data;
struct SvcSingleProcessTest {
local_svc: Channel,
stashed_svc: Channel,
}
impl SvcSingleProcessTest {
fn new() -> Self {
let svc_stash = svc_stash();
assert!(!svc_stash.is_invalid());
let stashed_svc = single_stashed_svc(svc_stash);
let local_svc = take_svc_channel();
let test = Self { local_svc, stashed_svc };
test.drain_stashed_svc();
test
}
/// Drains any preexisting messages from `stashed_svc` so tests can observe
/// state transitions cleanly even in instrumented builds that publish debug
/// data before `main`.
fn drain_stashed_svc(&self) {
on_each_message(&self.stashed_svc, |stashed_svc| {
let mut bytes = Vec::new();
let mut handles = Vec::new();
stashed_svc
.read_split(&mut bytes, &mut handles)
.expect("failed to drain message from stashed_svc");
true
});
}
fn svc_stub_is_valid_handle(&self) {
self.drain_stashed_svc();
assert!(!self.local_svc.is_invalid());
}
fn writing_into_svc_shows_up_in_stash_handle(&self) {
self.drain_stashed_svc();
assert!(!self.local_svc.is_invalid());
assert!(!self.stashed_svc.is_invalid());
let written_message = b"Hello World!\0";
self.local_svc.write(written_message, &mut []).expect("failed to write to local_svc");
let mut read_message = Vec::new();
let mut read_handles = Vec::new();
self.stashed_svc
.read_split(&mut read_message, &mut read_handles)
.expect("failed to read from stashed_svc");
assert_eq!(read_message.as_slice(), written_message);
assert!(read_handles.is_empty());
}
fn sanitizer_publish_data_shows_up_in_stashed_handle(&self) {
self.drain_stashed_svc();
assert!(!self.local_svc.is_invalid());
assert!(!self.stashed_svc.is_invalid());
let vmo = Vmo::create(124).expect("failed to create VMO");
// Channel transitions from not readable to readable.
assert!(matches!(
self.stashed_svc.wait_one(Signals::CHANNEL_READABLE, MonotonicInstant::INFINITE_PAST),
WaitResult::TimedOut(_)
));
const DATA_SINK: &str = "some_sink_name";
const DATA_SINK_CSTR: &CStr = c"some_sink_name";
let vmo_koid = koid(vmo.as_handle_ref());
let token_1: EventPair = publish_data(DATA_SINK_CSTR, vmo).into();
assert!(!token_1.is_invalid());
let token_koid = peer_koid(token_1.as_handle_ref());
assert_ne!(token_koid, Koid::from_raw(ZX_KOID_INVALID));
assert_ne!(vmo_koid, Koid::from_raw(ZX_KOID_INVALID));
let mut found = false;
on_each_message(&self.stashed_svc, |stashed_svc| {
let debug_msg = debug_data_message(stashed_svc);
if debug_msg.sink != DATA_SINK {
return true;
}
assert_eq!(koid(debug_msg.vmo.as_handle_ref()), vmo_koid);
assert_eq!(koid(debug_msg.token.as_handle_ref()), token_koid);
found = true;
false
});
assert!(found);
}
}
fn main() {
init_test_logging();
let test = SvcSingleProcessTest::new();
test.svc_stub_is_valid_handle();
test.writing_into_svc_shows_up_in_stash_handle();
test.sanitizer_publish_data_shows_up_in_stashed_handle();
}