diff --git a/crates/html/src/conformance/execution.rs b/crates/html/src/conformance/execution.rs index fa7cc3ba..6aa7b26f 100644 --- a/crates/html/src/conformance/execution.rs +++ b/crates/html/src/conformance/execution.rs @@ -26,6 +26,167 @@ pub enum ObservationRequest { }, } +#[cfg(test)] +mod execution_identity_tests { + use super::*; + + #[test] + fn every_closed_parser_observation_error_identity_is_preserved_without_text_classification() { + for (error, identity) in [ + ( + ParserObservationExecutionError::ParserInvariant, + ParserObservationExecutionIdentity::ParserInvariant, + ), + ( + ParserObservationExecutionError::TokenCanonicalizationInvariant, + ParserObservationExecutionIdentity::TokenCanonicalizationInvariant, + ), + ( + ParserObservationExecutionError::TreeTransitionTokenCanonicalizationInvariant, + ParserObservationExecutionIdentity::TreeTransitionTokenCanonicalizationInvariant, + ), + ( + ParserObservationExecutionError::ObservationRecorderMissing, + ParserObservationExecutionIdentity::ObservationRecorderMissing, + ), + ( + ParserObservationExecutionError::PatchHistoryCaptureMissing, + ParserObservationExecutionIdentity::PatchHistoryCaptureMissing, + ), + ] { + assert_eq!(error.identity(), identity); + } + + assert_eq!( + ParserObservationExecutionError::ParserFatal(crate::ParserFatalError::EngineInvariant) + .identity(), + ParserObservationExecutionIdentity::ParserFatal(ParserFatalIdentity::EngineInvariant), + ); + for (site, identity) in [ + ( + crate::ParserReservationSite::KnownTagAtomStorage, + ParserReservationSiteIdentity::KnownTagAtomStorage, + ), + ( + crate::ParserReservationSite::KnownTagLookupStorage, + ParserReservationSiteIdentity::KnownTagLookupStorage, + ), + ( + crate::ParserReservationSite::TemplateChildStorage, + ParserReservationSiteIdentity::TemplateChildStorage, + ), + ( + crate::ParserReservationSite::PatchHistoryObservationStorage, + ParserReservationSiteIdentity::PatchHistoryObservationStorage, + ), + ] { + let error = crate::ParserResourceExhaustion::at(site); + assert_eq!( + ParserObservationExecutionError::ParserFatal(error.into()).identity(), + ParserObservationExecutionIdentity::ParserFatal( + ParserFatalIdentity::ResourceExhaustion(identity) + ) + ); + } + + for code in [ + ParserTokenizerInvariantError::SelfClosingFlagMissingSolidusPosition, + ParserTokenizerInvariantError::SolidusPositionWithoutPendingTag, + ParserTokenizerInvariantError::SolidusPositionOutsideCurrentPendingTag, + ParserTokenizerInvariantError::SolidusPositionDoesNotReferenceConsumedSlash, + ParserTokenizerInvariantError::DoctypeNameStartMissingForNameState, + ParserTokenizerInvariantError::DoctypeNameStartMissingForTailScan, + ParserTokenizerInvariantError::DoctypeNameStartMissingForResourceObservation, + ParserTokenizerInvariantError::DoctypeNameStartAfterCursor, + ParserTokenizerInvariantError::DoctypeNameRangeInvalid, + ParserTokenizerInvariantError::DoctypeTailRangeInvalid, + ParserTokenizerInvariantError::AsciiPrefixCandidateRangeInvalid, + ParserTokenizerInvariantError::CommentStateMissingPendingStart, + ParserTokenizerInvariantError::CommentPendingRangeInvalid, + ParserTokenizerInvariantError::CommentPendingDelimiterOutsideCurrentRange, + ParserTokenizerInvariantError::CommentPendingDelimiterDoesNotMatchState, + ParserTokenizerInvariantError::TextModeEndTagCandidateRangeInvalid, + ParserTokenizerInvariantError::TextModeEndTagAttributePositionInvalid, + ParserTokenizerInvariantError::TextModeEndTagSolidusPositionInvalid, + ParserTokenizerInvariantError::PendingTextRangeInvalid, + ParserTokenizerInvariantError::CdataStateMissingPendingTextStart, + ParserTokenizerInvariantError::CdataEndDelimiterOutsidePendingTextRange, + ParserTokenizerInvariantError::CdataEndDelimiterDoesNotMatchState, + ParserTokenizerInvariantError::ProcessingInstructionStateMissingPendingMetadata, + ParserTokenizerInvariantError::ProcessingInstructionMetadataOutsideState, + ParserTokenizerInvariantError::ProcessingInstructionTargetRangeInvalid, + ParserTokenizerInvariantError::ProcessingInstructionDataRangeInvalid, + ParserTokenizerInvariantError::ProcessingInstructionTargetStartAfterCursor, + ParserTokenizerInvariantError::ProcessingInstructionDataStartAfterCursor, + ] { + assert_eq!( + ParserObservationExecutionError::TokenizerInvariant(code).identity(), + ParserObservationExecutionIdentity::TokenizerInvariant(code) + ); + } + + for code in [ + UnsupportedFeatureObservationInvariantError::TokenAttributeNameUnavailable, + UnsupportedFeatureObservationInvariantError::ExistingHtmlElementSemanticsUnavailable, + UnsupportedFeatureObservationInvariantError::ExistingBodyElementSemanticsUnavailable, + UnsupportedFeatureObservationInvariantError::ExistingElementIdentityContradiction, + ] { + assert_eq!( + ParserObservationExecutionError::UnsupportedFeatureObservationInvariant(code) + .identity(), + ParserObservationExecutionIdentity::UnsupportedFeatureObservationInvariant(code) + ); + } + + for code in [ + ParserObservationInvariantError::ParseErrorOccurrenceOverflow, + ParserObservationInvariantError::ImplementationDiagnosticOccurrenceOverflow, + ParserObservationInvariantError::TreeTransitionOccurrenceOverflow, + ParserObservationInvariantError::UnsupportedFeatureOccurrenceOverflow, + ParserObservationInvariantError::TokenDroppedCountOverflow, + ParserObservationInvariantError::ParseErrorDroppedCountOverflow, + ParserObservationInvariantError::ImplementationDiagnosticDroppedCountOverflow, + ParserObservationInvariantError::TreeTransitionDroppedCountOverflow, + ParserObservationInvariantError::UnsupportedFeatureDroppedCountOverflow, + ParserObservationInvariantError::NormalizedPositionOverflow, + ParserObservationInvariantError::NormalizedPositionIndexDiscontinuity, + ParserObservationInvariantError::NormalizedPositionIndexMissing, + ParserObservationInvariantError::InvalidNormalizedPositionOffset, + ParserObservationInvariantError::PatchDroppedCountOverflow, + ParserObservationInvariantError::CanonicalTreeUnitCountOverflow, + ParserObservationInvariantError::CanonicalTreeRootNotDocument, + ParserObservationInvariantError::UnexpectedLegacyDocumentDoctypeMetadata, + ParserObservationInvariantError::MissingHtmlTemplateContents, + ParserObservationInvariantError::InvalidTemplateContentsKind, + ParserObservationInvariantError::CanonicalTreeTraversalContradiction, + ParserObservationInvariantError::CanonicalTreePreflightProjectionMismatch, + ParserObservationInvariantError::InvalidPatchKey, + ParserObservationInvariantError::DuplicatePatchCreation, + ParserObservationInvariantError::MissingPatchCreationHistory, + ParserObservationInvariantError::SnapshotLabelSequenceOverflow, + ] { + assert_eq!( + ParserObservationExecutionError::ObservationInvariant(code).identity(), + ParserObservationExecutionIdentity::ObservationInvariant(code) + ); + } + + for site in [ + ObservationReservationSite::CanonicalTreeProjection, + ObservationReservationSite::CanonicalPatchProjection, + ObservationReservationSite::SnapshotLabelStorage, + ] { + assert_eq!( + ParserObservationExecutionError::ResourceExhaustion( + ObservationResourceExhaustion::at(site) + ) + .identity(), + ParserObservationExecutionIdentity::ResourceExhaustion(site) + ); + } + } +} + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum ScalarObservationRequest { #[default] @@ -82,6 +243,104 @@ pub enum ParserObservationExecutionError { ResourceExhaustion(ObservationResourceExhaustion), } +/// Closed, message-independent identity for fixture disposition matching. +/// +/// `ParserFatalError` and its reservation site are deliberately non-exhaustive +/// at the ordinary parser API boundary. Canonical test support therefore asks +/// the owning HTML subsystem for this feature-gated identity instead of +/// classifying `Display` or `Debug` text. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ParserObservationExecutionIdentity { + ParserFatal(ParserFatalIdentity), + ParserInvariant, + TokenizerInvariant(ParserTokenizerInvariantError), + TokenCanonicalizationInvariant, + TreeTransitionTokenCanonicalizationInvariant, + UnsupportedFeatureObservationInvariant(UnsupportedFeatureObservationInvariantError), + ObservationRecorderMissing, + PatchHistoryCaptureMissing, + ObservationInvariant(ParserObservationInvariantError), + ResourceExhaustion(ObservationReservationSite), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ParserFatalIdentity { + EngineInvariant, + ResourceExhaustion(ParserReservationSiteIdentity), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ParserReservationSiteIdentity { + KnownTagAtomStorage, + KnownTagLookupStorage, + TemplateChildStorage, + PatchHistoryObservationStorage, +} + +impl ParserObservationExecutionError { + #[must_use] + pub const fn identity(self) -> ParserObservationExecutionIdentity { + match self { + Self::ParserFatal(error) => { + ParserObservationExecutionIdentity::ParserFatal(parser_fatal_identity(error)) + } + Self::ParserInvariant => ParserObservationExecutionIdentity::ParserInvariant, + Self::TokenizerInvariant(error) => { + ParserObservationExecutionIdentity::TokenizerInvariant(error) + } + Self::TokenCanonicalizationInvariant => { + ParserObservationExecutionIdentity::TokenCanonicalizationInvariant + } + Self::TreeTransitionTokenCanonicalizationInvariant => { + ParserObservationExecutionIdentity::TreeTransitionTokenCanonicalizationInvariant + } + Self::UnsupportedFeatureObservationInvariant(error) => { + ParserObservationExecutionIdentity::UnsupportedFeatureObservationInvariant(error) + } + Self::ObservationRecorderMissing => { + ParserObservationExecutionIdentity::ObservationRecorderMissing + } + Self::PatchHistoryCaptureMissing => { + ParserObservationExecutionIdentity::PatchHistoryCaptureMissing + } + Self::ObservationInvariant(error) => { + ParserObservationExecutionIdentity::ObservationInvariant(error) + } + Self::ResourceExhaustion(error) => { + ParserObservationExecutionIdentity::ResourceExhaustion(error.site()) + } + } + } +} + +const fn parser_fatal_identity(error: crate::ParserFatalError) -> ParserFatalIdentity { + match error { + crate::ParserFatalError::EngineInvariant => ParserFatalIdentity::EngineInvariant, + crate::ParserFatalError::ResourceExhaustion(error) => { + ParserFatalIdentity::ResourceExhaustion(parser_reservation_site_identity(error.site())) + } + } +} + +const fn parser_reservation_site_identity( + site: crate::ParserReservationSite, +) -> ParserReservationSiteIdentity { + match site { + crate::ParserReservationSite::KnownTagAtomStorage => { + ParserReservationSiteIdentity::KnownTagAtomStorage + } + crate::ParserReservationSite::KnownTagLookupStorage => { + ParserReservationSiteIdentity::KnownTagLookupStorage + } + crate::ParserReservationSite::TemplateChildStorage => { + ParserReservationSiteIdentity::TemplateChildStorage + } + crate::ParserReservationSite::PatchHistoryObservationStorage => { + ParserReservationSiteIdentity::PatchHistoryObservationStorage + } + } +} + /// Fallible allocation boundary owned by post-parse canonical observation. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum ObservationReservationSite { diff --git a/crates/html/src/conformance/mod.rs b/crates/html/src/conformance/mod.rs index 537fb184..dfad7daa 100644 --- a/crates/html/src/conformance/mod.rs +++ b/crates/html/src/conformance/mod.rs @@ -24,8 +24,9 @@ pub use crate::html5::shared::{ }; pub use execution::{ ObservationRequest, ObservationReservationSite, ObservationResourceExhaustion, - ParserObservationExecutionError, ParserObservationInput, ParserObservationInvariantError, - ParserObservationRequest, ParserObservationTarget, ParserTokenizerInvariantError, + ParserFatalIdentity, ParserObservationExecutionError, ParserObservationExecutionIdentity, + ParserObservationInput, ParserObservationInvariantError, ParserObservationRequest, + ParserObservationTarget, ParserReservationSiteIdentity, ParserTokenizerInvariantError, ScalarObservationRequest, UnsupportedFeatureObservationInvariantError, execute_parser_observation, }; diff --git a/crates/html/tests/fixtures/html5/conformance/README.md b/crates/html/tests/fixtures/html5/conformance/README.md index 850a9fc4..8cd4e09c 100644 --- a/crates/html/tests/fixtures/html5/conformance/README.md +++ b/crates/html/tests/fixtures/html5/conformance/README.md @@ -1,6 +1,6 @@ # Canonical HTML Parser Conformance Fixtures -This is the native fixture root for `borrowser-html-parser-fixture-v1`. Discovery +This is the native fixture root for `borrowser-html-parser-fixture-v2`. Discovery is recursive and sorted by normalized repository-relative bundle path. Add a directory containing `fixture.toml`, exact input, and declared snapshots; no Rust registration is required. @@ -12,7 +12,7 @@ directory containing `fixture.toml` is a leaf; nested bundles are rejected. Native fixtures in this directory must be `source = native` and `disposition.status = active`. Xfail, skip, and expected-unsupported entries belong only to later external/adapted inputs or a separately identified -quarantine source. Fixture-v1 permits skips only for an exact unsupported +quarantine source. Fixture-v2 permits skips only for an exact unsupported capability; broad external-source and environment skips are rejected. Use `input.html` only for valid UTF-8 input whose intended checkout form has LF @@ -21,11 +21,19 @@ byte delivery, and any byte-sensitive case. `input.html` containing a carriage return is rejected. Always update the mandatory SHA-256 from the exact stored bytes; the loader never trims input. -AE13a executes only whole-input standalone-tokenizer fixtures with a declared -`tokens.txt` in `html5-token-v1`. Other fixture-v1 surfaces are declarable but -fail explicitly as unsupported expectations until their owning AE13 slice lands. +AE13b5 executes supported whole-input standalone-tokenizer and document +fixtures from typed canonical observations. Ordinary surfaces are unioned on +the reference delivery; transition expectations may name another declared +whole delivery. Each planned delivery executes once. Unused declared whole +deliveries are capability-checked but do not execute. -See `docs/html5/parser-fixture-format-v1.md` for the complete schema and +Canonical sidecars use the exact AE13b5 formats, including `html5-token-v2`, +`html5-dom-v3`, and `html5-dompatch-v3`. Header-only diagnostic, transition, +unsupported-feature, tree, and patch snapshots represent requested empty +collections. Fixture-v1 remains an isolated compatibility format. + +See `docs/html5/parser-fixture-format-v2.md` and +`docs/html5/ae13b5-parser-snapshot-formats.md` for the schema/codecs, and `docs/html5/ae13-parser-conformance-regression-harness.md` for ownership and slice boundaries. diff --git a/crates/html/tests/fixtures/html5/conformance/document-recovery-diagnostics/fixture.toml b/crates/html/tests/fixtures/html5/conformance/document-recovery-diagnostics/fixture.toml new file mode 100644 index 00000000..56abd453 --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-recovery-diagnostics/fixture.toml @@ -0,0 +1,32 @@ +format = "borrowser-html-parser-fixture-v2" +id = "document-recovery-diagnostics" + +[source] +kind = "native" + +[input] +path = "input.html" +kind = "utf8-text" +sha256 = "f6a8bd0ddf91049b25303c3e04f39f422817603e9a9b4ed051a8d0832294bb8a" + +[execution] +reference_delivery = "whole" + +[execution.target] +kind = "document" +scripting = "disabled" + +[[execution.deliveries]] +name = "whole" +unit = "unicode-scalars" +strategy = "whole" + +[expectations] +parse_errors = "parse-errors.txt" +implementation_diagnostics = "implementation-diagnostics.txt" + +[disposition] +status = "active" + +[metadata] +description = "Duplicate-attribute and non-void self-closing recovery use typed canonical diagnostics." diff --git a/crates/html/tests/fixtures/html5/conformance/document-recovery-diagnostics/implementation-diagnostics.txt b/crates/html/tests/fixtures/html5/conformance/document-recovery-diagnostics/implementation-diagnostics.txt new file mode 100644 index 00000000..877e6785 --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-recovery-diagnostics/implementation-diagnostics.txt @@ -0,0 +1,2 @@ +# format: html5-implementation-diagnostics-v1 +IMPLEMENTATION_DIAGNOSTIC occurrence=1 stage=tree-construction code=tree-construction:non-void-html-self-closing-flag-altered-stack-disposition payload=none position=unavailable:parser-did-not-provide-position context=present context-token=start-tag context-mode=in-body context-namespace=html diff --git a/crates/html/tests/fixtures/html5/conformance/document-recovery-diagnostics/input.html b/crates/html/tests/fixtures/html5/conformance/document-recovery-diagnostics/input.html new file mode 100644 index 00000000..9a709df8 --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-recovery-diagnostics/input.html @@ -0,0 +1 @@ +
diff --git a/crates/html/tests/fixtures/html5/conformance/document-recovery-diagnostics/parse-errors.txt b/crates/html/tests/fixtures/html5/conformance/document-recovery-diagnostics/parse-errors.txt new file mode 100644 index 00000000..8ba8621f --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-recovery-diagnostics/parse-errors.txt @@ -0,0 +1,3 @@ +# format: html5-parse-errors-v1 +PARSE_ERROR occurrence=1 stage=tokenizer code=standard:duplicate-attribute recovery=drop-duplicate-attribute position=normalized-utf8:30:1:31:source-unavailable:no-input-provenance-map context=absent context-token=null context-mode=null context-namespace=null +PARSE_ERROR occurrence=2 stage=tree-construction code=tree-construction:unacknowledged-self-closing-flag recovery=null position=unavailable:parser-did-not-provide-position context=present context-token=start-tag context-mode=in-body context-namespace=html diff --git a/crates/html/tests/fixtures/html5/conformance/document-structured-observations/document-mode.txt b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/document-mode.txt new file mode 100644 index 00000000..87d01a65 --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/document-mode.txt @@ -0,0 +1,2 @@ +# format: html5-document-mode-v1 +MODE value=no-quirks diff --git a/crates/html/tests/fixtures/html5/conformance/document-structured-observations/fixture.toml b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/fixture.toml new file mode 100644 index 00000000..6a711924 --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/fixture.toml @@ -0,0 +1,46 @@ +format = "borrowser-html-parser-fixture-v2" +id = "document-structured-observations" + +[source] +kind = "native" + +[input] +path = "input.html" +kind = "utf8-text" +sha256 = "0da2862be1c0ca4cfcebe04b6619baebf478aa3009bddbfb32d5347cd7e3a1ae" + +[execution] +reference_delivery = "whole" + +[execution.target] +kind = "document" +scripting = "disabled" + +[[execution.deliveries]] +name = "whole" +unit = "unicode-scalars" +strategy = "whole" + +[[execution.deliveries]] +name = "trace-whole" +unit = "unicode-scalars" +strategy = "whole" + +[expectations] +tokens = "tokens.txt" +parse_errors = "parse-errors.txt" +implementation_diagnostics = "implementation-diagnostics.txt" +document_mode = "document-mode.txt" +tree = "tree.txt" +patches = "patches.txt" +unsupported_features = "unsupported-features.txt" + +[[expectations.transitions]] +delivery = "trace-whole" +path = "transitions.trace-whole.txt" + +[disposition] +status = "active" + +[metadata] +description = "Canonical document observations preserve template contents, foreign namespaces, production patch order, and delivery-specific transitions." diff --git a/crates/html/tests/fixtures/html5/conformance/document-structured-observations/implementation-diagnostics.txt b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/implementation-diagnostics.txt new file mode 100644 index 00000000..6d1b1577 --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/implementation-diagnostics.txt @@ -0,0 +1 @@ +# format: html5-implementation-diagnostics-v1 diff --git a/crates/html/tests/fixtures/html5/conformance/document-structured-observations/input.html b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/input.html new file mode 100644 index 00000000..bb705737 --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/input.html @@ -0,0 +1 @@ + diff --git a/crates/html/tests/fixtures/html5/conformance/document-structured-observations/parse-errors.txt b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/parse-errors.txt new file mode 100644 index 00000000..c245bdef --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/parse-errors.txt @@ -0,0 +1 @@ +# format: html5-parse-errors-v1 diff --git a/crates/html/tests/fixtures/html5/conformance/document-structured-observations/patches.txt b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/patches.txt new file mode 100644 index 00000000..410db372 --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/patches.txt @@ -0,0 +1,23 @@ +# format: html5-dompatch-v3 +PATCH operation=1 kind=create-document node="node-1" legacy-doctype=null +PATCH operation=2 kind=create-document-type node="node-2" name="html" public-id=null system-id=null +PATCH operation=3 kind=append-child parent="node-1" child="node-2" +PATCH operation=4 kind=create-element node="node-3" namespace=html local-name="html" +PATCH operation=5 kind=append-child parent="node-1" child="node-3" +PATCH operation=6 kind=create-element node="node-4" namespace=html local-name="head" +PATCH operation=7 kind=append-child parent="node-3" child="node-4" +PATCH operation=8 kind=create-element node="node-5" namespace=html local-name="template" +PATCH_ATTRIBUTE operation=8 index=0 namespace=none prefix=null local-name="id" value="t" +PATCH operation=9 kind=create-template-contents host="node-5" contents="node-6" +PATCH operation=10 kind=append-child parent="node-4" child="node-5" +PATCH operation=11 kind=create-element node="node-7" namespace=svg local-name="svg" +PATCH_ATTRIBUTE operation=11 index=0 namespace=none prefix=null local-name="viewBox" value="0 0 1 1" +PATCH operation=12 kind=append-child parent="node-6" child="node-7" +PATCH operation=13 kind=create-element node="node-8" namespace=svg local-name="title" +PATCH operation=14 kind=append-child parent="node-7" child="node-8" +PATCH operation=15 kind=create-text node="node-9" text="x" +PATCH operation=16 kind=append-child parent="node-8" child="node-9" +PATCH operation=17 kind=create-text node="node-10" text="\n" +PATCH operation=18 kind=append-child parent="node-4" child="node-10" +PATCH operation=19 kind=create-element node="node-11" namespace=html local-name="body" +PATCH operation=20 kind=append-child parent="node-3" child="node-11" diff --git a/crates/html/tests/fixtures/html5/conformance/document-structured-observations/tokens.txt b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/tokens.txt new file mode 100644 index 00000000..da3a8d0c --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/tokens.txt @@ -0,0 +1,13 @@ +# format: html5-token-v2 +TOKEN ordinal=1 kind=doctype name="html" public-id=null system-id=null force-quirks=false +TOKEN ordinal=2 kind=start-tag name="template" self-closing=false +TOKEN_ATTRIBUTE token=2 index=0 name="id" value="t" +TOKEN ordinal=3 kind=start-tag name="svg" self-closing=false +TOKEN_ATTRIBUTE token=3 index=0 name="viewbox" value="0 0 1 1" +TOKEN ordinal=4 kind=start-tag name="title" self-closing=false +TOKEN ordinal=5 kind=character data="x" +TOKEN ordinal=6 kind=end-tag name="title" +TOKEN ordinal=7 kind=end-tag name="svg" +TOKEN ordinal=8 kind=end-tag name="template" +TOKEN ordinal=9 kind=character data="\n" +TOKEN ordinal=10 kind=eof diff --git a/crates/html/tests/fixtures/html5/conformance/document-structured-observations/transitions.trace-whole.txt b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/transitions.trace-whole.txt new file mode 100644 index 00000000..06126577 --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/transitions.trace-whole.txt @@ -0,0 +1,16 @@ +# format: html5-tree-transitions-v1 +TRANSITION occurrence=1 token-kind=doctype token-name=null token-data=null token-self-closing=null mode-before=initial dispatch=html-insertion-mode:initial mode-after=before-html reprocessed=false +TRANSITION occurrence=2 token-kind=start-tag token-name="template" token-data=null token-self-closing=false mode-before=before-html dispatch=html-insertion-mode:before-html mode-after=before-head reprocessed=false +TRANSITION occurrence=3 token-kind=start-tag token-name="template" token-data=null token-self-closing=false mode-before=before-head dispatch=html-insertion-mode:before-head mode-after=in-head reprocessed=true +TRANSITION occurrence=4 token-kind=start-tag token-name="template" token-data=null token-self-closing=false mode-before=in-head dispatch=shared-template-rules mode-after=in-template reprocessed=true +TRANSITION occurrence=5 token-kind=start-tag token-name="svg" token-data=null token-self-closing=false mode-before=in-template dispatch=html-insertion-mode:in-template mode-after=in-body reprocessed=false +TRANSITION occurrence=6 token-kind=start-tag token-name="svg" token-data=null token-self-closing=false mode-before=in-body dispatch=html-insertion-mode:in-body mode-after=in-body reprocessed=true +TRANSITION occurrence=7 token-kind=start-tag token-name="title" token-data=null token-self-closing=false mode-before=in-body dispatch=foreign-content mode-after=in-body reprocessed=false +TRANSITION occurrence=8 token-kind=character token-name=null token-data="x" token-self-closing=null mode-before=in-body dispatch=html-insertion-mode:in-body mode-after=in-body reprocessed=false +TRANSITION occurrence=9 token-kind=end-tag token-name="title" token-data=null token-self-closing=null mode-before=in-body dispatch=foreign-content mode-after=in-body reprocessed=false +TRANSITION occurrence=10 token-kind=end-tag token-name="svg" token-data=null token-self-closing=null mode-before=in-body dispatch=foreign-content mode-after=in-body reprocessed=false +TRANSITION occurrence=11 token-kind=end-tag token-name="template" token-data=null token-self-closing=null mode-before=in-body dispatch=shared-template-rules mode-after=in-head reprocessed=false +TRANSITION occurrence=12 token-kind=character token-name=null token-data="\n" token-self-closing=null mode-before=in-head dispatch=html-insertion-mode:in-head mode-after=in-head reprocessed=false +TRANSITION occurrence=13 token-kind=eof token-name=null token-data=null token-self-closing=null mode-before=in-head dispatch=html-insertion-mode:in-head mode-after=after-head reprocessed=false +TRANSITION occurrence=14 token-kind=eof token-name=null token-data=null token-self-closing=null mode-before=after-head dispatch=html-insertion-mode:after-head mode-after=in-body reprocessed=true +TRANSITION occurrence=15 token-kind=eof token-name=null token-data=null token-self-closing=null mode-before=in-body dispatch=html-insertion-mode:in-body mode-after=in-body reprocessed=true diff --git a/crates/html/tests/fixtures/html5/conformance/document-structured-observations/tree.txt b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/tree.txt new file mode 100644 index 00000000..ac88e2cd --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/tree.txt @@ -0,0 +1,14 @@ +# format: html5-dom-v3 +NODE path=/root[0] kind=document +NODE path=/root[0]/child[0] kind=document-type name="html" public-id=null system-id=null +NODE path=/root[0]/child[1] kind=element namespace=html local-name="html" +NODE path=/root[0]/child[1]/child[0] kind=element namespace=html local-name="head" +NODE path=/root[0]/child[1]/child[0]/child[0] kind=html-template-host +ATTRIBUTE path=/root[0]/child[1]/child[0]/child[0] index=0 namespace=none prefix=null local-name="id" value="t" +TEMPLATE_CONTENTS path=/root[0]/child[1]/child[0]/child[0]/contents host=/root[0]/child[1]/child[0]/child[0] +NODE path=/root[0]/child[1]/child[0]/child[0]/contents/child[0] kind=element namespace=svg local-name="svg" +ATTRIBUTE path=/root[0]/child[1]/child[0]/child[0]/contents/child[0] index=0 namespace=none prefix=null local-name="viewBox" value="0 0 1 1" +NODE path=/root[0]/child[1]/child[0]/child[0]/contents/child[0]/child[0] kind=element namespace=svg local-name="title" +NODE path=/root[0]/child[1]/child[0]/child[0]/contents/child[0]/child[0]/child[0] kind=text data="x" +NODE path=/root[0]/child[1]/child[0]/child[1] kind=text data="\n" +NODE path=/root[0]/child[1]/child[1] kind=element namespace=html local-name="body" diff --git a/crates/html/tests/fixtures/html5/conformance/document-structured-observations/unsupported-features.txt b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/unsupported-features.txt new file mode 100644 index 00000000..95d96670 --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-structured-observations/unsupported-features.txt @@ -0,0 +1 @@ +# format: html5-unsupported-features-v1 diff --git a/crates/html/tests/fixtures/html5/conformance/document-unsupported-features/fixture.toml b/crates/html/tests/fixtures/html5/conformance/document-unsupported-features/fixture.toml new file mode 100644 index 00000000..8c1431f5 --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-unsupported-features/fixture.toml @@ -0,0 +1,35 @@ +format = "borrowser-html-parser-fixture-v2" +id = "document-unsupported-features" + +[source] +kind = "native" + +[input] +path = "input.html" +kind = "utf8-text" +sha256 = "a5a2563a71c0fea391a581fb1eae5f1355d73104a04a8baae3e633afd419c853" + +[execution] +reference_delivery = "whole" + +[execution.target] +kind = "document" +scripting = "disabled" + +[[execution.deliveries]] +name = "whole" +unit = "unicode-scalars" +strategy = "whole" + +[expectations] +unsupported_features = "unsupported-features.txt" + +[[expectations.transitions]] +delivery = "whole" +path = "transitions.whole.txt" + +[disposition] +status = "active" + +[metadata] +description = "Unsupported-feature identity and transition order come from one unioned production observation." diff --git a/crates/html/tests/fixtures/html5/conformance/document-unsupported-features/input.html b/crates/html/tests/fixtures/html5/conformance/document-unsupported-features/input.html new file mode 100644 index 00000000..c92de793 --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-unsupported-features/input.html @@ -0,0 +1 @@ + diff --git a/crates/html/tests/fixtures/html5/conformance/document-unsupported-features/transitions.whole.txt b/crates/html/tests/fixtures/html5/conformance/document-unsupported-features/transitions.whole.txt new file mode 100644 index 00000000..b7bdb9c6 --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-unsupported-features/transitions.whole.txt @@ -0,0 +1,9 @@ +# format: html5-tree-transitions-v1 +TRANSITION occurrence=1 token-kind=doctype token-name=null token-data=null token-self-closing=null mode-before=initial dispatch=html-insertion-mode:initial mode-after=before-html reprocessed=false +TRANSITION occurrence=2 token-kind=start-tag token-name="body" token-data=null token-self-closing=false mode-before=before-html dispatch=html-insertion-mode:before-html mode-after=before-head reprocessed=false +TRANSITION occurrence=3 token-kind=start-tag token-name="body" token-data=null token-self-closing=false mode-before=before-head dispatch=html-insertion-mode:before-head mode-after=in-head reprocessed=true +TRANSITION occurrence=4 token-kind=start-tag token-name="body" token-data=null token-self-closing=false mode-before=in-head dispatch=html-insertion-mode:in-head mode-after=after-head reprocessed=true +TRANSITION occurrence=5 token-kind=start-tag token-name="body" token-data=null token-self-closing=false mode-before=after-head dispatch=html-insertion-mode:after-head mode-after=in-body reprocessed=true +TRANSITION occurrence=6 token-kind=start-tag token-name="body" token-data=null token-self-closing=false mode-before=in-body dispatch=html-insertion-mode:in-body mode-after=in-body reprocessed=false +TRANSITION occurrence=7 token-kind=character token-name=null token-data="\n" token-self-closing=null mode-before=in-body dispatch=html-insertion-mode:in-body mode-after=in-body reprocessed=false +TRANSITION occurrence=8 token-kind=eof token-name=null token-data=null token-self-closing=null mode-before=in-body dispatch=html-insertion-mode:in-body mode-after=in-body reprocessed=false diff --git a/crates/html/tests/fixtures/html5/conformance/document-unsupported-features/unsupported-features.txt b/crates/html/tests/fixtures/html5/conformance/document-unsupported-features/unsupported-features.txt new file mode 100644 index 00000000..937245be --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/document-unsupported-features/unsupported-features.txt @@ -0,0 +1,2 @@ +# format: html5-unsupported-features-v1 +UNSUPPORTED_FEATURE occurrence=1 subsystem=tree-construction feature=mark-frameset-not-ok-for-repeated-body-start-tag context-token=start-tag context-mode=in-body context-namespace=html diff --git a/crates/html/tests/fixtures/html5/conformance/tokenizer-character-data/fixture.toml b/crates/html/tests/fixtures/html5/conformance/tokenizer-character-data/fixture.toml index fced1253..8a5e4e84 100644 --- a/crates/html/tests/fixtures/html5/conformance/tokenizer-character-data/fixture.toml +++ b/crates/html/tests/fixtures/html5/conformance/tokenizer-character-data/fixture.toml @@ -1,4 +1,4 @@ -format = "borrowser-html-parser-fixture-v1" +format = "borrowser-html-parser-fixture-v2" id = "tokenizer-character-data" [source] @@ -27,4 +27,4 @@ tokens = "tokens.txt" status = "active" [metadata] -description = "Ordinary standalone-tokenizer character data through the canonical AE13a runner." +description = "Ordinary standalone-tokenizer character data through the canonical AE13 runner." diff --git a/crates/html/tests/fixtures/html5/conformance/tokenizer-character-data/tokens.txt b/crates/html/tests/fixtures/html5/conformance/tokenizer-character-data/tokens.txt index 29eec290..60996d9b 100644 --- a/crates/html/tests/fixtures/html5/conformance/tokenizer-character-data/tokens.txt +++ b/crates/html/tests/fixtures/html5/conformance/tokenizer-character-data/tokens.txt @@ -1,3 +1,3 @@ -# format: html5-token-v1 -CHAR text="Hello, parser fixtures!\n" -EOF +# format: html5-token-v2 +TOKEN ordinal=1 kind=character data="Hello, parser fixtures!\n" +TOKEN ordinal=2 kind=eof diff --git a/crates/html/tests/fixtures/html5/conformance/tokenizer-doctype-null-identity/fixture.toml b/crates/html/tests/fixtures/html5/conformance/tokenizer-doctype-null-identity/fixture.toml new file mode 100644 index 00000000..fdee9914 --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/tokenizer-doctype-null-identity/fixture.toml @@ -0,0 +1,30 @@ +format = "borrowser-html-parser-fixture-v2" +id = "tokenizer-doctype-null-identity" + +[source] +kind = "native" + +[input] +path = "input.html" +kind = "utf8-text" +sha256 = "19607cbfde051163f480d2d8b4f27fc03c3046d855a27e2c88c3e261865da2c6" + +[execution] +reference_delivery = "whole" + +[execution.target] +kind = "standalone-tokenizer" + +[[execution.deliveries]] +name = "whole" +unit = "unicode-scalars" +strategy = "whole" + +[expectations] +tokens = "tokens.txt" + +[disposition] +status = "active" + +[metadata] +description = "Token v2 distinguishes an absent doctype name from the literal name null." diff --git a/crates/html/tests/fixtures/html5/conformance/tokenizer-doctype-null-identity/input.html b/crates/html/tests/fixtures/html5/conformance/tokenizer-doctype-null-identity/input.html new file mode 100644 index 00000000..1c4e4e0d --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/tokenizer-doctype-null-identity/input.html @@ -0,0 +1 @@ + diff --git a/crates/html/tests/fixtures/html5/conformance/tokenizer-doctype-null-identity/tokens.txt b/crates/html/tests/fixtures/html5/conformance/tokenizer-doctype-null-identity/tokens.txt new file mode 100644 index 00000000..32daf8c1 --- /dev/null +++ b/crates/html/tests/fixtures/html5/conformance/tokenizer-doctype-null-identity/tokens.txt @@ -0,0 +1,5 @@ +# format: html5-token-v2 +TOKEN ordinal=1 kind=doctype name=null public-id=null system-id=null force-quirks=true +TOKEN ordinal=2 kind=doctype name="null" public-id=null system-id=null force-quirks=false +TOKEN ordinal=3 kind=character data="\n" +TOKEN ordinal=4 kind=eof diff --git a/crates/html/tests/html5_parser_conformance.rs b/crates/html/tests/html5_parser_conformance.rs index f612dcfa..36ab3688 100644 --- a/crates/html/tests/html5_parser_conformance.rs +++ b/crates/html/tests/html5_parser_conformance.rs @@ -40,4 +40,35 @@ fn canonical_parser_conformance_corpus_executes_every_discovered_fixture() { ObservedToken::Eof, ]) ); + + let structured = reports + .iter() + .find(|report| report.fixture_id().as_str() == "document-structured-observations") + .expect("structured document fixture report must exist"); + assert_eq!( + structured + .delivery_results() + .iter() + .map(|delivery| delivery.delivery().as_str()) + .collect::