1// Copyright (C) MongoDB, Inc. 2014-present. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); you may 4// not use this file except in compliance with the License. You may obtain 5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6// 7// Based on github.com/golang/go by The Go Authors 8// See THIRD-PARTY-NOTICES for original license terms. 9 10package json 11 12import ( 13 "bytes" 14 "math" 15 "reflect" 16 "testing" 17 "unicode" 18 19 "github.com/mongodb/mongo-tools/legacy/testtype" 20) 21 22type Optionals struct { 23 Sr string `json:"sr"` 24 So string `json:"so,omitempty"` 25 Sw string `json:"-"` 26 27 Ir int `json:"omitempty"` // actually named omitempty, not an option 28 Io int `json:"io,omitempty"` 29 30 Slr []string `json:"slr,random"` 31 Slo []string `json:"slo,omitempty"` 32 33 Mr map[string]interface{} `json:"mr"` 34 Mo map[string]interface{} `json:",omitempty"` 35 36 Fr float64 `json:"fr"` 37 Fo float64 `json:"fo,omitempty"` 38 39 Br bool `json:"br"` 40 Bo bool `json:"bo,omitempty"` 41 42 Ur uint `json:"ur"` 43 Uo uint `json:"uo,omitempty"` 44 45 Str struct{} `json:"str"` 46 Sto struct{} `json:"sto,omitempty"` 47} 48 49var optionalsExpected = `{ 50 "sr": "", 51 "omitempty": 0, 52 "slr": null, 53 "mr": {}, 54 "fr": 0, 55 "br": false, 56 "ur": 0, 57 "str": {}, 58 "sto": {} 59}` 60 61func TestOmitEmpty(t *testing.T) { 62 testtype.SkipUnlessTestType(t, testtype.UnitTestType) 63 var o Optionals 64 o.Sw = "something" 65 o.Mr = map[string]interface{}{} 66 o.Mo = map[string]interface{}{} 67 68 got, err := MarshalIndent(&o, "", " ") 69 if err != nil { 70 t.Fatal(err) 71 } 72 if got := string(got); got != optionalsExpected { 73 t.Errorf(" got: %s\nwant: %s\n", got, optionalsExpected) 74 } 75} 76 77type StringTag struct { 78 BoolStr bool `json:",string"` 79 IntStr int64 `json:",string"` 80 StrStr string `json:",string"` 81} 82 83var stringTagExpected = `{ 84 "BoolStr": "true", 85 "IntStr": "42", 86 "StrStr": "\"xzbit\"" 87}` 88 89func TestStringTag(t *testing.T) { 90 testtype.SkipUnlessTestType(t, testtype.UnitTestType) 91 var s StringTag 92 s.BoolStr = true 93 s.IntStr = 42 94 s.StrStr = "xzbit" 95 got, err := MarshalIndent(&s, "", " ") 96 if err != nil { 97 t.Fatal(err) 98 } 99 if got := string(got); got != stringTagExpected { 100 t.Fatalf(" got: %s\nwant: %s\n", got, stringTagExpected) 101 } 102 103 // Verify that it round-trips. 104 var s2 StringTag 105 err = NewDecoder(bytes.NewReader(got)).Decode(&s2) 106 if err != nil { 107 t.Fatalf("Decode: %v", err) 108 } 109 if !reflect.DeepEqual(s, s2) { 110 t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", s, string(got), s2) 111 } 112} 113 114// byte slices are special even if they're renamed types. 115type renamedByte byte 116type renamedByteSlice []byte 117type renamedRenamedByteSlice []renamedByte 118 119func TestEncodeRenamedByteSlice(t *testing.T) { 120 testtype.SkipUnlessTestType(t, testtype.UnitTestType) 121 s := renamedByteSlice("abc") 122 result, err := Marshal(s) 123 if err != nil { 124 t.Fatal(err) 125 } 126 expect := `"YWJj"` 127 if string(result) != expect { 128 t.Errorf(" got %s want %s", result, expect) 129 } 130 r := renamedRenamedByteSlice("abc") 131 result, err = Marshal(r) 132 if err != nil { 133 t.Fatal(err) 134 } 135 if string(result) != expect { 136 t.Errorf(" got %s want %s", result, expect) 137 } 138} 139 140func TestFloatSpecialValues(t *testing.T) { 141 testtype.SkipUnlessTestType(t, testtype.UnitTestType) 142 _, err := Marshal(math.NaN()) 143 if err != nil { 144 t.Errorf("Got error for NaN: %v", err) 145 } 146 147 _, err = Marshal(math.Inf(-1)) 148 if err != nil { 149 t.Errorf("Got error for -Inf: %v", err) 150 } 151 152 _, err = Marshal(math.Inf(1)) 153 if err != nil { 154 t.Errorf("Got error for +Inf: %v", err) 155 } 156} 157 158// Ref has Marshaler and Unmarshaler methods with pointer receiver. 159type Ref int 160 161func (*Ref) MarshalJSON() ([]byte, error) { 162 return []byte(`"ref"`), nil 163} 164 165func (r *Ref) UnmarshalJSON([]byte) error { 166 *r = 12 167 return nil 168} 169 170// Val has Marshaler methods with value receiver. 171type Val int 172 173func (Val) MarshalJSON() ([]byte, error) { 174 return []byte(`"val"`), nil 175} 176 177// RefText has Marshaler and Unmarshaler methods with pointer receiver. 178type RefText int 179 180func (*RefText) MarshalText() ([]byte, error) { 181 return []byte(`"ref"`), nil 182} 183 184func (r *RefText) UnmarshalText([]byte) error { 185 *r = 13 186 return nil 187} 188 189// ValText has Marshaler methods with value receiver. 190type ValText int 191 192func (ValText) MarshalText() ([]byte, error) { 193 return []byte(`"val"`), nil 194} 195 196func TestRefValMarshal(t *testing.T) { 197 testtype.SkipUnlessTestType(t, testtype.UnitTestType) 198 var s = struct { 199 R0 Ref 200 R1 *Ref 201 R2 RefText 202 R3 *RefText 203 V0 Val 204 V1 *Val 205 V2 ValText 206 V3 *ValText 207 }{ 208 R0: 12, 209 R1: new(Ref), 210 R2: 14, 211 R3: new(RefText), 212 V0: 13, 213 V1: new(Val), 214 V2: 15, 215 V3: new(ValText), 216 } 217 const want = `{"R0":"ref","R1":"ref","R2":"\"ref\"","R3":"\"ref\"","V0":"val","V1":"val","V2":"\"val\"","V3":"\"val\""}` 218 b, err := Marshal(&s) 219 if err != nil { 220 t.Fatalf("Marshal: %v", err) 221 } 222 if got := string(b); got != want { 223 t.Errorf("got %q, want %q", got, want) 224 } 225} 226 227// C1 implements Marshaler and returns unescaped JSON. 228type C1 int 229 230func (C1) MarshalJSON() ([]byte, error) { 231 return []byte(`"<&>"`), nil 232} 233 234// CText implements Marshaler and returns unescaped text. 235type CText int 236 237func (CText) MarshalText() ([]byte, error) { 238 return []byte(`"<&>"`), nil 239} 240 241func TestMarshalerEscaping(t *testing.T) { 242 testtype.SkipUnlessTestType(t, testtype.UnitTestType) 243 var c C1 244 want := `"\u003c\u0026\u003e"` 245 b, err := Marshal(c) 246 if err != nil { 247 t.Fatalf("Marshal(c1): %v", err) 248 } 249 if got := string(b); got != want { 250 t.Errorf("Marshal(c1) = %#q, want %#q", got, want) 251 } 252 253 var ct CText 254 want = `"\"\u003c\u0026\u003e\""` 255 b, err = Marshal(ct) 256 if err != nil { 257 t.Fatalf("Marshal(ct): %v", err) 258 } 259 if got := string(b); got != want { 260 t.Errorf("Marshal(ct) = %#q, want %#q", got, want) 261 } 262} 263 264type IntType int 265 266type MyStruct struct { 267 IntType 268} 269 270func TestAnonymousNonstruct(t *testing.T) { 271 testtype.SkipUnlessTestType(t, testtype.UnitTestType) 272 var i IntType = 11 273 a := MyStruct{i} 274 const want = `{"IntType":11}` 275 276 b, err := Marshal(a) 277 if err != nil { 278 t.Fatalf("Marshal: %v", err) 279 } 280 if got := string(b); got != want { 281 t.Errorf("got %q, want %q", got, want) 282 } 283} 284 285type BugA struct { 286 S string 287} 288 289type BugB struct { 290 BugA 291 S string 292} 293 294type BugC struct { 295 S string 296} 297 298// Legal Go: We never use the repeated embedded field (S). 299type BugX struct { 300 A int 301 BugA 302 BugB 303} 304 305// Issue 5245. 306func TestEmbeddedBug(t *testing.T) { 307 testtype.SkipUnlessTestType(t, testtype.UnitTestType) 308 v := BugB{ 309 BugA{"A"}, 310 "B", 311 } 312 b, err := Marshal(v) 313 if err != nil { 314 t.Fatal("Marshal:", err) 315 } 316 want := `{"S":"B"}` 317 got := string(b) 318 if got != want { 319 t.Fatalf("Marshal: got %s want %s", got, want) 320 } 321 // Now check that the duplicate field, S, does not appear. 322 x := BugX{ 323 A: 23, 324 } 325 b, err = Marshal(x) 326 if err != nil { 327 t.Fatal("Marshal:", err) 328 } 329 want = `{"A":23}` 330 got = string(b) 331 if got != want { 332 t.Fatalf("Marshal: got %s want %s", got, want) 333 } 334} 335 336type BugD struct { // Same as BugA after tagging. 337 XXX string `json:"S"` 338} 339 340// BugD's tagged S field should dominate BugA's. 341type BugY struct { 342 BugA 343 BugD 344} 345 346// Test that a field with a tag dominates untagged fields. 347func TestTaggedFieldDominates(t *testing.T) { 348 testtype.SkipUnlessTestType(t, testtype.UnitTestType) 349 v := BugY{ 350 BugA{"BugA"}, 351 BugD{"BugD"}, 352 } 353 b, err := Marshal(v) 354 if err != nil { 355 t.Fatal("Marshal:", err) 356 } 357 want := `{"S":"BugD"}` 358 got := string(b) 359 if got != want { 360 t.Fatalf("Marshal: got %s want %s", got, want) 361 } 362} 363 364// There are no tags here, so S should not appear. 365type BugZ struct { 366 BugA 367 BugC 368 BugY // Contains a tagged S field through BugD; should not dominate. 369} 370 371func TestDuplicatedFieldDisappears(t *testing.T) { 372 testtype.SkipUnlessTestType(t, testtype.UnitTestType) 373 v := BugZ{ 374 BugA{"BugA"}, 375 BugC{"BugC"}, 376 BugY{ 377 BugA{"nested BugA"}, 378 BugD{"nested BugD"}, 379 }, 380 } 381 b, err := Marshal(v) 382 if err != nil { 383 t.Fatal("Marshal:", err) 384 } 385 want := `{}` 386 got := string(b) 387 if got != want { 388 t.Fatalf("Marshal: got %s want %s", got, want) 389 } 390} 391 392func TestStringBytes(t *testing.T) { 393 testtype.SkipUnlessTestType(t, testtype.UnitTestType) 394 // Test that encodeState.stringBytes and encodeState.string use the same encoding. 395 es := &encodeState{} 396 var r []rune 397 for i := '\u0000'; i <= unicode.MaxRune; i++ { 398 r = append(r, i) 399 } 400 s := string(r) + "\xff\xff\xffhello" // some invalid UTF-8 too 401 _, err := es.string(s) 402 if err != nil { 403 t.Fatal(err) 404 } 405 406 esBytes := &encodeState{} 407 _, err = esBytes.stringBytes([]byte(s)) 408 if err != nil { 409 t.Fatal(err) 410 } 411 412 enc := es.Buffer.String() 413 encBytes := esBytes.Buffer.String() 414 if enc != encBytes { 415 i := 0 416 for i < len(enc) && i < len(encBytes) && enc[i] == encBytes[i] { 417 i++ 418 } 419 enc = enc[i:] 420 encBytes = encBytes[i:] 421 i = 0 422 for i < len(enc) && i < len(encBytes) && enc[len(enc)-i-1] == encBytes[len(encBytes)-i-1] { 423 i++ 424 } 425 enc = enc[:len(enc)-i] 426 encBytes = encBytes[:len(encBytes)-i] 427 428 if len(enc) > 20 { 429 enc = enc[:20] + "..." 430 } 431 if len(encBytes) > 20 { 432 encBytes = encBytes[:20] + "..." 433 } 434 435 t.Errorf("encodings differ at %#q vs %#q", enc, encBytes) 436 } 437} 438 439func TestIssue6458(t *testing.T) { 440 testtype.SkipUnlessTestType(t, testtype.UnitTestType) 441 type Foo struct { 442 M RawMessage 443 } 444 x := Foo{RawMessage(`"foo"`)} 445 446 b, err := Marshal(&x) 447 if err != nil { 448 t.Fatal(err) 449 } 450 if want := `{"M":"foo"}`; string(b) != want { 451 t.Errorf("Marshal(&x) = %#q; want %#q", b, want) 452 } 453 454 b, err = Marshal(x) 455 if err != nil { 456 t.Fatal(err) 457 } 458 459 if want := `{"M":"ImZvbyI="}`; string(b) != want { 460 t.Errorf("Marshal(x) = %#q; want %#q", b, want) 461 } 462} 463 464func TestHTMLEscape(t *testing.T) { 465 testtype.SkipUnlessTestType(t, testtype.UnitTestType) 466 var b, want bytes.Buffer 467 m := `{"M":"<html>foo &` + "\xe2\x80\xa8 \xe2\x80\xa9" + `</html>"}` 468 want.Write([]byte(`{"M":"\u003chtml\u003efoo \u0026\u2028 \u2029\u003c/html\u003e"}`)) 469 HTMLEscape(&b, []byte(m)) 470 if !bytes.Equal(b.Bytes(), want.Bytes()) { 471 t.Errorf("HTMLEscape(&b, []byte(m)) = %s; want %s", b.Bytes(), want.Bytes()) 472 } 473} 474