Skip to content
Merged
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
4 changes: 2 additions & 2 deletions crates/workshop-rs/src/catalog/data/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -3992,7 +3992,7 @@
]
}
],
"digest": "4a4ad444069253758d1b6970f000226851c089a14d8393a7e3d9ebb23df9387a",
"digest": "174a4ebc3f1250485ea55605ebb885016d84897368f65f1ad3818f9789728060",
"enums": [
{
"domain": "Impulse",
Expand Down Expand Up @@ -9882,7 +9882,7 @@
"target": {
"format": "vanilla Workshop text",
"game": "Overwatch 2",
"surface": "M5 P0 set, documented in docs/provenance.md (mirrors wright docs/workshop/support-matrix.md)"
"surface": "Supported Workshop set, documented in docs/provenance.md (mirrors wright docs/workshop/support-matrix.md)"
},
"values": [
{
Expand Down
2 changes: 1 addition & 1 deletion crates/workshop-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ pub mod gameplay_query;
pub mod ids;
pub mod lexer;
pub mod live_capture;
pub mod p0;
pub mod parser;
pub mod real_projects;
pub mod roundtrip;
pub mod semantic;
pub mod settings;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Owner-controlled expectations for the pinned P0 Workshop corpus.
//! Owner-controlled expectations for the pinned real-project Workshop corpus.
//!
//! This is a test-support contract, not a conformance result or evidence
//! report. It contains only the current owner-defined input identities and
Expand All @@ -8,15 +8,15 @@
use crate::error::WorkshopError;
use crate::semantic::{IncompletenessKind, ResidualClassification, SemanticIssue};

/// The schema version of [`P0_EXPECTATION`].
pub const P0_EXPECTATION_SCHEMA_VERSION: u32 = 1;
/// The schema version of [`REAL_PROJECT_EXPECTATION`].
pub const REAL_PROJECT_EXPECTATION_SCHEMA_VERSION: u32 = 1;

/// The stable identity of the pinned P0 source corpus.
pub const P0_CORPUS_ID: &str = "raw-workshop-p0-corpus/v1";
/// The stable identity of the pinned real-project source corpus.
pub const REAL_PROJECT_CORPUS_ID: &str = "raw-workshop-real-projects/v1";

/// The stage at which an admitted P0 gap is observed.
/// The stage at which an admitted real-project gap is observed.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum P0Stage {
pub enum RealProjectStage {
/// Canonical builtin references are validated after parsing.
CanonicalValidation,
/// Canonical WIR is emitted back to Workshop text.
Expand All @@ -25,7 +25,7 @@ pub enum P0Stage {
LocaleConversion,
}

impl P0Stage {
impl RealProjectStage {
/// Return the stable machine-readable stage name.
pub const fn as_str(self) -> &'static str {
match self {
Expand All @@ -36,14 +36,14 @@ impl P0Stage {
}
}

/// The Workshop error identity admitted for a P0 stage gap.
/// The Workshop error identity admitted for a real-project stage gap.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum P0GapKind {
pub enum RealProjectGapKind {
/// An action spelling is not present in the canonical Workshop catalog.
UnknownAction,
}

impl P0GapKind {
impl RealProjectGapKind {
/// Return the stable machine-readable error kind name.
pub const fn as_str(self) -> &'static str {
match self {
Expand All @@ -52,9 +52,9 @@ impl P0GapKind {
}
}

/// An admitted semantic residual for one P0 source case.
/// An admitted semantic residual for one real-project source case.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct P0ResidualExpectation {
pub struct RealProjectResidualExpectation {
/// The semantic issue kind reported by [`crate::semantic::inspect`].
pub kind: IncompletenessKind,
/// The locale-independent Workshop identity of the residual.
Expand All @@ -63,22 +63,22 @@ pub struct P0ResidualExpectation {
pub classification: ResidualClassification,
}

/// An admitted Workshop error for one P0 processing stage.
/// An admitted Workshop error for one real-project processing stage.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct P0GapExpectation {
pub struct RealProjectGapExpectation {
/// The processing stage where the error is admitted.
pub stage: P0Stage,
pub stage: RealProjectStage,
/// The structured Workshop error kind.
pub kind: P0GapKind,
pub kind: RealProjectGapKind,
/// The localized spelling or identity carried by the error.
pub identity: &'static str,
/// The owner-defined classification corresponding to this gap.
pub classification: ResidualClassification,
}

/// The pinned source identity and owner-defined expectation for one P0 case.
/// The pinned source identity and owner-defined expectation for one real-project case.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct P0CaseExpectation {
pub struct RealProjectCaseExpectation {
/// Stable case identity.
pub id: &'static str,
/// Source locale of the pinned Workshop input.
Expand All @@ -88,12 +88,12 @@ pub struct P0CaseExpectation {
/// SHA-256 digest of [`Self::source_fixture`].
pub source_sha256: &'static str,
/// Semantic residuals admitted for this case at every inspection stage.
pub residuals: &'static [P0ResidualExpectation],
pub residuals: &'static [RealProjectResidualExpectation],
/// Stage-specific Workshop errors admitted for this case.
pub gaps: &'static [P0GapExpectation],
pub gaps: &'static [RealProjectGapExpectation],
}

impl P0CaseExpectation {
impl RealProjectCaseExpectation {
/// Whether the inspected semantic issue is admitted for this case.
pub fn admits_residual(&self, issue: &SemanticIssue) -> bool {
self.residuals.iter().any(|expected| {
Expand All @@ -104,97 +104,98 @@ impl P0CaseExpectation {
}

/// Whether the error is an owner-admitted gap at the given stage.
pub fn admits_gap(&self, stage: P0Stage, error: &WorkshopError) -> bool {
pub fn admits_gap(&self, stage: RealProjectStage, error: &WorkshopError) -> bool {
self.gaps.iter().any(|expected| {
expected.stage == stage
&& match (expected.kind, error) {
(P0GapKind::UnknownAction, WorkshopError::Unknown { kind, spelling, .. }) => {
*kind == expected.kind.as_str() && spelling == expected.identity
}
(
RealProjectGapKind::UnknownAction,
WorkshopError::Unknown { kind, spelling, .. },
) => *kind == expected.kind.as_str() && spelling == expected.identity,
_ => false,
}
})
}
}

/// The owner-controlled P0 expectation contract consumed by the harness and
/// downstream conformance tests.
/// The owner-controlled real-project expectation contract consumed by the
/// harness and downstream conformance tests.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct P0Expectation {
pub struct RealProjectExpectation {
/// Contract schema version.
pub schema_version: u32,
/// Stable identity of the source corpus.
pub corpus_id: &'static str,
/// The complete current P0 case inventory and expectations.
pub cases: &'static [P0CaseExpectation],
/// The complete current real-project case inventory and expectations.
pub cases: &'static [RealProjectCaseExpectation],
}

const NO_RESIDUALS: &[P0ResidualExpectation] = &[];
const NO_GAPS: &[P0GapExpectation] = &[];
const DEFEND_RESIDUALS: &[P0ResidualExpectation] = &[P0ResidualExpectation {
const NO_RESIDUALS: &[RealProjectResidualExpectation] = &[];
const NO_GAPS: &[RealProjectGapExpectation] = &[];
const DEFEND_RESIDUALS: &[RealProjectResidualExpectation] = &[RealProjectResidualExpectation {
kind: IncompletenessKind::OpaqueAction,
identity: "rawWorkshopAction",
classification: ResidualClassification::LegacyOpaque,
}];
const DEFEND_GAPS: &[P0GapExpectation] = &[
P0GapExpectation {
stage: P0Stage::CanonicalValidation,
kind: P0GapKind::UnknownAction,
const DEFEND_GAPS: &[RealProjectGapExpectation] = &[
RealProjectGapExpectation {
stage: RealProjectStage::CanonicalValidation,
kind: RealProjectGapKind::UnknownAction,
identity: "rawWorkshopAction",
classification: ResidualClassification::LegacyOpaque,
},
P0GapExpectation {
stage: P0Stage::Emission,
kind: P0GapKind::UnknownAction,
RealProjectGapExpectation {
stage: RealProjectStage::Emission,
kind: RealProjectGapKind::UnknownAction,
identity: "rawWorkshopAction",
classification: ResidualClassification::LegacyOpaque,
},
P0GapExpectation {
stage: P0Stage::LocaleConversion,
kind: P0GapKind::UnknownAction,
RealProjectGapExpectation {
stage: RealProjectStage::LocaleConversion,
kind: RealProjectGapKind::UnknownAction,
identity: "rawWorkshopAction",
classification: ResidualClassification::LegacyOpaque,
},
];

/// The single authoritative P0 case and expectation definition.
pub const P0_EXPECTATION: P0Expectation = P0Expectation {
schema_version: P0_EXPECTATION_SCHEMA_VERSION,
corpus_id: P0_CORPUS_ID,
/// The single authoritative real-project case and expectation definition.
pub const REAL_PROJECT_EXPECTATION: RealProjectExpectation = RealProjectExpectation {
schema_version: REAL_PROJECT_EXPECTATION_SCHEMA_VERSION,
corpus_id: REAL_PROJECT_CORPUS_ID,
cases: &[
P0CaseExpectation {
RealProjectCaseExpectation {
id: "ai-pve",
locale: "zh-CN",
source_fixture: "tests/fixtures/real-projects/ai-pve.ow",
source_sha256: "d9c6460ca550e40083efcc2b57de16360088631970824599a22c0aa2cb7f11f9",
residuals: NO_RESIDUALS,
gaps: NO_GAPS,
},
P0CaseExpectation {
RealProjectCaseExpectation {
id: "bastion",
locale: "en-US",
source_fixture: "tests/fixtures/real-projects/bastion.ow",
source_sha256: "44e453ddf7f373be65aea82d019abd45dd60f5ecb57c8d1607d3576a8bc60259",
residuals: NO_RESIDUALS,
gaps: NO_GAPS,
},
P0CaseExpectation {
RealProjectCaseExpectation {
id: "defend",
locale: "en-US",
source_fixture: "tests/fixtures/real-projects/defend.ow",
source_sha256: "06a956b650313ee2d6e24ec989f907244dc4444579bdba27c580b031de97b268",
residuals: DEFEND_RESIDUALS,
gaps: DEFEND_GAPS,
},
P0CaseExpectation {
RealProjectCaseExpectation {
id: "illari",
locale: "zh-CN",
source_fixture: "tests/fixtures/real-projects/illari.ow",
source_sha256: "f3aff73b9e677730bddc9c85b04c2bd38439bb7a4ba4fa2e80dc28db2e4a0860",
residuals: NO_RESIDUALS,
gaps: NO_GAPS,
},
P0CaseExpectation {
RealProjectCaseExpectation {
id: "rework",
locale: "en-US",
source_fixture: "tests/fixtures/real-projects/rework.ow",
Expand All @@ -210,11 +211,14 @@ mod tests {
use super::*;

#[test]
fn p0_expectation_has_unique_pinned_case_identities() {
assert_eq!(P0_EXPECTATION.schema_version, P0_EXPECTATION_SCHEMA_VERSION);
assert_eq!(P0_EXPECTATION.corpus_id, P0_CORPUS_ID);
fn real_projects_expectation_has_unique_pinned_case_identities() {
assert_eq!(
REAL_PROJECT_EXPECTATION.schema_version,
REAL_PROJECT_EXPECTATION_SCHEMA_VERSION
);
assert_eq!(REAL_PROJECT_EXPECTATION.corpus_id, REAL_PROJECT_CORPUS_ID);

for (index, case) in P0_EXPECTATION.cases.iter().enumerate() {
for (index, case) in REAL_PROJECT_EXPECTATION.cases.iter().enumerate() {
assert!(!case.id.is_empty());
assert!(case.locale == "en-US" || case.locale == "zh-CN");
assert!(
Expand All @@ -223,26 +227,26 @@ mod tests {
);
assert_eq!(case.source_sha256.len(), 64);
assert!(
P0_EXPECTATION.cases[index + 1..]
REAL_PROJECT_EXPECTATION.cases[index + 1..]
.iter()
.all(|other| other.id != case.id),
"duplicate P0 case identity: {}",
"duplicate real-project case identity: {}",
case.id
);
}
}

#[test]
fn p0_expectation_keeps_the_admitted_gap_identity_and_classification() {
let defend = P0_EXPECTATION
fn real_projects_expectation_keeps_the_admitted_gap_identity_and_classification() {
let defend = REAL_PROJECT_EXPECTATION
.cases
.iter()
.find(|case| case.id == "defend")
.expect("defend case");
assert_eq!(defend.residuals, DEFEND_RESIDUALS);
assert_eq!(defend.gaps.len(), 3);
assert!(defend.gaps.iter().all(|gap| {
gap.kind == P0GapKind::UnknownAction
gap.kind == RealProjectGapKind::UnknownAction
&& gap.identity == "rawWorkshopAction"
&& gap.classification == ResidualClassification::LegacyOpaque
}));
Expand All @@ -253,9 +257,9 @@ mod tests {
locale: crate::catalog::Locale::new("en-US"),
span: None,
};
assert!(defend.admits_gap(P0Stage::Emission, &error));
assert!(defend.admits_gap(RealProjectStage::Emission, &error));
assert!(!defend.admits_gap(
P0Stage::CanonicalValidation,
RealProjectStage::CanonicalValidation,
&WorkshopError::Unknown {
kind: "value",
spelling: "rawWorkshopAction".to_string(),
Expand Down
16 changes: 10 additions & 6 deletions crates/workshop-rs/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use sha2::{Digest, Sha256};
use workshop_rs::{
WorkshopError,
catalog::{Catalog, Locale},
p0::{P0CaseExpectation, P0Stage},
real_projects::{RealProjectCaseExpectation, RealProjectStage},
semantic,
};

Expand All @@ -17,11 +17,11 @@ struct SpanReport {
end_column: u32,
}

pub(crate) fn cases() -> &'static [P0CaseExpectation] {
workshop_rs::p0::P0_EXPECTATION.cases
pub(crate) fn cases() -> &'static [RealProjectCaseExpectation] {
workshop_rs::real_projects::REAL_PROJECT_EXPECTATION.cases
}

pub(crate) fn source(case: &P0CaseExpectation) -> (String, Locale) {
pub(crate) fn source(case: &RealProjectCaseExpectation) -> (String, Locale) {
let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join(case.source_fixture);
let bytes = std::fs::read(&path).unwrap_or_else(|error| panic!("{path:?}: {error}"));
assert_eq!(
Expand All @@ -43,7 +43,7 @@ pub(crate) fn target_locale(source_locale: &Locale) -> Locale {
}

pub(crate) fn assert_residual_policy(
case: &P0CaseExpectation,
case: &RealProjectCaseExpectation,
stage: &str,
issues: &[semantic::SemanticIssue],
) {
Expand All @@ -69,7 +69,11 @@ pub(crate) fn assert_residual_policy(
);
}

pub(crate) fn assert_gap(case: &P0CaseExpectation, stage: P0Stage, error: &WorkshopError) {
pub(crate) fn assert_gap(
case: &RealProjectCaseExpectation,
stage: RealProjectStage,
error: &WorkshopError,
) {
assert!(
case.admits_gap(stage, error),
"{} {} failed outside known gaps: {error:?}",
Expand Down
Loading
Loading