github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/json/encode_test.go (about) 1 // Copyright 2017 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package json 12 13 import ( 14 "flag" 15 "fmt" 16 "io/ioutil" 17 "math/rand" 18 "path/filepath" 19 "runtime" 20 "strings" 21 "testing" 22 23 "github.com/cockroachdb/cockroach/pkg/util/timeutil" 24 ) 25 26 var rewriteResultsInTestfiles = flag.Bool( 27 "rewrite-results-in-testfiles", false, 28 "ignore the expected results and rewrite the test files with the actual results from this "+ 29 "run. Used to update tests when a change affects many cases; please verify the testfile "+ 30 "diffs carefully!", 31 ) 32 33 func assertEncodeRoundTrip(t *testing.T, j JSON) { 34 encoded, err := EncodeJSON(nil, j) 35 if err != nil { 36 t.Fatal(j, err) 37 } 38 _, decoded, err := DecodeJSON(encoded) 39 if err != nil { 40 t.Fatal(j, err) 41 } 42 43 c, err := j.Compare(decoded) 44 if err != nil { 45 t.Fatal(j, err) 46 } 47 if c != 0 { 48 t.Fatalf("expected %s, got %s (encoding %v)", j, decoded, encoded) 49 } 50 } 51 52 func TestJSONRandomEncodeRoundTrip(t *testing.T) { 53 rng := rand.New(rand.NewSource(timeutil.Now().Unix())) 54 for i := 0; i < 1000; i++ { 55 j, err := Random(20, rng) 56 if err != nil { 57 t.Fatal(err) 58 } 59 60 assertEncodeRoundTrip(t, j) 61 } 62 } 63 64 func TestFilesEncode(t *testing.T) { 65 _, fname, _, ok := runtime.Caller(0) 66 if !ok { 67 t.Fatal("couldn't get directory") 68 } 69 dir := filepath.Join(filepath.Dir(fname), "testdata", "raw") 70 dirContents, err := ioutil.ReadDir(dir) 71 if err != nil { 72 t.Fatal(err) 73 } 74 75 numFilesRan := 0 76 77 for _, tc := range dirContents { 78 if !strings.HasSuffix(tc.Name(), ".json") { 79 continue 80 } 81 t.Run(tc.Name(), func(t *testing.T) { 82 numFilesRan++ 83 path := filepath.Join(dir, tc.Name()) 84 contents, err := ioutil.ReadFile(path) 85 if err != nil { 86 t.Fatal(err) 87 } 88 jsonString := string(contents) 89 90 j, err := ParseJSON(jsonString) 91 if err != nil { 92 t.Fatal(err) 93 } 94 95 encoded, err := EncodeJSON(nil, j) 96 if err != nil { 97 t.Fatal(err) 98 } 99 100 t.Run(`round trip`, func(t *testing.T) { 101 _, decoded, err := DecodeJSON(encoded) 102 if err != nil { 103 t.Fatal(err) 104 } 105 106 newStr := decoded.String() 107 if newStr != j.String() { 108 t.Fatalf("expected %s, got %s", jsonString, newStr) 109 } 110 }) 111 112 // If this test is failing because you changed the encoding of JSON values, 113 // rerun with -rewrite-results-in-testfiles. 114 t.Run(`explicit encoding`, func(t *testing.T) { 115 stringifiedEncoding := fmt.Sprintf("%v", encoded) 116 fixtureFilename := filepath.Join( 117 filepath.Dir(fname), 118 "testdata", "encoded", 119 tc.Name()+".bytes", 120 ) 121 122 if *rewriteResultsInTestfiles { 123 err := ioutil.WriteFile(fixtureFilename, []byte(stringifiedEncoding), 0644) 124 if err != nil { 125 t.Fatal(err) 126 } 127 } 128 129 expected, err := ioutil.ReadFile(fixtureFilename) 130 if err != nil { 131 t.Fatal(err) 132 } 133 134 if string(expected) != stringifiedEncoding { 135 t.Fatalf("expected %s, got %s", string(expected), stringifiedEncoding) 136 } 137 }) 138 }) 139 } 140 141 // Sanity check. 142 if numFilesRan == 0 { 143 t.Fatal("didn't find any test files!") 144 } 145 } 146 147 func TestJSONEncodeRoundTrip(t *testing.T) { 148 cases := []string{ 149 `true`, 150 `false`, 151 `null`, 152 `""`, 153 `"🤔"`, 154 `"\""`, 155 `"\n"`, 156 `"hello"`, 157 `"goodbye"`, 158 `1`, 159 `1.00`, 160 `100`, 161 `-1`, 162 `1000000000000000`, 163 `100000000000000000000000000000000000`, 164 `0e1`, 165 `[]`, 166 `["hello"]`, 167 `[1]`, 168 `[1, 2]`, 169 `[1, 2, 3]`, 170 `[100000000000000000000000000000000000]`, 171 `[1, true, "three"]`, 172 `[[1]]`, 173 `[[1], [1], [[1]]]`, 174 `[[[["hello"]]]]`, 175 `{}`, 176 `{"a": 1, "b": 2}`, 177 `{"b": 1, "a": 2}`, 178 `{"a": [1, 2, 3]}`, 179 `{"a": [{"b": 2}, {"b": 4}]}`, 180 `[1, {"a": 3}, null, {"b": null}]`, 181 `{"🤔": "foo"}`, 182 `"6U閆崬밺뀫颒myj츥휘:$薈mY햚#rz飏+玭V㭢뾿愴YꖚX亥ᮉ푊\u0006垡㐭룝\"厓ᔧḅ^Sqpv媫\"⤽걒\"˽Ἆ?ꇆ䬔未tv{DV鯀Tἆl凸g\\㈭ĭ즿UH㽤"`, 183 `{"a":"b","c":"d"}`, 184 } 185 186 for _, tc := range cases { 187 j, err := ParseJSON(tc) 188 if err != nil { 189 t.Fatal(err) 190 } 191 192 assertEncodeRoundTrip(t, j) 193 } 194 } 195 196 // This tests that the stringified version is the same, for testing precision 197 // is maintained for numbers. This will not maintain, for example, the 198 // ordering of object keys. 199 func TestJSONEncodeStrictRoundTrip(t *testing.T) { 200 cases := []string{ 201 `1`, 202 `1.00`, 203 `100`, 204 `-1`, 205 `-1.000000000`, 206 `-1000000000`, 207 `1.1231231230`, 208 `1.1231231230000`, 209 `1.1231231230000000`, 210 `0E+1`, 211 } 212 213 for _, tc := range cases { 214 j, err := ParseJSON(tc) 215 if err != nil { 216 t.Fatal(err) 217 } 218 219 encoded, err := EncodeJSON(nil, j) 220 if err != nil { 221 t.Fatal(err) 222 } 223 _, decoded, err := DecodeJSON(encoded) 224 if err != nil { 225 t.Fatal(err) 226 } 227 228 newStr := decoded.String() 229 if newStr != tc { 230 t.Fatalf("expected %s, got %s", tc, newStr) 231 } 232 } 233 } 234 235 // Taken from Wikipedia's JSON page. 236 const sampleJSON = `{ 237 "firstName": "John", 238 "lastName": "Smith", 239 "isAlive": true, 240 "age": 25, 241 "address": { 242 "streetAddress": "21 2nd Street", 243 "city": "New York", 244 "state": "NY", 245 "postalCode": "10021-3100" 246 }, 247 "phoneNumbers": [ 248 { 249 "type": "home", 250 "number": "212 555-1234" 251 }, 252 { 253 "type": "office", 254 "number": "646 555-4567" 255 }, 256 { 257 "type": "mobile", 258 "number": "123 456-7890" 259 } 260 ], 261 "children": [], 262 "spouse": null 263 }` 264 265 func BenchmarkEncodeJSON(b *testing.B) { 266 j := jsonTestShorthand(sampleJSON) 267 268 b.ResetTimer() 269 270 for i := 0; i < b.N; i++ { 271 _, _ = EncodeJSON(nil, j) 272 } 273 } 274 275 func BenchmarkDecodeJSON(b *testing.B) { 276 j := jsonTestShorthand(sampleJSON) 277 278 b.ResetTimer() 279 bytes, err := EncodeJSON(nil, j) 280 if err != nil { 281 b.Fatal(err) 282 } 283 284 for i := 0; i < b.N; i++ { 285 _, _, _ = DecodeJSON(bytes) 286 } 287 }