package tce import ( "encoding/json" "os" "strings" "testing" ) // vectorsPath is the location of the frozen vector file relative to the // package's test working directory. const vectorsPath = "../../testdata/vectors/tce_vectors.json" type vectorFile struct { TceVersion int `json:"tce_version"` NumberCanonicalization struct { Accept map[string]string `json:"accept"` Reject map[string]string `json:"reject"` } `json:"number_canonicalization"` } func loadVectors(t *testing.T) *vectorFile { t.Helper() b, err := os.ReadFile(vectorsPath) if err != nil { t.Fatalf("read vectors: %v", err) } var vf vectorFile if err := json.Unmarshal(b, &vf); err != nil { t.Fatalf("parse vectors: %v", err) } return &vf } // TestNumberCanonicalizationVectors runs the frozen number vectors from the // reference implementation: every accepted token must canonicalize to exactly // the reference output, and every rejected token must be refused. func TestNumberCanonicalizationVectors(t *testing.T) { vf := loadVectors(t) accept := vf.NumberCanonicalization.Accept reject := vf.NumberCanonicalization.Reject if len(accept) != 28 { t.Errorf("expect 28 accepted tokens, got %d", len(accept)) } if len(reject) != 18 { t.Errorf("expect 18 rejected tokens, got %d", len(reject)) } for token, want := range accept { got, err := CanonicalNumber(token) if err != nil { t.Errorf("CanonicalNumber(%q): %v", token, err) continue } if got != want { t.Errorf("CanonicalNumber(%q) = %q, want %q", token, got, want) } } for token, reason := range reject { if strings.Contains(reason, "ERROR") { t.Errorf("reference said %q is accepted but it is in the reject list", token) } if _, err := CanonicalNumber(token); err == nil { t.Errorf("CanonicalNumber(%q) accepted, but the reference refuses it (%s)", token, reason) } } } // TestCanonicalNumberIdempotence pins the ยง5.1 properties that make the // encoding canonical: canonicalizing an already canonical token is a no-op, // and every accepted token yields an accepted, further-canonical result. func TestCanonicalNumberIdempotence(t *testing.T) { for _, token := range []string{ "0", "1", "-1", "1.5", "0.001", "12345678901234567890", "999999999999999999999999", "0.000000000000000001", "-0.000000000000000001", } { c, err := CanonicalNumber(token) if err != nil { t.Fatalf("CanonicalNumber(%q): %v", token, err) } again, err := CanonicalNumber(c) if err != nil { t.Fatalf("canonical form %q does not canonicalize: %v", c, err) } if again != c { t.Fatalf("canonical form not fixed point: %q -> %q", c, again) } if !IsCanonicalNumber(c) { t.Fatalf("IsCanonicalNumber(%q) = false", c) } } }