Source file src/encoding/json/jsontext/value_test.go

     1  // Copyright 2020 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build goexperiment.jsonv2
     6  
     7  package jsontext
     8  
     9  import (
    10  	"io"
    11  	"strings"
    12  	"testing"
    13  
    14  	"encoding/json/internal/jsontest"
    15  	"encoding/json/internal/jsonwire"
    16  )
    17  
    18  type valueTestdataEntry struct {
    19  	name                jsontest.CaseName
    20  	in                  string
    21  	wantValid           bool
    22  	wantCompacted       string
    23  	wantCompactErr      error  // implies wantCompacted is in
    24  	wantIndented        string // wantCompacted if empty; uses "\t" for indent prefix and "    " for indent
    25  	wantIndentErr       error  // implies wantCompacted is in
    26  	wantCanonicalized   string // wantCompacted if empty
    27  	wantCanonicalizeErr error  // implies wantCompacted is in
    28  }
    29  
    30  var valueTestdata = append(func() (out []valueTestdataEntry) {
    31  	// Initialize valueTestdata from coderTestdata.
    32  	for _, td := range coderTestdata {
    33  		// NOTE: The Compact method preserves the raw formatting of strings,
    34  		// while the Encoder (by default) does not.
    35  		if td.name.Name == "ComplicatedString" {
    36  			td.outCompacted = strings.TrimSpace(td.in)
    37  		}
    38  		out = append(out, valueTestdataEntry{
    39  			name:              td.name,
    40  			in:                td.in,
    41  			wantValid:         true,
    42  			wantCompacted:     td.outCompacted,
    43  			wantIndented:      td.outIndented,
    44  			wantCanonicalized: td.outCanonicalized,
    45  		})
    46  	}
    47  	return out
    48  }(), []valueTestdataEntry{{
    49  	name: jsontest.Name("RFC8785/Primitives"),
    50  	in: `{
    51  		"numbers": [333333333.33333329, 1E30, 4.50,
    52  					2e-3, 0.000000000000000000000000001, -0],
    53  		"string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/",
    54  		"literals": [null, true, false]
    55  	}`,
    56  	wantValid:     true,
    57  	wantCompacted: `{"numbers":[333333333.33333329,1E30,4.50,2e-3,0.000000000000000000000000001,-0],"string":"\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/","literals":[null,true,false]}`,
    58  	wantIndented: `{
    59  	    "numbers": [
    60  	        333333333.33333329,
    61  	        1E30,
    62  	        4.50,
    63  	        2e-3,
    64  	        0.000000000000000000000000001,
    65  	        -0
    66  	    ],
    67  	    "string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/",
    68  	    "literals": [
    69  	        null,
    70  	        true,
    71  	        false
    72  	    ]
    73  	}`,
    74  	wantCanonicalized: `{"literals":[null,true,false],"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27,0],"string":"€$\u000f\nA'B\"\\\\\"/"}`,
    75  }, {
    76  	name: jsontest.Name("RFC8785/ObjectOrdering"),
    77  	in: `{
    78  		"\u20ac": "Euro Sign",
    79  		"\r": "Carriage Return",
    80  		"\ufb33": "Hebrew Letter Dalet With Dagesh",
    81  		"1": "One",
    82  		"\ud83d\ude00": "Emoji: Grinning Face",
    83  		"\u0080": "Control",
    84  		"\u00f6": "Latin Small Letter O With Diaeresis"
    85  	}`,
    86  	wantValid:     true,
    87  	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"}`,
    88  	wantIndented: `{
    89  	    "\u20ac": "Euro Sign",
    90  	    "\r": "Carriage Return",
    91  	    "\ufb33": "Hebrew Letter Dalet With Dagesh",
    92  	    "1": "One",
    93  	    "\ud83d\ude00": "Emoji: Grinning Face",
    94  	    "\u0080": "Control",
    95  	    "\u00f6": "Latin Small Letter O With Diaeresis"
    96  	}`,
    97  	wantCanonicalized: `{"\r":"Carriage Return","1":"One","€":"Control","ö":"Latin Small Letter O With Diaeresis","€":"Euro Sign","😀":"Emoji: Grinning Face","דּ":"Hebrew Letter Dalet With Dagesh"}`,
    98  }, {
    99  	name:          jsontest.Name("LargeIntegers"),
   100  	in:            ` [ -9223372036854775808 , 9223372036854775807 ] `,
   101  	wantValid:     true,
   102  	wantCompacted: `[-9223372036854775808,9223372036854775807]`,
   103  	wantIndented: `[
   104  	    -9223372036854775808,
   105  	    9223372036854775807
   106  	]`,
   107  	wantCanonicalized: `[-9223372036854776000,9223372036854776000]`, // NOTE: Loss of precision due to numbers being treated as floats.
   108  }, {
   109  	name:                jsontest.Name("InvalidUTF8"),
   110  	in:                  `  "living` + "\xde\xad\xbe\xef" + `\ufffd�"  `,
   111  	wantValid:           false, // uses RFC 7493 as the definition; which validates UTF-8
   112  	wantCompacted:       `"living` + "\xde\xad\xbe\xef" + `\ufffd�"`,
   113  	wantCanonicalizeErr: E(jsonwire.ErrInvalidUTF8).withPos(`  "living`+"\xde\xad", ""),
   114  }, {
   115  	name:                jsontest.Name("InvalidUTF8/SurrogateHalf"),
   116  	in:                  `"\ud800"`,
   117  	wantValid:           false, // uses RFC 7493 as the definition; which validates UTF-8
   118  	wantCompacted:       `"\ud800"`,
   119  	wantCanonicalizeErr: newInvalidEscapeSequenceError(`\ud800"`).withPos(`"`, ""),
   120  }, {
   121  	name:              jsontest.Name("UppercaseEscaped"),
   122  	in:                `"\u000B"`,
   123  	wantValid:         true,
   124  	wantCompacted:     `"\u000B"`,
   125  	wantCanonicalized: `"\u000b"`,
   126  }, {
   127  	name:          jsontest.Name("DuplicateNames"),
   128  	in:            ` { "0" : 0 , "1" : 1 , "0" : 0 }`,
   129  	wantValid:     false, // uses RFC 7493 as the definition; which does check for object uniqueness
   130  	wantCompacted: `{"0":0,"1":1,"0":0}`,
   131  	wantIndented: `{
   132  	    "0": 0,
   133  	    "1": 1,
   134  	    "0": 0
   135  	}`,
   136  	wantCanonicalizeErr: E(ErrDuplicateName).withPos(` { "0" : 0 , "1" : 1 , `, "/0"),
   137  }, {
   138  	name:                jsontest.Name("Whitespace"),
   139  	in:                  " \n\r\t",
   140  	wantValid:           false,
   141  	wantCompacted:       " \n\r\t",
   142  	wantCompactErr:      E(io.ErrUnexpectedEOF).withPos(" \n\r\t", ""),
   143  	wantIndentErr:       E(io.ErrUnexpectedEOF).withPos(" \n\r\t", ""),
   144  	wantCanonicalizeErr: E(io.ErrUnexpectedEOF).withPos(" \n\r\t", ""),
   145  }}...)
   146  
   147  func TestValueMethods(t *testing.T) {
   148  	for _, td := range valueTestdata {
   149  		t.Run(td.name.Name, func(t *testing.T) {
   150  			if td.wantIndented == "" {
   151  				td.wantIndented = td.wantCompacted
   152  			}
   153  			if td.wantCanonicalized == "" {
   154  				td.wantCanonicalized = td.wantCompacted
   155  			}
   156  			if td.wantCompactErr != nil {
   157  				td.wantCompacted = td.in
   158  			}
   159  			if td.wantIndentErr != nil {
   160  				td.wantIndented = td.in
   161  			}
   162  			if td.wantCanonicalizeErr != nil {
   163  				td.wantCanonicalized = td.in
   164  			}
   165  
   166  			v := Value(td.in)
   167  			gotValid := v.IsValid()
   168  			if gotValid != td.wantValid {
   169  				t.Errorf("%s: Value.IsValid = %v, want %v", td.name.Where, gotValid, td.wantValid)
   170  			}
   171  
   172  			gotCompacted := Value(td.in)
   173  			gotCompactErr := gotCompacted.Compact()
   174  			if string(gotCompacted) != td.wantCompacted {
   175  				t.Errorf("%s: Value.Compact = %s, want %s", td.name.Where, gotCompacted, td.wantCompacted)
   176  			}
   177  			if !equalError(gotCompactErr, td.wantCompactErr) {
   178  				t.Errorf("%s: Value.Compact error mismatch:\ngot  %v\nwant %v", td.name.Where, gotCompactErr, td.wantCompactErr)
   179  			}
   180  
   181  			gotIndented := Value(td.in)
   182  			gotIndentErr := gotIndented.Indent(WithIndentPrefix("\t"), WithIndent("    "))
   183  			if string(gotIndented) != td.wantIndented {
   184  				t.Errorf("%s: Value.Indent = %s, want %s", td.name.Where, gotIndented, td.wantIndented)
   185  			}
   186  			if !equalError(gotIndentErr, td.wantIndentErr) {
   187  				t.Errorf("%s: Value.Indent error mismatch:\ngot  %v\nwant %v", td.name.Where, gotIndentErr, td.wantIndentErr)
   188  			}
   189  
   190  			gotCanonicalized := Value(td.in)
   191  			gotCanonicalizeErr := gotCanonicalized.Canonicalize()
   192  			if string(gotCanonicalized) != td.wantCanonicalized {
   193  				t.Errorf("%s: Value.Canonicalize = %s, want %s", td.name.Where, gotCanonicalized, td.wantCanonicalized)
   194  			}
   195  			if !equalError(gotCanonicalizeErr, td.wantCanonicalizeErr) {
   196  				t.Errorf("%s: Value.Canonicalize error mismatch:\ngot  %v\nwant %v", td.name.Where, gotCanonicalizeErr, td.wantCanonicalizeErr)
   197  			}
   198  		})
   199  	}
   200  }
   201  

View as plain text