diff --git a/src/backends/isolation_session/common/src/availability.rs b/src/backends/isolation_session/common/src/availability.rs index d8226c45f..0bd3d65e9 100644 --- a/src/backends/isolation_session/common/src/availability.rs +++ b/src/backends/isolation_session/common/src/availability.rs @@ -3,13 +3,13 @@ //! IsolationSession host-availability probe. //! -//! Availability is whether the in-proc `Windows.AI.IsolationSession` -//! `IsoSessionOps` WinRT class is registered on the OS (its activation factory -//! resolves), matching PR #761 rather than gating on a Windows build number. +//! Available when `IsoSessionOps` activates and reports a feature level above +//! zero for `LocalAgentUser`, which the lifecycle requires. No build number is +//! consulted. use std::sync::OnceLock; -use isolation_session_bindings::bindings::IsoSessionOps; +use isolation_session_bindings::bindings::{IsoSessionFeature, IsoSessionOps}; use windows::Win32::System::Com::{CoInitializeEx, CoUninitialize, COINIT_MULTITHREADED}; use windows_core::HRESULT; @@ -17,25 +17,23 @@ static AVAILABLE: OnceLock = OnceLock::new(); /// Cached for the process; never requires elevation. pub fn is_isolation_session_available() -> bool { - *AVAILABLE.get_or_init(|| available_from(probe_activation())) + *AVAILABLE.get_or_init(|| available_from(probe_feature_level())) } -/// Split from [`probe_activation`] so the decision is testable without COM/WinRT. -/// Any activation failure (class not registered, or the OS feature gate off) -/// means not available here. -fn available_from(activation: Result<(), HRESULT>) -> bool { - activation.is_ok() +/// Split from [`probe_feature_level`] so the decision is testable without +/// COM/WinRT. A failed probe or a zero level means not available. +fn available_from(probe: Result) -> bool { + matches!(probe, Ok(level) if level > 0) } -fn probe_activation() -> Result<(), HRESULT> { +fn probe_feature_level() -> Result { // Guard uninitializes on drop, so a panic in `IsoSessionOps::new()` still - // balances `CoInitializeEx`. The `_ops` handle drops before `_apartment` + // balances `CoInitializeEx`. The `ops` handle drops before `_apartment` // (reverse declaration order), preserving COM's create-before-uninit rule. let _apartment = ComApartment::enter(); - match IsoSessionOps::new() { - Ok(_ops) => Ok(()), - Err(e) => Err(e.code()), - } + let ops = IsoSessionOps::new().map_err(|e| e.code())?; + ops.GetFeatureLevel(IsoSessionFeature::LocalAgentUser) + .map_err(|e| e.code()) } /// Owns the COM apartment for the duration of a probe and uninitializes it on @@ -71,8 +69,11 @@ mod tests { use windows::Win32::Foundation::{CLASS_E_CLASSNOTAVAILABLE, REGDB_E_CLASSNOTREG}; #[test] - fn only_successful_activation_means_available() { - assert!(available_from(Ok(()))); + fn availability_needs_a_positive_feature_level() { + assert!(available_from(Ok(1))); + assert!(!available_from(Ok(0))); + // Pins the predicate as `> 0` rather than `!= 0`. + assert!(!available_from(Ok(-1))); assert!(!available_from(Err(CLASS_E_CLASSNOTAVAILABLE))); assert!(!available_from(Err(REGDB_E_CLASSNOTREG))); assert!(!available_from(Err(HRESULT(0x8000_4005u32 as i32)))); diff --git a/tests/scripts/README.md b/tests/scripts/README.md index f3e3d534a..62887713b 100644 --- a/tests/scripts/README.md +++ b/tests/scripts/README.md @@ -97,9 +97,7 @@ node scripts/ci/resolve-validation-test-matrix.mjs --plan nightly **Skip semantics.** Several suites degrade gracefully on an unsupported host: the IsolationSession suites decide availability from a single `wxc-exec --probe` -read of `probes.isolationSessionAvailable` (covering both a host that cannot -activate the API and a binary built without `--features isolation_session`), -print `SKIPPED`, and exit 0. +read of `probes.isolationSessionAvailable`, print `SKIPPED`, and exit 0. Because a skip exits 0 and the dispatchers propagate only the exit code, **a green CI job does not by itself prove the suite ran.** Anything treating these diff --git a/tests/scripts/run_isolation_session_resize_smoke.ps1 b/tests/scripts/run_isolation_session_resize_smoke.ps1 index ac1650c5e..5d40eae89 100644 --- a/tests/scripts/run_isolation_session_resize_smoke.ps1 +++ b/tests/scripts/run_isolation_session_resize_smoke.ps1 @@ -148,7 +148,7 @@ function Get-IsolationSessionProbe { $probeResult = Get-IsolationSessionProbe -Exe $WxcExec if ($probeResult.Status -eq 'unavailable') { - Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false (host cannot activate the isolation session API, or this binary was built without --features isolation_session)" -ForegroundColor Yellow + Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false" -ForegroundColor Yellow exit 0 } if ($probeResult.Status -ne 'available') { diff --git a/tests/scripts/run_isolation_session_state_aware_tests.ps1 b/tests/scripts/run_isolation_session_state_aware_tests.ps1 index 10ad8438d..2a7af4dd2 100644 --- a/tests/scripts/run_isolation_session_state_aware_tests.ps1 +++ b/tests/scripts/run_isolation_session_state_aware_tests.ps1 @@ -21,9 +21,7 @@ PowerShell on that host. Prerequisite probe (skip if unavailable -- not a failure): - - `wxc-exec --probe` reports `probes.isolationSessionAvailable`. This - single signal covers an OS that cannot activate the isolation session - API as well as a binary built without `--features isolation_session`. + - `wxc-exec --probe` reports `probes.isolationSessionAvailable`. .PARAMETER WxcExePath Path to wxc-exec.exe. Default probes the host-arch target dir, then @@ -148,7 +146,7 @@ function Get-IsolationSessionProbe { $probeResult = Get-IsolationSessionProbe -Exe $WxcExec if ($probeResult.Status -eq 'unavailable') { - Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false (host cannot activate the isolation session API, or this binary was built without --features isolation_session)" -ForegroundColor Yellow + Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false" -ForegroundColor Yellow exit 0 } if ($probeResult.Status -ne 'available') { diff --git a/tests/scripts/run_isolation_session_tests.ps1 b/tests/scripts/run_isolation_session_tests.ps1 index f63c239fe..c41f258fe 100644 --- a/tests/scripts/run_isolation_session_tests.ps1 +++ b/tests/scripts/run_isolation_session_tests.ps1 @@ -114,12 +114,7 @@ Write-Host "Configs: $ConfigDir`n" -ForegroundColor Gray # ---------------- Backend-availability probe ---------------- # # Availability is decided by a single call to `wxc-exec --probe`, which reports -# `probes.isolationSessionAvailable`. That one signal covers both reasons this -# suite can be unrunnable: -# -# - the host cannot activate the isolation session API, or -# - wxc-exec was built without `--features isolation_session`, in which case -# the field stays false because the backend is not compiled in. +# `probes.isolationSessionAvailable`. # # This deliberately replaces the earlier checks for a specific DLL and a # hard-coded WinRT activatable-class registry key. Those named a runtime class @@ -199,7 +194,7 @@ function Get-IsolationSessionProbe { $probeResult = Get-IsolationSessionProbe -Exe $WxcExec if ($probeResult.Status -eq 'unavailable') { - Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false (host cannot activate the isolation session API, or this binary was built without --features isolation_session)" -ForegroundColor Yellow + Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false" -ForegroundColor Yellow exit 0 } if ($probeResult.Status -ne 'available') {