use std::error::Error;
use std::process::ExitCode;
use bevy::MinimalPlugins;
use bevy::app::App;
use bevy::ecs::reflect::ReflectComponent;
use bevy::ecs::relationship::Relationship;
use bevy::ecs::relationship::RelationshipTarget;
use bevy::prelude::Component;
use bevy::prelude::On;
use bevy::prelude::Reflect;
use bevy::prelude::ResMut;
use bevy::prelude::Resource;
use bevy::prelude::World;
use hana_rigging::prelude::ApplyDeadline;
use hana_rigging::prelude::ApplyPermit;
use hana_rigging::prelude::AttachmentPath;
use hana_rigging::prelude::AttemptFinished;
use hana_rigging::prelude::AttemptId;
use hana_rigging::prelude::AttemptOutcome;
use hana_rigging::prelude::AttemptProgress;
use hana_rigging::prelude::AuthoritativeReporterCoverage;
use hana_rigging::prelude::Binding;
use hana_rigging::prelude::BindingEntities;
use hana_rigging::prelude::BindingEntityLookup;
use hana_rigging::prelude::Bindings;
use hana_rigging::prelude::Capabilities;
use hana_rigging::prelude::CaptureOutcome;
use hana_rigging::prelude::Claim;
use hana_rigging::prelude::ClaimChanged;
use hana_rigging::prelude::ConfiguredDevice;
use hana_rigging::prelude::ConfiguredDeviceConnection;
use hana_rigging::prelude::ConfiguredDeviceConnectionChanged;
use hana_rigging::prelude::ConfiguredDeviceMode;
use hana_rigging::prelude::CoveredDeviceIdentitySpace;
use hana_rigging::prelude::DeviceArrived;
use hana_rigging::prelude::DeviceDeparted;
use hana_rigging::prelude::DeviceDescriptor;
use hana_rigging::prelude::DeviceEndpoint;
use hana_rigging::prelude::DeviceIdSource;
use hana_rigging::prelude::DeviceKey;
use hana_rigging::prelude::DeviceKind;
use hana_rigging::prelude::DeviceRecord;
use hana_rigging::prelude::DeviceReporter;
use hana_rigging::prelude::DeviceResolution;
use hana_rigging::prelude::DeviceScan;
use hana_rigging::prelude::DeviceStateLookup;
use hana_rigging::prelude::Devices;
use hana_rigging::prelude::DiscoveryCadence;
use hana_rigging::prelude::DiscoveryControl;
use hana_rigging::prelude::DiscoveryStatus;
use hana_rigging::prelude::DiscoveryWork;
use hana_rigging::prelude::DriverId;
use hana_rigging::prelude::EndpointDriver;
use hana_rigging::prelude::EndpointId;
use hana_rigging::prelude::HardwareInventory;
use hana_rigging::prelude::IdentityChanged;
use hana_rigging::prelude::LastDiscoveryOutcome;
use hana_rigging::prelude::LastKnownGoodConfiguration;
use hana_rigging::prelude::MainThreadDiscoveryJob;
use hana_rigging::prelude::OnAbort;
use hana_rigging::prelude::OnSessionLoss;
use hana_rigging::prelude::PlatformDeviceHandle;
use hana_rigging::prelude::Presence;
use hana_rigging::prelude::PresenceChanged;
use hana_rigging::prelude::RecoveryPolicy;
use hana_rigging::prelude::ReportedAs;
use hana_rigging::prelude::ReportedId;
use hana_rigging::prelude::ReportedParent;
use hana_rigging::prelude::ReportedSerial;
use hana_rigging::prelude::ReporterCoverage;
use hana_rigging::prelude::ReporterId;
use hana_rigging::prelude::ReporterRegistration;
use hana_rigging::prelude::RequestedConfiguration;
use hana_rigging::prelude::ResolvedBindings;
use hana_rigging::prelude::ResolvedToDevice;
use hana_rigging::prelude::RetryOn;
use hana_rigging::prelude::RiggingAppExt;
use hana_rigging::prelude::RiggingPlugin;
use hana_rigging::prelude::RiggingRevision;
use hana_rigging::prelude::RoleAvailable;
use hana_rigging::prelude::RoleAwaiting;
use hana_rigging::prelude::RoleKey;
use hana_rigging::prelude::RoleState;
use hana_rigging::prelude::RoleStateChanged;
use hana_rigging::prelude::SchemeName;
const COLOR_MANAGEMENT_REPORTER: &str = "color-management";
const PANEL_ROLE: &str = "primary-window";
const DESK_MONITOR: &str = "DESK-4K-0002";
const DISPLAY_SCHEME: &str = "example-edid-serial";
const FRAME_CEILING: u32 = 64;
const SCANS_BEFORE_UNPLUG: u32 = 2;
const SHARED_PANEL: &str = "BUILT-IN-PANEL-0001";
const STAGE_PROJECTOR: &str = "STAGE-PROJECTOR-0003";
const WINDOW_SYSTEM_REPORTER: &str = "window-system";
struct FixedSetReporter {
reported_keys: Vec<DeviceKey>,
withdrawal: KeyWithdrawal,
completed_scans: u32,
}
enum KeyWithdrawal {
Never,
AfterScans {
after_scans: u32,
key: DeviceKey,
},
}
impl DeviceReporter for FixedSetReporter {
fn discover(&mut self) -> DiscoveryWork {
self.completed_scans += 1;
let mut reported_keys = self.reported_keys.clone();
if let KeyWithdrawal::AfterScans { after_scans, key } = &self.withdrawal
&& self.completed_scans > *after_scans
{
reported_keys.retain(|reported_key| reported_key != key);
}
DiscoveryWork::Immediate(MainThreadDiscoveryJob::new(move |_: &mut World| {
DeviceScan::Complete(reported_keys.into_iter().map(present_display).collect())
}))
}
}
struct PanelDriver;
impl EndpointDriver for PanelDriver {
type Configuration = PanelPlacement;
fn capture(
&mut self,
_: &mut World,
_: &DeviceEndpoint,
) -> CaptureOutcome<Self::Configuration> {
CaptureOutcome::NotReadable
}
fn start_apply(
&mut self,
world: &mut World,
_: &DeviceEndpoint,
configuration: &Self::Configuration,
_: AttemptId,
_: ApplyPermit,
) {
world.resource_mut::<AppliedPlacements>().0.push(format!(
"left {} top {}",
configuration.left_pixels, configuration.top_pixels
));
}
fn poll(&mut self, _: &mut World, _: AttemptId) -> AttemptProgress {
AttemptProgress::Finished(AttemptOutcome::Succeeded)
}
}
#[derive(Default, Resource)]
struct AppliedPlacements(Vec<String>);
struct ObservedAttemptEnding {
role: RoleKey,
attempt: AttemptId,
outcome: AttemptOutcome,
}
#[derive(Default, Resource)]
struct ObservedAttemptEndings(Vec<ObservedAttemptEnding>);
fn observe_attempt_ending(
attempt_finished: On<AttemptFinished>,
mut observed_attempt_endings: ResMut<ObservedAttemptEndings>,
) {
observed_attempt_endings.0.push(ObservedAttemptEnding {
role: attempt_finished.role.clone(),
attempt: attempt_finished.attempt,
outcome: attempt_finished.outcome.clone(),
});
}
struct ObservedLifecycleEvent {
axis: &'static str,
moved: String,
}
#[derive(Default, Resource)]
struct ObservedLifecycle(Vec<ObservedLifecycleEvent>);
impl ObservedLifecycle {
fn record(&mut self, axis: &'static str, moved: String) {
self.0.push(ObservedLifecycleEvent { axis, moved });
}
fn saw(&self, axis: &str) -> bool {
self.0
.iter()
.any(|observed_lifecycle_event| observed_lifecycle_event.axis == axis)
}
}
fn observe_device_arrived(
device_arrived: On<DeviceArrived>,
mut observed_lifecycle: ResMut<ObservedLifecycle>,
) {
let moved = describe_key(&device_arrived.key);
observed_lifecycle.record("arrival", moved);
}
fn observe_presence_changed(
presence_changed: On<PresenceChanged>,
mut observed_lifecycle: ResMut<ObservedLifecycle>,
) {
let moved = format!("{:?}", presence_changed.presence);
observed_lifecycle.record("presence", moved);
}
fn observe_claim_changed(
claim_changed: On<ClaimChanged>,
mut observed_lifecycle: ResMut<ObservedLifecycle>,
) {
let moved = format!("{:?}", claim_changed.claim);
observed_lifecycle.record("claim", moved);
}
fn observe_identity_changed(
identity_changed: On<IdentityChanged>,
mut observed_lifecycle: ResMut<ObservedLifecycle>,
) {
let moved = format!("{:?}", identity_changed.verdict);
observed_lifecycle.record("identity", moved);
}
fn observe_device_departed(
device_departed: On<DeviceDeparted>,
mut observed_lifecycle: ResMut<ObservedLifecycle>,
) {
let moved = format!(
"{} | {:?}",
describe_key(&device_departed.key),
device_departed.departure
);
observed_lifecycle.record("departure", moved);
}
fn observe_connection_changed(
connection_changed: On<ConfiguredDeviceConnectionChanged>,
mut observed_lifecycle: ResMut<ObservedLifecycle>,
) {
let moved = format!(
"{} | {:?}",
describe_key(&connection_changed.key),
connection_changed.connection
);
observed_lifecycle.record("authored connection", moved);
}
fn observe_role_state_changed(
role_state_changed: On<RoleStateChanged>,
mut observed_lifecycle: ResMut<ObservedLifecycle>,
) {
let moved = format!(
"role `{}` | {:?}",
role_state_changed.role, role_state_changed.state
);
observed_lifecycle.record("role state", moved);
}
fn observe_role_awaiting(
role_awaiting: On<RoleAwaiting>,
mut observed_lifecycle: ResMut<ObservedLifecycle>,
) {
let moved = format!("role `{}` has no live device", role_awaiting.role);
observed_lifecycle.record("role availability", moved);
}
fn observe_role_available(
role_available: On<RoleAvailable>,
mut observed_lifecycle: ResMut<ObservedLifecycle>,
) {
let moved = format!("role `{}` resolved to a live device", role_available.role);
observed_lifecycle.record("role availability", moved);
}
enum AttemptRun {
Succeeded { frames: u32 },
CeilingReached,
}
#[derive(Component, Reflect)]
#[reflect(Component)]
struct PanelPlacement {
left_pixels: i32,
top_pixels: i32,
}
struct NamedReporter {
id: ReporterId,
name: &'static str,
}
struct ReportedDisplay {
key: DeviceKey,
label: &'static str,
expected_contributors: usize,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum ScanCoverage {
Pending,
EveryReporterCompleted,
}
enum ReporterStartup {
Completed { frames: u32 },
CeilingReached,
}
enum SmokeCheck {
Matched,
Mismatched(Vec<String>),
}
fn main() -> ExitCode {
match run() {
Ok(SmokeCheck::Matched) => {
println!(
"OK — three reconciled devices, the shared panel carries two contributing \
reporters and a live ResolvedToDevice link, no duplicate keys, the panel role's \
apply attempt succeeded and left the role Ready, and the withdrawn desk monitor \
departed and left its authored inventory entry reading Absent"
);
ExitCode::SUCCESS
},
Ok(SmokeCheck::Mismatched(mismatches)) => {
println!("FAILED — {}", mismatches.join("; "));
ExitCode::FAILURE
},
Err(error) => {
println!("FAILED — the kernel rejected this example's setup: {error}");
ExitCode::FAILURE
},
}
}
fn reported_displays(
shared_panel: DeviceKey,
desk_monitor: DeviceKey,
stage_projector: DeviceKey,
) -> Vec<ReportedDisplay> {
vec![
ReportedDisplay {
key: shared_panel,
label: "built-in panel (reported by both)",
expected_contributors: 2,
},
ReportedDisplay {
key: desk_monitor,
label: "desk monitor (window-system only)",
expected_contributors: 1,
},
ReportedDisplay {
key: stage_projector,
label: "stage projector (color-management only)",
expected_contributors: 1,
},
]
}
fn run() -> Result<SmokeCheck, Box<dyn Error>> {
let shared_panel = reported_display_key(SHARED_PANEL)?;
let desk_monitor = reported_display_key(DESK_MONITOR)?;
let stage_projector = reported_display_key(STAGE_PROJECTOR)?;
let mut app = App::new();
app.add_plugins(MinimalPlugins)
.add_plugins(RiggingPlugin)
.register_device_scheme(SchemeName::new(DISPLAY_SCHEME)?)
.init_resource::<AppliedPlacements>()
.init_resource::<ObservedAttemptEndings>()
.init_resource::<ObservedLifecycle>()
.add_observer(observe_attempt_ending)
.add_observer(observe_device_arrived)
.add_observer(observe_presence_changed)
.add_observer(observe_claim_changed)
.add_observer(observe_identity_changed)
.add_observer(observe_device_departed)
.add_observer(observe_connection_changed)
.add_observer(observe_role_state_changed)
.add_observer(observe_role_awaiting)
.add_observer(observe_role_available);
let panel_role = RoleKey::new(PANEL_ROLE)?;
let panel_driver = app.add_endpoint_driver(PanelDriver);
app.world_mut()
.resource_mut::<Bindings>()
.register(panel_binding(
panel_role.clone(),
shared_panel.clone(),
panel_driver,
))?;
app.world_mut()
.resource_mut::<HardwareInventory>()
.configure(ConfiguredDevice {
key: desk_monitor.clone(),
mode: ConfiguredDeviceMode::Managed,
});
let reporters = vec![
NamedReporter {
name: WINDOW_SYSTEM_REPORTER,
id: app.add_device_reporter(
FixedSetReporter {
reported_keys: vec![shared_panel.clone(), desk_monitor.clone()],
withdrawal: KeyWithdrawal::AfterScans {
after_scans: SCANS_BEFORE_UNPLUG,
key: desk_monitor.clone(),
},
completed_scans: 0,
},
on_demand_registration()?,
),
},
NamedReporter {
name: COLOR_MANAGEMENT_REPORTER,
id: app.add_device_reporter(
FixedSetReporter {
reported_keys: vec![shared_panel.clone(), stage_projector.clone()],
withdrawal: KeyWithdrawal::Never,
completed_scans: 0,
},
on_demand_registration()?,
),
},
];
let displays = reported_displays(shared_panel, desk_monitor.clone(), stage_projector);
for named_reporter in &reporters {
println!(
"registered reporter: {} {:?}",
named_reporter.name, named_reporter.id
);
}
match run_until_reporters_complete(&mut app, &reporters) {
ReporterStartup::CeilingReached => Ok(SmokeCheck::Mismatched(vec![format!(
"reached the {FRAME_CEILING}-frame ceiling before every reporter completed a scan"
)])),
ReporterStartup::Completed { frames } => {
println!("frames until every reporter completed a scan: {frames}");
let devices = app.world().resource::<Devices>();
print_reconciled_devices(devices, &displays, &reporters);
println!(
"rigging revision: {}",
app.world().resource::<RiggingRevision>().get()
);
let mut smoke_check = check_reconciled(devices, &displays);
print_binding(app.world(), &panel_role, &mut smoke_check);
print_connection(app.world(), &desk_monitor, "before the unplug");
report_attempt(&mut app, &panel_role, &mut smoke_check);
provoke_departure(
&mut app,
&reporters,
&desk_monitor,
&displays,
&mut smoke_check,
);
report_lifecycle(app.world(), &mut smoke_check);
Ok(smoke_check)
},
}
}
fn provoke_departure(
app: &mut App,
reporters: &[NamedReporter],
departing: &DeviceKey,
displays: &[ReportedDisplay],
smoke_check: &mut SmokeCheck,
) {
for frame in 1..=FRAME_CEILING {
request_one_scan(app, reporters, smoke_check);
app.update();
if app.world().resource::<Devices>().resolve(departing) == DeviceResolution::NotResolved {
println!("frames until the withdrawn key left the reconciled set: {frame}");
let remaining: Vec<&ReportedDisplay> = displays
.iter()
.filter(|display| &display.key != departing)
.collect();
let devices = app.world().resource::<Devices>();
println!(
"reconciled devices after the departure: {}",
devices.count()
);
if devices.count() != remaining.len() {
record_mismatch(
smoke_check,
format!(
"expected {} reconciled devices after the departure, kernel retained {}",
remaining.len(),
devices.count()
),
);
}
print_connection(app.world(), departing, "after the unplug");
let connection = app
.world()
.resource::<HardwareInventory>()
.connection(departing);
if connection != Ok(ConfiguredDeviceConnection::Absent) {
record_mismatch(
smoke_check,
format!(
"expected the authored desk monitor to read Absent after the unplug, got \
{connection:?}"
),
);
}
return;
}
}
record_mismatch(
smoke_check,
format!(
"reached the {FRAME_CEILING}-frame ceiling before the withdrawn key left the \
reconciled set"
),
);
}
fn report_attempt(app: &mut App, role: &RoleKey, smoke_check: &mut SmokeCheck) {
let AttemptRun::Succeeded { frames } = run_until_attempt_succeeded(app) else {
record_mismatch(
smoke_check,
format!(
"reached the {FRAME_CEILING}-frame ceiling before an apply on role `{role}` \
succeeded"
),
);
return;
};
println!("frames until an apply on the panel role succeeded: {frames}");
println!(
"placements dispatched to the driver: {}",
app.world().resource::<AppliedPlacements>().0.join(", ")
);
for observed_attempt_ending in &app.world().resource::<ObservedAttemptEndings>().0 {
println!(
" attempt ending | role `{}` | {:?} | {:?}",
observed_attempt_ending.role,
observed_attempt_ending.attempt,
observed_attempt_ending.outcome
);
}
let succeeded = app
.world()
.resource::<ObservedAttemptEndings>()
.0
.iter()
.find(|observed_attempt_ending| {
observed_attempt_ending.outcome == AttemptOutcome::Succeeded
})
.map(|observed_attempt_ending| observed_attempt_ending.role.clone());
match succeeded {
None => record_mismatch(
smoke_check,
format!("role `{role}`'s successful attempt never reached its binding entity"),
),
Some(succeeded_role) if succeeded_role != *role => record_mismatch(
smoke_check,
format!(
"expected the successful attempt to name role `{role}`, got `{succeeded_role}`"
),
),
Some(_) => {},
}
if app.world().resource::<AppliedPlacements>().0.is_empty() {
record_mismatch(
smoke_check,
format!("role `{role}` reported a successful apply the driver was never handed"),
);
}
match app.world().resource::<Bindings>().binding(role) {
Ok(binding) if binding.state == RoleState::Ready => {},
Ok(binding) => record_mismatch(
smoke_check,
format!(
"expected role `{role}` to be Ready after a successful apply, got {:?}",
binding.state
),
),
Err(error) => record_mismatch(
smoke_check,
format!("role `{role}` is no longer bound after its apply: {error}"),
),
}
}
fn run_until_attempt_succeeded(app: &mut App) -> AttemptRun {
for frame in 0..FRAME_CEILING {
if app
.world()
.resource::<ObservedAttemptEndings>()
.0
.iter()
.any(|observed_attempt_ending| {
observed_attempt_ending.outcome == AttemptOutcome::Succeeded
})
{
return AttemptRun::Succeeded { frames: frame };
}
app.update();
}
AttemptRun::CeilingReached
}
fn request_one_scan(app: &mut App, reporters: &[NamedReporter], smoke_check: &mut SmokeCheck) {
let mut discovery_control = app.world_mut().resource_mut::<DiscoveryControl>();
for named_reporter in reporters {
if let Err(error) = discovery_control.request(named_reporter.id) {
record_mismatch(
smoke_check,
format!(
"the kernel refused a discovery run for reporter {}: {error}",
named_reporter.name
),
);
}
}
}
fn print_connection(world: &World, device_key: &DeviceKey, when: &str) {
match world.resource::<HardwareInventory>().connection(device_key) {
Ok(connection) => println!(
" authored inventory {} | {when} | {connection:?}",
describe_key(device_key)
),
Err(error) => println!(" authored inventory read failed {when}: {error}"),
}
}
fn panel_binding(role: RoleKey, device: DeviceKey, driver: DriverId) -> Binding {
Binding {
role,
endpoint: DeviceEndpoint {
device,
id: EndpointId::Whole,
},
driver,
recovery: RecoveryPolicy::default(),
retry: RetryOn::NewRevision,
on_abort: OnAbort::default(),
on_loss: OnSessionLoss::default(),
state: RoleState::default(),
requested: RequestedConfiguration::new(PanelPlacement {
left_pixels: 0,
top_pixels: 0,
}),
last_known_good: LastKnownGoodConfiguration::default(),
apply_deadline: ApplyDeadline::ProcessDefault,
}
}
fn on_demand_registration() -> Result<ReporterRegistration, Box<dyn Error>> {
Ok(ReporterRegistration::required(
DiscoveryCadence::OnDemand,
ReporterCoverage::EstablishesAbsence(AuthoritativeReporterCoverage::one(
CoveredDeviceIdentitySpace::ReportedScheme {
kind: DeviceKind::Display,
scheme: SchemeName::new(DISPLAY_SCHEME)?,
},
)),
))
}
fn reported_display_key(value: &str) -> Result<DeviceKey, Box<dyn Error>> {
Ok(DeviceKey {
kind: DeviceKind::Display,
id: DeviceIdSource::Reported {
scheme: SchemeName::new(DISPLAY_SCHEME)?,
value: ReportedId::new(value)?,
},
})
}
fn present_display(device_key: DeviceKey) -> DeviceRecord {
DeviceRecord {
reported_as: ReportedAs::Keyed(device_key),
parent: ReportedParent::Root,
presence: Presence::Present,
claim: Claim::NotApplicable,
capabilities: Capabilities::new(),
serial: ReportedSerial::NotExposedByUnit,
platform_device_handle: PlatformDeviceHandle::PlatformReportedNothing,
attachment: AttachmentPath::PlatformHasNoConcept,
descriptor: DeviceDescriptor::PlatformReportedNothing,
}
}
fn run_until_reporters_complete(app: &mut App, reporters: &[NamedReporter]) -> ReporterStartup {
for frame in 1..=FRAME_CEILING {
app.update();
if scan_coverage(app.world(), reporters) == ScanCoverage::EveryReporterCompleted {
return ReporterStartup::Completed { frames: frame };
}
}
ReporterStartup::CeilingReached
}
fn scan_coverage(world: &World, reporters: &[NamedReporter]) -> ScanCoverage {
let discovery_status = world.resource::<DiscoveryStatus>();
for named_reporter in reporters {
match discovery_status.reporter_status(named_reporter.id) {
Ok(reporter_discovery_status)
if matches!(
reporter_discovery_status.last_outcome,
LastDiscoveryOutcome::Succeeded { .. }
) => {},
Ok(_) => return ScanCoverage::Pending,
Err(error) => {
println!("reporter status unavailable: {error}");
return ScanCoverage::Pending;
},
}
}
ScanCoverage::EveryReporterCompleted
}
fn print_reconciled_devices(
devices: &Devices,
displays: &[ReportedDisplay],
reporters: &[NamedReporter],
) {
println!("reconciled devices: {}", devices.count());
for display in displays {
let key = describe_key(&display.key);
match devices.resolve(&display.key) {
DeviceResolution::NotResolved => {
println!(" {key} | {} | no device handle was issued", display.label);
},
DeviceResolution::Resolved(device_id) => match devices.state(device_id) {
DeviceStateLookup::Retired => {
println!(
" {key} | {} | DeviceId({}) | retired",
display.label,
device_id.get()
);
},
DeviceStateLookup::Retained(reconciled_device_state) => {
println!(
" {key} | {} | DeviceId({}) | {:?} | {:?} | contributors: {}",
display.label,
device_id.get(),
reconciled_device_state.presence,
reconciled_device_state.verdict,
contributor_names(&reconciled_device_state.contributors, reporters)
);
},
},
}
}
let duplicate_keys = devices.duplicate_keys();
if duplicate_keys.is_empty() {
println!("duplicate keys: none");
} else {
for duplicate_key in duplicate_keys {
println!("duplicate key: {}", describe_key(duplicate_key));
}
}
}
fn check_reconciled(devices: &Devices, displays: &[ReportedDisplay]) -> SmokeCheck {
let mut mismatches = Vec::new();
if devices.count() != displays.len() {
mismatches.push(format!(
"expected {} reconciled devices, kernel retained {}",
displays.len(),
devices.count()
));
}
for display in displays {
let contributors = match devices.resolve(&display.key) {
DeviceResolution::NotResolved => {
mismatches.push(format!("{} resolved to no device", display.label));
continue;
},
DeviceResolution::Resolved(device_id) => match devices.state(device_id) {
DeviceStateLookup::Retired => {
mismatches.push(format!("{} resolved to a retired handle", display.label));
continue;
},
DeviceStateLookup::Retained(reconciled_device_state) => {
reconciled_device_state.contributors.len()
},
},
};
if contributors != display.expected_contributors {
mismatches.push(format!(
"{} expected {} contributing reporters, got {contributors}",
display.label, display.expected_contributors
));
}
}
if !devices.duplicate_keys().is_empty() {
mismatches.push(format!(
"expected no duplicate keys, kernel reported {}",
devices.duplicate_keys().len()
));
}
if mismatches.is_empty() {
SmokeCheck::Matched
} else {
SmokeCheck::Mismatched(mismatches)
}
}
fn print_binding(world: &World, role: &RoleKey, smoke_check: &mut SmokeCheck) {
let binding_entities = world.resource::<BindingEntities>();
println!("binding entities: {}", binding_entities.count());
match binding_entities.entity(role) {
BindingEntityLookup::Unregistered => {
record_mismatch(
smoke_check,
format!("role `{role}` has no binding entity after registration"),
);
},
BindingEntityLookup::Registered(entity) => {
match (
world.get::<RecoveryPolicy>(entity),
world.get::<RoleState>(entity),
) {
(Some(recovery_policy), Some(role_state)) => {
println!(" role `{role}` | {entity} | {recovery_policy:?} | {role_state:?}");
},
_ => record_mismatch(
smoke_check,
format!(
"role `{role}`'s binding entity is missing a mirrored recovery policy or \
role state"
),
),
}
match world.get::<ResolvedToDevice>(entity) {
None => record_mismatch(
smoke_check,
format!(
"role `{role}` has no ResolvedToDevice link even though its endpoint names \
a device the kernel retains"
),
),
Some(resolved_to_device) => {
let device = resolved_to_device.get();
let resolved_bindings = world
.get::<ResolvedBindings>(device)
.map_or(0, RelationshipTarget::len);
println!(
" role `{role}` | ResolvedToDevice({device}) | that device carries \
{resolved_bindings} resolved binding(s)"
);
},
}
},
}
}
fn report_lifecycle(world: &World, smoke_check: &mut SmokeCheck) {
println!("kernel lifecycle events, in arrival order:");
let observed_lifecycle = world.resource::<ObservedLifecycle>();
for observed_lifecycle_event in &observed_lifecycle.0 {
println!(
" {} | {}",
observed_lifecycle_event.axis, observed_lifecycle_event.moved
);
}
for axis in ["arrival", "presence", "role state", "departure"] {
if !observed_lifecycle.saw(axis) {
record_mismatch(
smoke_check,
format!("no {axis} event reached an observer during this run"),
);
}
}
}
fn record_mismatch(smoke_check: &mut SmokeCheck, mismatch: String) {
match smoke_check {
SmokeCheck::Matched => *smoke_check = SmokeCheck::Mismatched(vec![mismatch]),
SmokeCheck::Mismatched(mismatches) => mismatches.push(mismatch),
}
}
fn contributor_names(contributors: &[ReporterId], reporters: &[NamedReporter]) -> String {
contributors
.iter()
.map(|contributor| {
reporters
.iter()
.find(|named_reporter| named_reporter.id == *contributor)
.map_or_else(
|| format!("{contributor:?}"),
|named_reporter| format!("{} {contributor:?}", named_reporter.name),
)
})
.collect::<Vec<_>>()
.join(", ")
}
fn describe_key(device_key: &DeviceKey) -> String {
match &device_key.id {
DeviceIdSource::Reported { scheme, value } => {
format!(
"{:?} {}:{}",
device_key.kind,
scheme.as_str(),
value.as_str()
)
},
DeviceIdSource::Synthesized { digest } => {
format!("{:?} synthesized:{digest:?}", device_key.kind)
},
DeviceIdSource::Authored { value } => {
format!("{:?} authored:{}", device_key.kind, value.as_str())
},
}
}