1#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4pub enum FailureMode {
5 UnknownParent,
6 DuplicateParent,
7 InconsistentC3,
8 ParentCycle,
9 ReadConstructorMissing,
10 ReadShapeMismatch,
11}
12
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub enum ScenarioKind {
15 C3Resolution,
16 DeclaredParentTraversal,
17 SubclassTest,
18 ReadConstruction,
19 Failure(FailureMode),
20}
21
22#[derive(Clone, Copy, Debug, Eq, PartialEq)]
23pub enum ExpectedOutcome {
24 Linearization(&'static [&'static str]),
25 Traversal(&'static [&'static str]),
26 Subclass(bool),
27 Constructed(&'static str),
28 Rejected(&'static str),
29}
30
31#[derive(Clone, Copy, Debug, Eq, PartialEq)]
33pub struct CharacterizationScenario {
34 pub id: &'static str,
35 pub kind: ScenarioKind,
36 pub canonical: &'static str,
37 pub expected: ExpectedOutcome,
38}
39
40pub const fn characterization_scenarios() -> &'static [CharacterizationScenario] {
41 &[
42 CharacterizationScenario {
43 id: "characterize-1/c3-diamond",
44 kind: ScenarioKind::C3Resolution,
45 canonical: "class O(); class A(O); class B(O); class C(A,B)",
46 expected: ExpectedOutcome::Linearization(&["C", "A", "B", "O"]),
47 },
48 CharacterizationScenario {
49 id: "characterize-1/declared-parent-depth-first",
50 kind: ScenarioKind::DeclaredParentTraversal,
51 canonical: "class Root(); class Left(Root); class Right(Root); class Leaf(Left,Right)",
52 expected: ExpectedOutcome::Traversal(&["Left", "Root", "Right", "Root"]),
53 },
54 CharacterizationScenario {
55 id: "characterize-1/subclass-transitive",
56 kind: ScenarioKind::SubclassTest,
57 canonical: "class Root(); class Middle(Root); class Leaf(Middle); subclass(Leaf,Root)",
58 expected: ExpectedOutcome::Subclass(true),
59 },
60 CharacterizationScenario {
61 id: "characterize-1/subclass-unrelated",
62 kind: ScenarioKind::SubclassTest,
63 canonical: "class Left(); class Right(); subclass(Left,Right)",
64 expected: ExpectedOutcome::Subclass(false),
65 },
66 CharacterizationScenario {
67 id: "characterize-1/read-construction",
68 kind: ScenarioKind::ReadConstruction,
69 canonical: "class Point(x:i64,y:i64); read #(Point 3 5)",
70 expected: ExpectedOutcome::Constructed("Point{x=3,y=5}"),
71 },
72 CharacterizationScenario {
73 id: "characterize-1/fail-unknown-parent",
74 kind: ScenarioKind::Failure(FailureMode::UnknownParent),
75 canonical: "class Child(Missing)",
76 expected: ExpectedOutcome::Rejected("class/unknown-parent"),
77 },
78 CharacterizationScenario {
79 id: "characterize-1/fail-duplicate-parent",
80 kind: ScenarioKind::Failure(FailureMode::DuplicateParent),
81 canonical: "class Child(Base,Base)",
82 expected: ExpectedOutcome::Rejected("class/duplicate-parent"),
83 },
84 CharacterizationScenario {
85 id: "characterize-1/fail-inconsistent-c3",
86 kind: ScenarioKind::Failure(FailureMode::InconsistentC3),
87 canonical: "class A(X,Y); class B(Y,X); class Z(A,B)",
88 expected: ExpectedOutcome::Rejected("class/inconsistent-c3"),
89 },
90 CharacterizationScenario {
91 id: "characterize-1/fail-parent-cycle",
92 kind: ScenarioKind::Failure(FailureMode::ParentCycle),
93 canonical: "class A(B); class B(A)",
94 expected: ExpectedOutcome::Rejected("class/parent-cycle"),
95 },
96 CharacterizationScenario {
97 id: "characterize-1/fail-read-constructor-missing",
98 kind: ScenarioKind::Failure(FailureMode::ReadConstructorMissing),
99 canonical: "class Opaque(); read #(Opaque)",
100 expected: ExpectedOutcome::Rejected("class/read-constructor-missing"),
101 },
102 CharacterizationScenario {
103 id: "characterize-1/fail-read-shape",
104 kind: ScenarioKind::Failure(FailureMode::ReadShapeMismatch),
105 canonical: "class Point(x:i64,y:i64); read #(Point three 5)",
106 expected: ExpectedOutcome::Rejected("class/read-shape-mismatch"),
107 },
108 ]
109}
110
111pub fn scenario_content_id(scenario: &CharacterizationScenario) -> [u8; 32] {
113 let observation = format!("{:?}", scenario.expected);
114 let fields = [
115 b"sim.class-characterization/v1".as_slice(),
116 scenario.id.as_bytes(),
117 scenario.canonical.as_bytes(),
118 observation.as_bytes(),
119 ];
120 let mut output = [0_u8; 32];
121 for lane in 0..4 {
122 let mut hash = 0xcbf29ce484222325_u64 ^ (lane as u64).wrapping_mul(0x9e3779b97f4a7c15);
123 for field in fields {
124 for byte in field {
125 hash ^= u64::from(*byte);
126 hash = hash.wrapping_mul(0x100000001b3);
127 }
128 hash ^= 0xff;
129 hash = hash.wrapping_mul(0x100000001b3);
130 }
131 output[lane * 8..(lane + 1) * 8].copy_from_slice(&hash.to_be_bytes());
132 }
133 output
134}