// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build goexperiment.jsonv2 package jsontext import ( "io" "strings" "testing" "encoding/json/internal/jsontest" "encoding/json/internal/jsonwire" ) type valueTestdataEntry struct { name jsontest.CaseName in string wantValid bool wantCompacted string wantCompactErr error // implies wantCompacted is in wantIndented string // wantCompacted if empty; uses "\t" for indent prefix and " " for indent wantIndentErr error // implies wantCompacted is in wantCanonicalized string // wantCompacted if empty wantCanonicalizeErr error // implies wantCompacted is in } var valueTestdata = append(func() (out []valueTestdataEntry) { // Initialize valueTestdata from coderTestdata. for _, td := range coderTestdata { // NOTE: The Compact method preserves the raw formatting of strings, // while the Encoder (by default) does not. if td.name.Name == "ComplicatedString" { td.outCompacted = strings.TrimSpace(td.in) } out = append(out, valueTestdataEntry{ name: td.name, in: td.in, wantValid: true, wantCompacted: td.outCompacted, wantIndented: td.outIndented, wantCanonicalized: td.outCanonicalized, }) } return out }(), []valueTestdataEntry{{ name: jsontest.Name("RFC8785/Primitives"), in: `{ "numbers": [333333333.33333329, 1E30, 4.50, 2e-3, 0.000000000000000000000000001, -0], "string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/", "literals": [null, true, false] }`, wantValid: true, wantCompacted: `{"numbers":[333333333.33333329,1E30,4.50,2e-3,0.000000000000000000000000001,-0],"string":"\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/","literals":[null,true,false]}`, wantIndented: `{ "numbers": [ 333333333.33333329, 1E30, 4.50, 2e-3, 0.000000000000000000000000001, -0 ], "string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/", "literals": [ null, true, false ] }`, wantCanonicalized: `{"literals":[null,true,false],"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27,0],"string":"€$\u000f\nA'B\"\\\\\"/"}`, }, { name: jsontest.Name("RFC8785/ObjectOrdering"), in: `{ "\u20ac": "Euro Sign", "\r": "Carriage Return", "\ufb33": "Hebrew Letter Dalet With Dagesh", "1": "One", "\ud83d\ude00": "Emoji: Grinning Face", "\u0080": "Control", "\u00f6": "Latin Small Letter O With Diaeresis" }`, wantValid: true, wantCompacted: `{"\u20ac":"Euro Sign","\r":"Carriage Return","\ufb33":"Hebrew Letter Dalet With Dagesh","1":"One","\ud83d\ude00":"Emoji: Grinning Face","\u0080":"Control","\u00f6":"Latin Small Letter O With Diaeresis"}`, wantIndented: `{ "\u20ac": "Euro Sign", "\r": "Carriage Return", "\ufb33": "Hebrew Letter Dalet With Dagesh", "1": "One", "\ud83d\ude00": "Emoji: Grinning Face", "\u0080": "Control", "\u00f6": "Latin Small Letter O With Diaeresis" }`, wantCanonicalized: `{"\r":"Carriage Return","1":"One","€":"Control","ö":"Latin Small Letter O With Diaeresis","€":"Euro Sign","😀":"Emoji: Grinning Face","דּ":"Hebrew Letter Dalet With Dagesh"}`, }, { name: jsontest.Name("LargeIntegers"), in: ` [ -9223372036854775808 , 9223372036854775807 ] `, wantValid: true, wantCompacted: `[-9223372036854775808,9223372036854775807]`, wantIndented: `[ -9223372036854775808, 9223372036854775807 ]`, wantCanonicalized: `[-9223372036854776000,9223372036854776000]`, // NOTE: Loss of precision due to numbers being treated as floats. }, { name: jsontest.Name("InvalidUTF8"), in: ` "living` + "\xde\xad\xbe\xef" + `\ufffd�" `, wantValid: false, // uses RFC 7493 as the definition; which validates UTF-8 wantCompacted: `"living` + "\xde\xad\xbe\xef" + `\ufffd�"`, wantCanonicalizeErr: E(jsonwire.ErrInvalidUTF8).withPos(` "living`+"\xde\xad", ""), }, { name: jsontest.Name("InvalidUTF8/SurrogateHalf"), in: `"\ud800"`, wantValid: false, // uses RFC 7493 as the definition; which validates UTF-8 wantCompacted: `"\ud800"`, wantCanonicalizeErr: newInvalidEscapeSequenceError(`\ud800"`).withPos(`"`, ""), }, { name: jsontest.Name("UppercaseEscaped"), in: `"\u000B"`, wantValid: true, wantCompacted: `"\u000B"`, wantCanonicalized: `"\u000b"`, }, { name: jsontest.Name("DuplicateNames"), in: ` { "0" : 0 , "1" : 1 , "0" : 0 }`, wantValid: false, // uses RFC 7493 as the definition; which does check for object uniqueness wantCompacted: `{"0":0,"1":1,"0":0}`, wantIndented: `{ "0": 0, "1": 1, "0": 0 }`, wantCanonicalizeErr: E(ErrDuplicateName).withPos(` { "0" : 0 , "1" : 1 , `, "/0"), }, { name: jsontest.Name("Whitespace"), in: " \n\r\t", wantValid: false, wantCompacted: " \n\r\t", wantCompactErr: E(io.ErrUnexpectedEOF).withPos(" \n\r\t", ""), wantIndentErr: E(io.ErrUnexpectedEOF).withPos(" \n\r\t", ""), wantCanonicalizeErr: E(io.ErrUnexpectedEOF).withPos(" \n\r\t", ""), }}...) func TestValueMethods(t *testing.T) { for _, td := range valueTestdata { t.Run(td.name.Name, func(t *testing.T) { if td.wantIndented == "" { td.wantIndented = td.wantCompacted } if td.wantCanonicalized == "" { td.wantCanonicalized = td.wantCompacted } if td.wantCompactErr != nil { td.wantCompacted = td.in } if td.wantIndentErr != nil { td.wantIndented = td.in } if td.wantCanonicalizeErr != nil { td.wantCanonicalized = td.in } v := Value(td.in) gotValid := v.IsValid() if gotValid != td.wantValid { t.Errorf("%s: Value.IsValid = %v, want %v", td.name.Where, gotValid, td.wantValid) } gotCompacted := Value(td.in) gotCompactErr := gotCompacted.Compact() if string(gotCompacted) != td.wantCompacted { t.Errorf("%s: Value.Compact = %s, want %s", td.name.Where, gotCompacted, td.wantCompacted) } if !equalError(gotCompactErr, td.wantCompactErr) { t.Errorf("%s: Value.Compact error mismatch:\ngot %v\nwant %v", td.name.Where, gotCompactErr, td.wantCompactErr) } gotIndented := Value(td.in) gotIndentErr := gotIndented.Indent(WithIndentPrefix("\t"), WithIndent(" ")) if string(gotIndented) != td.wantIndented { t.Errorf("%s: Value.Indent = %s, want %s", td.name.Where, gotIndented, td.wantIndented) } if !equalError(gotIndentErr, td.wantIndentErr) { t.Errorf("%s: Value.Indent error mismatch:\ngot %v\nwant %v", td.name.Where, gotIndentErr, td.wantIndentErr) } gotCanonicalized := Value(td.in) gotCanonicalizeErr := gotCanonicalized.Canonicalize() if string(gotCanonicalized) != td.wantCanonicalized { t.Errorf("%s: Value.Canonicalize = %s, want %s", td.name.Where, gotCanonicalized, td.wantCanonicalized) } if !equalError(gotCanonicalizeErr, td.wantCanonicalizeErr) { t.Errorf("%s: Value.Canonicalize error mismatch:\ngot %v\nwant %v", td.name.Where, gotCanonicalizeErr, td.wantCanonicalizeErr) } }) } }