From ef16b0c90b890cc7a1e58dbc3f09df751e4c7b2a Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 9 Jul 2026 01:39:26 +0100 Subject: [PATCH] Strengthen split target conformance consumption --- docs/eve-migration-roadmap.md | 7 +++ docs/parity-testing-harness.md | 12 ++++ scripts/run-parity-harness.ps1 | 1 + ...plit-target-conformance-consumer-smoke.ps1 | 53 ++++++++++++++++ tools/conformance/consume-export.mjs | 62 +++++++++++++++++++ 5 files changed, 135 insertions(+) create mode 100644 scripts/run-split-target-conformance-consumer-smoke.ps1 diff --git a/docs/eve-migration-roadmap.md b/docs/eve-migration-roadmap.md index 5d91603..80d5ba8 100644 --- a/docs/eve-migration-roadmap.md +++ b/docs/eve-migration-roadmap.md @@ -280,6 +280,13 @@ Exit criteria: Recently cut: +- Split-target conformance consumption now verifies graduation state instead of + only split-target presence. The generic consumer supports + `--expect-split-target-status`, `--expect-split-target-blocker`, and + `--expect-split-target-proof`; the parity harness runs + `scripts/run-split-target-conformance-consumer-smoke.ps1`; and EveFlutter / + EveUnity consumers can now prove that the targets remain incubating for named + blockers while their current split-readiness proofs stay passed. - Runtime owner conformance consumption now verifies runtime target health instead of only runtime presence. The generic consumer supports `--expect-runtime-status`, `--expect-runtime-feature`, diff --git a/docs/parity-testing-harness.md b/docs/parity-testing-harness.md index 3fdff48..9e5dacc 100644 --- a/docs/parity-testing-harness.md +++ b/docs/parity-testing-harness.md @@ -120,6 +120,18 @@ That script runs the export consumer from `flutter/eve_parity` and asserts the runtime pack, Sai provider fixture, Flutter runtime targets, and `EveFlutter` split target without reading `tools/parity/parity-manifest.json`. +Split-target consumer smoke: + +```powershell +powershell -ExecutionPolicy Bypass -File .\scripts\run-split-target-conformance-consumer-smoke.ps1 +``` + +That script asserts that EveFlutter and EveUnity are exported as incubating +split targets, that their current split-readiness proofs are passed, and that +their graduation blockers remain visible to consumers. A target being present +is not enough; future owner repos must be able to consume why it is or is not +ready to leave Eve incubation. + The Flutter widget smoke also exercises `EveProviderCatalog` and `EveProviderPicker` against a conformance-export-shaped provider list. That is the runtime-side provider picker proof; it consumes provider entries and plugin diff --git a/scripts/run-parity-harness.ps1 b/scripts/run-parity-harness.ps1 index e4ee1f3..f2061f8 100644 --- a/scripts/run-parity-harness.ps1 +++ b/scripts/run-parity-harness.ps1 @@ -17,6 +17,7 @@ try { & (Join-Path $PSScriptRoot "run-conformance-consumer-smoke.ps1") & (Join-Path $PSScriptRoot "run-plugin-owner-conformance-consumer-smoke.ps1") & (Join-Path $PSScriptRoot "run-runtime-owner-conformance-consumer-smoke.ps1") + & (Join-Path $PSScriptRoot "run-split-target-conformance-consumer-smoke.ps1") } finally { Pop-Location } diff --git a/scripts/run-split-target-conformance-consumer-smoke.ps1 b/scripts/run-split-target-conformance-consumer-smoke.ps1 new file mode 100644 index 0000000..424232f --- /dev/null +++ b/scripts/run-split-target-conformance-consumer-smoke.ps1 @@ -0,0 +1,53 @@ +param( + [string] $SourceDirectory = "artifacts\conformance\latest", + [string] $SmokeDirectory = "artifacts\split-target-conformance-consumer-smoke" +) + +$ErrorActionPreference = "Stop" + +$projectRoot = Split-Path -Parent $PSScriptRoot +$sourcePath = if ([System.IO.Path]::IsPathRooted($SourceDirectory)) { + $SourceDirectory +} else { + Join-Path $projectRoot $SourceDirectory +} +$smokePath = if ([System.IO.Path]::IsPathRooted($SmokeDirectory)) { + $SmokeDirectory +} else { + Join-Path $projectRoot $SmokeDirectory +} +$exportPath = Join-Path $smokePath "export" + +if (-not (Test-Path -LiteralPath (Join-Path $sourcePath "index.json"))) { + throw "Conformance export not found: $sourcePath" +} + +if (Test-Path -LiteralPath $exportPath) { + Remove-Item -LiteralPath $exportPath -Recurse -Force +} +New-Item -ItemType Directory -Force -Path $smokePath | Out-Null +Copy-Item -LiteralPath $sourcePath -Destination $exportPath -Recurse + +Push-Location $projectRoot +try { + node .\tools\conformance\consume-export.mjs $exportPath ` + --expect-split-target EveFlutter ` + --expect-split-target-status EveFlutter:incubating ` + --expect-split-target-proof "EveFlutter:Provider picker consumes provider advertisements" ` + --expect-split-target-proof "EveFlutter:Conformance pack can be consumed" ` + --expect-split-target-proof "EveFlutter:Flutter lifecycle evidence is declared" ` + --expect-split-target-blocker "EveFlutter:Tagged EveFlutter release" ` + --expect-split-target EveUnity ` + --expect-split-target-status EveUnity:incubating ` + --expect-split-target-proof "EveUnity:Unity lifecycle evidence is declared" ` + --expect-split-target-blocker "EveUnity:Tagged UPM release" ` + --expect-split-target-blocker "EveUnity:Unity batchmode EditMode runner" ` + --expect-split-target-blocker "EveUnity:Unity batchmode or editor capture artifact" + if ($LASTEXITCODE -ne 0) { + throw "Split target conformance consumer smoke failed with exit code $LASTEXITCODE" + } +} finally { + Pop-Location +} + +Write-Host "Split target conformance consumer smoke passed: $exportPath" diff --git a/tools/conformance/consume-export.mjs b/tools/conformance/consume-export.mjs index 3091318..4732667 100644 --- a/tools/conformance/consume-export.mjs +++ b/tools/conformance/consume-export.mjs @@ -25,6 +25,9 @@ if (!exportDirectory) { " --expect-runtime-feature ", " --expect-runtime-command-schema ", " --expect-runtime-capture-status ", + " --expect-split-target-status ", + " --expect-split-target-blocker ", + " --expect-split-target-proof ", ].join("\n")); process.exit(2); } @@ -208,6 +211,41 @@ function validateIndex(index, directory, expectations, errors) { for (const expectedSplitTarget of expectations.splitTargets) { if (!splitTargets.some(target => target.id === expectedSplitTarget)) errors.push(`splitTargets:${expectedSplitTarget}:missing`); } + for (const expectation of expectations.splitTargetStatuses) { + const target = splitTargets.find(candidate => candidate.id === expectation.targetId); + if (!target) { + errors.push(`splitTargets:${expectation.targetId}:missing`); + continue; + } + if (target.status !== expectation.status) { + errors.push(`splitTargets:${expectation.targetId}:status:expected ${expectation.status} got ${target.status || ""}`); + } + } + for (const expectation of expectations.splitTargetBlockers) { + const target = splitTargets.find(candidate => candidate.id === expectation.targetId); + if (!target) { + errors.push(`splitTargets:${expectation.targetId}:missing`); + continue; + } + const blockers = Array.isArray(target.blockers) ? target.blockers : []; + if (!blockers.some(blocker => blocker.includes(expectation.blocker))) { + errors.push(`splitTargets:${expectation.targetId}:blocker:${expectation.blocker}:missing`); + } + } + for (const expectation of expectations.splitTargetProofs) { + const target = splitTargets.find(candidate => candidate.id === expectation.targetId); + if (!target) { + errors.push(`splitTargets:${expectation.targetId}:missing`); + continue; + } + const proofs = Array.isArray(target.proofs) ? target.proofs : []; + const proof = proofs.find(candidate => candidate.description?.includes(expectation.proof)); + if (!proof) { + errors.push(`splitTargets:${expectation.targetId}:proof:${expectation.proof}:missing`); + } else if (proof.status !== "passed") { + errors.push(`splitTargets:${expectation.targetId}:proof:${expectation.proof}:expected passed got ${proof.status || ""}`); + } + } if (!plugins.some(plugin => plugin.pluginId === "sai.vn" && plugin.abiFixturePath)) { errors.push("plugins:sai.vn:abiFixturePath:missing"); @@ -246,6 +284,9 @@ function parseArguments(args) { runtimeCaptureStatuses: [], scenarios: [], splitTargets: [], + splitTargetStatuses: [], + splitTargetBlockers: [], + splitTargetProofs: [], }; const optionTargets = new Map([ ["--expect-pack", expectations.packs], @@ -264,6 +305,9 @@ function parseArguments(args) { ["--expect-runtime-feature", expectations.runtimeFeatures], ["--expect-runtime-command-schema", expectations.runtimeCommandSchemas], ["--expect-runtime-capture-status", expectations.runtimeCaptureStatuses], + ["--expect-split-target-status", expectations.splitTargetStatuses], + ["--expect-split-target-blocker", expectations.splitTargetBlockers], + ["--expect-split-target-proof", expectations.splitTargetProofs], ]); for (let index = 1; index < args.length; index += 1) { @@ -296,6 +340,12 @@ function parseArguments(args) { target.push(parseRuntimeExpectation(value, "schema")); } else if (option === "--expect-runtime-capture-status") { target.push(parseRuntimeExpectation(value, "status")); + } else if (option === "--expect-split-target-status") { + target.push(parseSplitTargetExpectation(value, "status")); + } else if (option === "--expect-split-target-blocker") { + target.push(parseSplitTargetExpectation(value, "blocker")); + } else if (option === "--expect-split-target-proof") { + target.push(parseSplitTargetExpectation(value, "proof")); } else { target.push(value); } @@ -341,6 +391,18 @@ function parseRuntimeExpectation(value, field) { }; } +function parseSplitTargetExpectation(value, field) { + const separator = value.indexOf(":"); + if (separator <= 0 || separator === value.length - 1) { + console.error(`Expected split target ${field} in form, got: ${value}`); + process.exit(2); + } + return { + targetId: value.slice(0, separator), + [field]: value.slice(separator + 1), + }; +} + function mergeRuntimeRecords(topLevelRuntimes, runtimeTargets) { const byId = new Map(); for (const runtime of runtimeTargets) {