Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

53 changes: 51 additions & 2 deletions crates/labcolors-core/src/generic_boundary_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,11 @@ fn shared_observation_ssot_has_one_backing_without_lifecycle_or_adapter_facades(
"production must have exactly one generic revision-bound Session owner",
);
for required in [
"type OwnerLease;",
"type Verified: SessionEvidenceV1;",
"type Violation: SessionEvidenceV1;",
"fn try_acquire_owner(&self) -> Option<Self::OwnerLease>;",
"SessionUpdateError::OwnerExpired",
".is_same_binding_as(expected_observation)",
"SessionUpdateError::EvidenceBindingInvariant",
] {
Expand Down Expand Up @@ -239,14 +242,38 @@ fn shared_observation_ssot_has_one_backing_without_lifecycle_or_adapter_facades(
"into_session_recheck",
"ObservationStreamBinding",
"ProgramExpired",
"Weak<",
] {
assert!(
!source.contains(forbidden),
"{path} must not restore a second owner or adapter `{forbidden}`",
);
}
}
for (path, source) in [
("session.rs", SESSION_SOURCE),
("point_support.rs", POINT_SUPPORT_SOURCE),
] {
assert!(
!source.contains("Weak<"),
"{path} must not create another weak ownership boundary",
);
}

let update = normalized_source_scope(
SESSION_SOURCE,
"pub(crate) fn update(",
"/// Move exactly one retained verified witness",
);
let owner_preflight = update
.find(".try_acquire_owner()")
.expect("Session update must acquire the exact owner generation");
let admission = update
.find("prepare_observation(")
.expect("Session update must perform canonical admission");
assert!(
owner_preflight < admission,
"owner expiry must precede raw admission and physical execution",
);

let consuming_entry = source_scope(
POINT_SUPPORT_SOURCE,
Expand Down Expand Up @@ -419,9 +446,31 @@ fn program_session_owns_context_bound_lcs_evidence_and_one_session_scratch_cache

let plan = source_scope(
PROGRAM_SESSION_SOURCE,
"pub struct ProgramSessionPlan<Evaluation>",
"pub(crate) struct ProgramSessionPlan<Evaluation>",
"impl<Evaluation> session_private::PlanSealed for ProgramSessionPlan<Evaluation>",
);
assert_eq!(
plan.matches("owner_generation: Weak<ProgramEpochV1<Evaluation>>,")
.count(),
1,
"a Program Session must hold exactly one weak compiled-generation binding",
);
assert!(
!plan.contains("epoch: Rc<ProgramEpochV1<Evaluation>>,"),
"a Program Session must not prolong its CompiledProgram owner",
);
let compiled = source_scope(
PROGRAM_SESSION_SOURCE,
"pub struct CompiledProgram<Evaluation>",
"impl<Evaluation> CompiledProgram<Evaluation>",
);
assert_eq!(
compiled
.matches("owner_generation: Rc<ProgramEpochV1<Evaluation>>,")
.count(),
1,
"CompiledProgram must be the one strong owner of its generation",
);
assert_eq!(
plan.matches("modeled_occurrences: Vec<Option<ModeledLcsOccurrenceV1>>,")
.count(),
Expand Down
6 changes: 6 additions & 0 deletions crates/labcolors-core/src/point_support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,16 +322,22 @@ impl CompiledPointSupportRecheckV1 {
impl session_private::PlanSealed for CompiledPointSupportRecheckV1 {}

impl SessionPlanV1 for CompiledPointSupportRecheckV1 {
type OwnerLease = ();
type Verified = VerifiedPointSupportV1;
type Violation = PointSupportViolationV1;
type Error = PointSupportEvaluationErrorV1;

fn try_acquire_owner(&self) -> Option<Self::OwnerLease> {
Some(())
}

fn observation_schema(&self) -> &CanonicalObservationSchemaV1 {
&self.surface_schema
}

fn evaluate(
&mut self,
_owner: &Self::OwnerLease,
observation: RevisionBoundObservationV1,
_permit: SessionObservationBindingPermitV1,
) -> Result<SessionDecision<Self::Verified, Self::Violation>, Self::Error> {
Expand Down
108 changes: 106 additions & 2 deletions crates/labcolors-core/src/program_joint_integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ use crate::lcs_occurrence::{
BackgroundLuminanceRatio, ColorSignal, IEC_SRGB_D65_XYZ_FRAME_V1, SurroundProfileId,
};
use crate::observation::{
ObservationGroupId, ObservationPayloadInput, ObservationStreamId, ObservationUpdateInput,
ObservedScenarioSetInput, Revision, ScenarioId, ScenarioInput, SurfaceInputBinding,
ObservationGroupId, ObservationHeadViewV1, ObservationPayloadInput, ObservationStreamId,
ObservationUpdateInput, ObservedScenarioSetInput, Revision, ScenarioId, ScenarioInput,
SurfaceInputBinding,
};
use crate::program_session::{
CompositionProfile, ConstraintId, ConstraintInvocation, ConstraintSet,
Expand Down Expand Up @@ -1077,6 +1078,109 @@ fn every_fallible_fixed_preflight_reservation_precedes_evaluator_work() {
}
}

fn counting_fixed_program(
evaluator: CountingProgramWcag22Srgb8V1,
) -> crate::program_session::CompiledProgram<CountingProgramWcag22Srgb8V1> {
Program::new(
vec![Source::new(SOURCE, signal(0xFF))],
vec![Target::fixed(TARGET, SOURCE)],
ObservationGroup::new(GROUP, vec![SURFACE_PORT]),
vec![],
vec![Paint::Solid {
id: PAINT,
target: TARGET,
}],
vec![Surface::Input {
id: BACKDROP,
input: SURFACE_PORT,
}],
vec![Occurrence::new(
OCCURRENCE,
PAINT,
BACKDROP,
CompositionProfile::EncodedSrgb8SourceOverV1,
appearance_context(),
)],
ConstraintSet::new(
vec![ConstraintInvocation::hard(
ConstraintId::new(1),
OCCURRENCE,
Wcag22CriterionV1::Sc143TextLargeScale,
)],
vec![],
),
vec![OutputBinding::new(OUTPUT, PAINT)],
evaluator,
)
.compile()
.unwrap()
}

#[test]
fn expired_program_generation_precedes_composition_and_evaluation_without_allocation() {
let evaluator = CountingProgramWcag22Srgb8V1::default();
let calls = evaluator.clone();
let compiled = counting_fixed_program(evaluator);
let mut session = compiled.instantiate(STREAM).unwrap();

crate::composition::reset_source_over_evaluation_count();
let SessionState::Ready { current } = session.update(update(1, 0x00)).unwrap() else {
panic!("control generation must certify");
};
assert_eq!(current.report().observation().revision(), Revision::new(1));
assert_eq!(calls.calls().len(), 1);
assert_eq!(crate::composition::source_over_evaluation_count(), 1);

drop(compiled);
let expired_update = update(2, 0x00);
let (error, allocations) = crate::test_support::measured_allocations(|| {
session.update(expired_update).map(|_| ()).unwrap_err()
});
assert_eq!(error, SessionUpdateError::OwnerExpired);
assert_eq!(allocations, 0);
assert_eq!(calls.calls().len(), 1);
assert_eq!(crate::composition::source_over_evaluation_count(), 1);
assert_eq!(session.raw_head().revision(), Some(Revision::new(1)));
let SessionState::Ready { current } = session.state() else {
panic!("expiry must retain the previous committed state");
};
assert_eq!(current.report().observation().revision(), Revision::new(1));
}

#[test]
fn equivalent_recompiled_owner_is_a_new_generation_and_cannot_revive_old_sessions() {
let first_evaluator = CountingProgramWcag22Srgb8V1::default();
let first_calls = first_evaluator.clone();
let mut compiled = counting_fixed_program(first_evaluator);
let mut old_session = compiled.instantiate(STREAM).unwrap();
assert!(matches!(
old_session.update(update(1, 0x00)).unwrap(),
SessionState::Ready { .. }
));

let replacement_evaluator = CountingProgramWcag22Srgb8V1::default();
let replacement_calls = replacement_evaluator.clone();
compiled = counting_fixed_program(replacement_evaluator);
assert!(matches!(
old_session.update(update(2, 0x00)),
Err(SessionUpdateError::OwnerExpired),
));
assert_eq!(first_calls.calls().len(), 1);
assert!(replacement_calls.calls().is_empty());
assert_eq!(old_session.raw_head().revision(), Some(Revision::new(1)));

let mut replacement_session = compiled.instantiate(STREAM).unwrap();
assert!(matches!(
replacement_session.update(update(1, 0x00)).unwrap(),
SessionState::Ready { .. }
));
assert_eq!(replacement_calls.calls().len(), 1);
assert!(matches!(
replacement_session.raw_head(),
ObservationHeadViewV1::Observed(_)
));
}

#[test]
fn final_recheck_violation_is_typed_and_retains_the_previous_certificate() {
let evaluator = FinalRecheckMutantProgramEvaluatorV1::default();
Expand Down
Loading
Loading