Artifact 0ed5fd4f15cd16a7b9bbaab622369ccbb4819f80738f4c3e1586dc1b364fae82:
- File
conformance/artifact_gaps_test.go
— part of check-in
[127685f0f5]
at
2026-10-12 17:39:36
on branch main
— fix(deck): parse D and E timestamps with the full 4.6.4 grammar (#271) (#337)
The parser tried only two time.Parse layouts and rewrote a lowercase t to T, so it rejected the HH:MM, timezone, hour-24, Feb-31 and negative-year forms fossil accepts via SQLite's julianday(), and it silently accepted a lowercase t separator the draft rejects.
Add deck/timestamp.go: a byte cursor that parses the date-time form (optional leading minus, YYYY-MM-DD, one or more whitespace-or-T separators, HH:MM with optional seconds and fraction, an optional Z/z or +HH:MM/-HH:MM zone), the date-only form, and a bare Julian-day number, with the exact-digit field ranges the 4.6.4 erratum scopes to those forms and the "strictly after -4713-11-24" lower bound. A lowercase t separator is rejected. time.Date normalizes the hour-24 and unchecked-day forms the way julianday() does. (user: [email protected], size: 7957) [annotate] [blame] [check-ins using]
package conformance import "testing" // artifactGaps marks the artifact cases go-libfossil is known to fail, by // case ID, grouped by the code at fault, each with the issue tracking its fix. var artifactGaps = map[string]string{ // Marshal drops a present but empty C, N or T value, an empty P card and an // empty W body, so the artifact does not re-encode (deck/marshal.go:58, :149, // :153, :194, :205). "artifact.4.7.16.t-empty-value": "#272", "artifact.4.7.18.w-empty": "#272", "artifact.4.7.c-empty": "#272", "artifact.4.7.n-empty": "#272", "artifact.4.7.p-empty": "#272", // Marshal writes D to the millisecond and E to the second, so a longer // fraction the parser accepted is lost (deck/marshal.go:63, :67). Found // by FuzzArtifactRoundTrip. "artifact.4.6.4.fraction-round-trip": "#272", "artifact.4.6.4.e-fraction-round-trip": "#272", // No card-legality table, required cards or per-type semantic checks: // inferType only picks a type (deck/parse.go:442). "artifact.5.2.attachment-illegal-B": "#273", "artifact.5.2.attachment-illegal-E": "#273", "artifact.5.2.attachment-illegal-F": "#273", "artifact.5.2.attachment-illegal-G": "#273", "artifact.5.2.attachment-illegal-H": "#273", "artifact.5.2.attachment-illegal-I": "#273", "artifact.5.2.attachment-illegal-J": "#273", "artifact.5.2.attachment-illegal-K": "#273", "artifact.5.2.attachment-illegal-L": "#273", "artifact.5.2.attachment-illegal-M": "#273", "artifact.5.2.attachment-illegal-P": "#273", "artifact.5.2.attachment-illegal-Q": "#273", "artifact.5.2.attachment-illegal-R": "#273", "artifact.5.2.attachment-illegal-T": "#273", "artifact.5.2.attachment-illegal-W": "#273", "artifact.5.2.attachment-no-D": "#273", "artifact.5.2.cluster-illegal-A": "#273", "artifact.5.2.cluster-illegal-B": "#273", "artifact.5.2.cluster-illegal-C": "#273", "artifact.5.2.cluster-illegal-D": "#273", "artifact.5.2.cluster-illegal-E": "#273", "artifact.5.2.cluster-illegal-F": "#273", "artifact.5.2.cluster-illegal-G": "#273", "artifact.5.2.cluster-illegal-H": "#273", "artifact.5.2.cluster-illegal-I": "#273", "artifact.5.2.cluster-illegal-J": "#273", "artifact.5.2.cluster-illegal-K": "#273", "artifact.5.2.cluster-illegal-L": "#273", "artifact.5.2.cluster-illegal-N": "#273", "artifact.5.2.cluster-illegal-P": "#273", "artifact.5.2.cluster-illegal-Q": "#273", "artifact.5.2.cluster-illegal-R": "#273", "artifact.5.2.cluster-illegal-T": "#273", "artifact.5.2.cluster-illegal-U": "#273", "artifact.5.2.cluster-illegal-W": "#273", "artifact.5.2.cluster-no-M": "#273", "artifact.5.2.control-illegal-A": "#273", "artifact.5.2.control-illegal-E": "#273", "artifact.5.2.control-illegal-G": "#273", "artifact.5.2.control-illegal-H": "#273", "artifact.5.2.control-illegal-I": "#273", "artifact.5.2.control-illegal-J": "#273", "artifact.5.2.control-illegal-K": "#273", "artifact.5.2.control-illegal-L": "#273", "artifact.5.2.control-illegal-M": "#273", "artifact.5.2.control-illegal-N": "#273", "artifact.5.2.control-illegal-W": "#273", "artifact.5.2.control-no-D": "#273", "artifact.5.2.control-no-T": "#273", "artifact.5.2.control-no-U": "#273", "artifact.5.2.event-illegal-A": "#273", "artifact.5.2.event-illegal-B": "#273", "artifact.5.2.event-illegal-F": "#273", "artifact.5.2.event-illegal-G": "#273", "artifact.5.2.event-illegal-H": "#273", "artifact.5.2.event-illegal-I": "#273", "artifact.5.2.event-illegal-J": "#273", "artifact.5.2.event-illegal-K": "#273", "artifact.5.2.event-illegal-L": "#273", "artifact.5.2.event-illegal-M": "#273", "artifact.5.2.event-illegal-Q": "#273", "artifact.5.2.event-illegal-R": "#273", "artifact.5.2.event-no-D": "#273", "artifact.5.2.event-no-E": "#273", "artifact.5.2.event-no-W": "#273", "artifact.5.2.forum-illegal-A": "#273", "artifact.5.2.forum-illegal-B": "#273", "artifact.5.2.forum-illegal-C": "#273", "artifact.5.2.forum-illegal-E": "#273", "artifact.5.2.forum-illegal-F": "#273", "artifact.5.2.forum-illegal-J": "#273", "artifact.5.2.forum-illegal-K": "#273", "artifact.5.2.forum-illegal-L": "#273", "artifact.5.2.forum-illegal-M": "#273", "artifact.5.2.forum-illegal-Q": "#273", "artifact.5.2.forum-illegal-R": "#273", "artifact.5.2.forum-illegal-T": "#273", "artifact.5.2.forum-no-D": "#273", "artifact.5.2.forum-no-U": "#273", "artifact.5.2.forum-no-W": "#273", "artifact.5.2.manifest-illegal-A": "#273", "artifact.5.2.manifest-illegal-E": "#273", "artifact.5.2.manifest-illegal-G": "#273", "artifact.5.2.manifest-illegal-H": "#273", "artifact.5.2.manifest-illegal-I": "#273", "artifact.5.2.manifest-illegal-J": "#273", "artifact.5.2.manifest-illegal-K": "#273", "artifact.5.2.manifest-illegal-L": "#273", "artifact.5.2.manifest-illegal-M": "#273", "artifact.5.2.manifest-illegal-W": "#273", "artifact.5.2.manifest-no-D": "#273", "artifact.5.2.ticket-illegal-A": "#273", "artifact.5.2.ticket-illegal-B": "#273", "artifact.5.2.ticket-illegal-C": "#273", "artifact.5.2.ticket-illegal-E": "#273", "artifact.5.2.ticket-illegal-F": "#273", "artifact.5.2.ticket-illegal-G": "#273", "artifact.5.2.ticket-illegal-H": "#273", "artifact.5.2.ticket-illegal-I": "#273", "artifact.5.2.ticket-illegal-L": "#273", "artifact.5.2.ticket-illegal-M": "#273", "artifact.5.2.ticket-illegal-N": "#273", "artifact.5.2.ticket-illegal-P": "#273", "artifact.5.2.ticket-illegal-Q": "#273", "artifact.5.2.ticket-illegal-R": "#273", "artifact.5.2.ticket-illegal-T": "#273", "artifact.5.2.ticket-illegal-W": "#273", "artifact.5.2.ticket-no-D": "#273", "artifact.5.2.ticket-no-J": "#273", "artifact.5.2.ticket-no-K": "#273", "artifact.5.2.ticket-no-U": "#273", "artifact.5.2.wiki-illegal-A": "#273", "artifact.5.2.wiki-illegal-B": "#273", "artifact.5.2.wiki-illegal-E": "#273", "artifact.5.2.wiki-illegal-F": "#273", "artifact.5.2.wiki-illegal-G": "#273", "artifact.5.2.wiki-illegal-H": "#273", "artifact.5.2.wiki-illegal-I": "#273", "artifact.5.2.wiki-illegal-J": "#273", "artifact.5.2.wiki-illegal-K": "#273", "artifact.5.2.wiki-illegal-M": "#273", "artifact.5.2.wiki-illegal-Q": "#273", "artifact.5.2.wiki-illegal-R": "#273", "artifact.5.2.wiki-illegal-T": "#273", "artifact.5.2.wiki-no-D": "#273", "artifact.5.2.wiki-no-L": "#273", "artifact.5.2.wiki-no-U": "#273", "artifact.5.2.wiki-no-W": "#273", "artifact.5.3.latest-setter-illegal": "#273", "artifact.5.4.control-self": "#273", "artifact.5.4.event-cancel": "#273", "artifact.5.4.event-external-tag": "#273", "artifact.5.4.event-propagating": "#273", "artifact.5.4.forum-many-parents": "#273", "artifact.5.4.forum-title-reply": "#273", } // TestArtifactGapKeys checks that every marked case exists, and that every // loss the fuzz test forgives is still a marked gap. func TestArtifactGapKeys(t *testing.T) { ids := map[string]bool{} for _, c := range artifactSuiteCases() { ids[c.ID] = true } for id := range artifactGaps { if !ids[id] { t.Errorf("artifactGaps names %s, which is not a case", id) } } for _, loss := range knownLosses { if artifactGaps[loss.caseID] == "" { t.Errorf("FuzzArtifactRoundTrip forgives %s, which is no longer a gap; "+ "remove it from knownLosses", loss.caseID) } } }