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 }