k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/internal/third_party/go-json-experiment/json/value_test.go (about)

     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  package json
     6  
     7  import (
     8  	"reflect"
     9  	"strings"
    10  	"testing"
    11  	"unicode/utf16"
    12  )
    13  
    14  type rawValueTestdataEntry struct {
    15  	name                testName
    16  	in                  string
    17  	wantValid           bool
    18  	wantCompacted       string
    19  	wantCompactErr      error  // implies wantCompacted is in
    20  	wantIndented        string // wantCompacted if empty; uses "\t" for indent prefix and "    " for indent
    21  	wantIndentErr       error  // implies wantCompacted is in
    22  	wantCanonicalized   string // wantCompacted if empty
    23  	wantCanonicalizeErr error  // implies wantCompacted is in
    24  }
    25  
    26  var rawValueTestdata = append(func() (out []rawValueTestdataEntry) {
    27  	// Initialize rawValueTestdata from coderTestdata.
    28  	for _, td := range coderTestdata {
    29  		// NOTE: The Compact method preserves the raw formatting of strings,
    30  		// while the Encoder (by default) does not.
    31  		if td.name.name == "ComplicatedString" {
    32  			td.outCompacted = strings.TrimSpace(td.in)
    33  		}
    34  		out = append(out, rawValueTestdataEntry{
    35  			name:              td.name,
    36  			in:                td.in,
    37  			wantValid:         true,
    38  			wantCompacted:     td.outCompacted,
    39  			wantIndented:      td.outIndented,
    40  			wantCanonicalized: td.outCanonicalized,
    41  		})
    42  	}
    43  	return out
    44  }(), []rawValueTestdataEntry{{
    45  	name: name("RFC8785/Primitives"),
    46  	in: `{
    47  		"numbers": [333333333.33333329, 1E30, 4.50,
    48  					2e-3, 0.000000000000000000000000001],
    49  		"string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/",
    50  		"literals": [null, true, false]
    51  	}`,
    52  	wantValid:     true,
    53  	wantCompacted: `{"numbers":[333333333.33333329,1E30,4.50,2e-3,0.000000000000000000000000001],"string":"\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/","literals":[null,true,false]}`,
    54  	wantIndented: `{
    55  	    "numbers": [
    56  	        333333333.33333329,
    57  	        1E30,
    58  	        4.50,
    59  	        2e-3,
    60  	        0.000000000000000000000000001
    61  	    ],
    62  	    "string": "\u20ac$\u000F\u000aA'\u0042\u0022\u005c\\\"\/",
    63  	    "literals": [
    64  	        null,
    65  	        true,
    66  	        false
    67  	    ]
    68  	}`,
    69  	wantCanonicalized: `{"literals":[null,true,false],"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27],"string":"€$\u000f\nA'B\"\\\\\"/"}`,
    70  }, {
    71  	name: name("RFC8785/ObjectOrdering"),
    72  	in: `{
    73  		"\u20ac": "Euro Sign",
    74  		"\r": "Carriage Return",
    75  		"\ufb33": "Hebrew Letter Dalet With Dagesh",
    76  		"1": "One",
    77  		"\ud83d\ude00": "Emoji: Grinning Face",
    78  		"\u0080": "Control",
    79  		"\u00f6": "Latin Small Letter O With Diaeresis"
    80  	}`,
    81  	wantValid:     true,
    82  	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"}`,
    83  	wantIndented: `{
    84  	    "\u20ac": "Euro Sign",
    85  	    "\r": "Carriage Return",
    86  	    "\ufb33": "Hebrew Letter Dalet With Dagesh",
    87  	    "1": "One",
    88  	    "\ud83d\ude00": "Emoji: Grinning Face",
    89  	    "\u0080": "Control",
    90  	    "\u00f6": "Latin Small Letter O With Diaeresis"
    91  	}`,
    92  	wantCanonicalized: `{"\r":"Carriage Return","1":"One","€":"Control","ö":"Latin Small Letter O With Diaeresis","€":"Euro Sign","😀":"Emoji: Grinning Face","דּ":"Hebrew Letter Dalet With Dagesh"}`,
    93  }, {
    94  	name:          name("LargeIntegers"),
    95  	in:            ` [ -9223372036854775808 , 9223372036854775807 ] `,
    96  	wantValid:     true,
    97  	wantCompacted: `[-9223372036854775808,9223372036854775807]`,
    98  	wantIndented: `[
    99  	    -9223372036854775808,
   100  	    9223372036854775807
   101  	]`,
   102  	wantCanonicalized: `[-9223372036854776000,9223372036854776000]`, // NOTE: Loss of precision due to numbers being treated as floats.
   103  }, {
   104  	name:                name("InvalidUTF8"),
   105  	in:                  `  "living` + "\xde\xad\xbe\xef" + `\ufffd�"  `,
   106  	wantValid:           false, // uses RFC 7493 as the definition; which validates UTF-8
   107  	wantCompacted:       `"living` + "\xde\xad\xbe\xef" + `\ufffd�"`,
   108  	wantCanonicalizeErr: &SyntacticError{str: "invalid UTF-8 within string"},
   109  }, {
   110  	name:                name("InvalidUTF8/SurrogateHalf"),
   111  	in:                  `"\ud800"`,
   112  	wantValid:           false, // uses RFC 7493 as the definition; which validates UTF-8
   113  	wantCompacted:       `"\ud800"`,
   114  	wantCanonicalizeErr: &SyntacticError{str: `invalid escape sequence "\"" within string`},
   115  }, {
   116  	name:              name("UppercaseEscaped"),
   117  	in:                `"\u000B"`,
   118  	wantValid:         true,
   119  	wantCompacted:     `"\u000B"`,
   120  	wantCanonicalized: `"\u000b"`,
   121  }, {
   122  	name:          name("DuplicateNames"),
   123  	in:            ` { "0" : 0 , "1" : 1 , "0" : 0 }`,
   124  	wantValid:     false, // uses RFC 7493 as the definition; which does check for object uniqueness
   125  	wantCompacted: `{"0":0,"1":1,"0":0}`,
   126  	wantIndented: `{
   127  	    "0": 0,
   128  	    "1": 1,
   129  	    "0": 0
   130  	}`,
   131  	wantCanonicalizeErr: &SyntacticError{str: `duplicate name "0" in object`},
   132  }}...)
   133  
   134  func TestRawValueMethods(t *testing.T) {
   135  	for _, td := range rawValueTestdata {
   136  		t.Run(td.name.name, func(t *testing.T) {
   137  			if td.wantIndented == "" {
   138  				td.wantIndented = td.wantCompacted
   139  			}
   140  			if td.wantCanonicalized == "" {
   141  				td.wantCanonicalized = td.wantCompacted
   142  			}
   143  			if td.wantCompactErr != nil {
   144  				td.wantCompacted = td.in
   145  			}
   146  			if td.wantIndentErr != nil {
   147  				td.wantIndented = td.in
   148  			}
   149  			if td.wantCanonicalizeErr != nil {
   150  				td.wantCanonicalized = td.in
   151  			}
   152  
   153  			v := RawValue(td.in)
   154  			gotValid := v.IsValid()
   155  			if gotValid != td.wantValid {
   156  				t.Errorf("%s: RawValue.IsValid = %v, want %v", td.name.where, gotValid, td.wantValid)
   157  			}
   158  
   159  			gotCompacted := RawValue(td.in)
   160  			gotCompactErr := gotCompacted.Compact()
   161  			if string(gotCompacted) != td.wantCompacted {
   162  				t.Errorf("%s: RawValue.Compact = %s, want %s", td.name.where, gotCompacted, td.wantCompacted)
   163  			}
   164  			if !reflect.DeepEqual(gotCompactErr, td.wantCompactErr) {
   165  				t.Errorf("%s: RawValue.Compact error mismatch: got %#v, want %#v", td.name.where, gotCompactErr, td.wantCompactErr)
   166  			}
   167  
   168  			gotIndented := RawValue(td.in)
   169  			gotIndentErr := gotIndented.Indent("\t", "    ")
   170  			if string(gotIndented) != td.wantIndented {
   171  				t.Errorf("%s: RawValue.Indent = %s, want %s", td.name.where, gotIndented, td.wantIndented)
   172  			}
   173  			if !reflect.DeepEqual(gotIndentErr, td.wantIndentErr) {
   174  				t.Errorf("%s: RawValue.Indent error mismatch: got %#v, want %#v", td.name.where, gotIndentErr, td.wantIndentErr)
   175  			}
   176  
   177  			gotCanonicalized := RawValue(td.in)
   178  			gotCanonicalizeErr := gotCanonicalized.Canonicalize()
   179  			if string(gotCanonicalized) != td.wantCanonicalized {
   180  				t.Errorf("%s: RawValue.Canonicalize = %s, want %s", td.name.where, gotCanonicalized, td.wantCanonicalized)
   181  			}
   182  			if !reflect.DeepEqual(gotCanonicalizeErr, td.wantCanonicalizeErr) {
   183  				t.Errorf("%s: RawValue.Canonicalize error mismatch: got %#v, want %#v", td.name.where, gotCanonicalizeErr, td.wantCanonicalizeErr)
   184  			}
   185  		})
   186  	}
   187  }
   188  
   189  var lessUTF16Testdata = []string{"", "\r", "1", "\u0080", "\u00f6", "\u20ac", "\U0001f600", "\ufb33"}
   190  
   191  func TestLessUTF16(t *testing.T) {
   192  	for i, si := range lessUTF16Testdata {
   193  		for j, sj := range lessUTF16Testdata {
   194  			got := lessUTF16([]byte(si), []byte(sj))
   195  			want := i < j
   196  			if got != want {
   197  				t.Errorf("lessUTF16(%q, %q) = %v, want %v", si, sj, got, want)
   198  			}
   199  		}
   200  	}
   201  }
   202  
   203  func FuzzLessUTF16(f *testing.F) {
   204  	for _, td1 := range lessUTF16Testdata {
   205  		for _, td2 := range lessUTF16Testdata {
   206  			f.Add([]byte(td1), []byte(td2))
   207  		}
   208  	}
   209  
   210  	// lessUTF16Simple is identical to lessUTF16,
   211  	// but relies on naively converting a string to a []uint16 codepoints.
   212  	// It is easy to verify as correct, but is slow.
   213  	lessUTF16Simple := func(x, y []byte) bool {
   214  		ux := utf16.Encode([]rune(string(x)))
   215  		uy := utf16.Encode([]rune(string(y)))
   216  		// TODO(https://go.dev/issue/57433): Use slices.Compare.
   217  		for {
   218  			if len(ux) == 0 || len(uy) == 0 {
   219  				if len(ux) == len(uy) {
   220  					return string(x) < string(y)
   221  				}
   222  				return len(ux) < len(uy)
   223  			}
   224  			if ux[0] != uy[0] {
   225  				return ux[0] < uy[0]
   226  			}
   227  			ux, uy = ux[1:], uy[1:]
   228  		}
   229  	}
   230  
   231  	f.Fuzz(func(t *testing.T, s1, s2 []byte) {
   232  		// Compare the optimized and simplified implementations.
   233  		got := lessUTF16(s1, s2)
   234  		want := lessUTF16Simple(s1, s2)
   235  		if got != want {
   236  			t.Errorf("lessUTF16(%q, %q) = %v, want %v", s1, s2, got, want)
   237  		}
   238  	})
   239  }