github.com/neilotoole/jsoncolor@v0.7.2-0.20231115150201-1637fae69be1/jsoncolor_test.go (about) 1 package jsoncolor_test 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "testing" 8 9 "github.com/segmentio/encoding/json" 10 11 "github.com/neilotoole/jsoncolor" 12 "github.com/stretchr/testify/require" 13 14 stdjson "encoding/json" 15 ) 16 17 // TestPackageDropIn checks that jsoncolor satisfies basic requirements 18 // to be a drop-in for encoding/json. 19 func TestPackageDropIn(t *testing.T) { 20 // Verify encoding/json types exists 21 var ( 22 _ = jsoncolor.Decoder{} 23 _ = jsoncolor.Delim(0) 24 _ = jsoncolor.Encoder{} 25 _ = jsoncolor.InvalidUnmarshalError{} 26 _ = jsoncolor.Marshaler(nil) 27 _ = jsoncolor.MarshalerError{} 28 _ = jsoncolor.Number("0") 29 _ = jsoncolor.RawMessage{} 30 _ = jsoncolor.SyntaxError{} 31 _ = jsoncolor.Token(nil) 32 _ = jsoncolor.UnmarshalTypeError{} 33 _ = jsoncolor.Unmarshaler(nil) 34 _ = jsoncolor.UnsupportedTypeError{} 35 _ = jsoncolor.UnsupportedValueError{} 36 ) 37 38 const prefix, indent = "", " " 39 40 testCases := []string{"testdata/sakila_actor.json", "testdata/sakila_payment.json"} 41 for _, tc := range testCases { 42 tc := tc 43 t.Run(tc, func(t *testing.T) { 44 b, readErr := ioutil.ReadFile(tc) 45 require.NoError(t, readErr) 46 47 // Test json.Valid equivalence 48 fv1, fv2 := json.Valid, jsoncolor.Valid 49 require.Equal(t, fv1(b), fv2(b)) 50 51 // Test json.Unmarshal equivalence 52 fu1, fu2 := json.Unmarshal, jsoncolor.Unmarshal 53 var m1, m2 interface{} 54 err1, err2 := fu1(b, &m1), fu2(b, &m2) 55 require.NoError(t, err1) 56 require.NoError(t, err2) 57 require.EqualValues(t, m1, m2) 58 59 // Test json.Marshal equivalence 60 fm1, fm2 := json.Marshal, jsoncolor.Marshal 61 gotMarshalB1, err1 := fm1(m1) 62 require.NoError(t, err1) 63 gotMarshalB2, err2 := fm2(m1) 64 require.NoError(t, err2) 65 require.Equal(t, gotMarshalB1, gotMarshalB2) 66 67 // Test json.MarshalIndent equivalence 68 fmi1, fmi2 := json.MarshalIndent, jsoncolor.MarshalIndent 69 gotMarshallIndentB1, err1 := fmi1(m1, prefix, indent) 70 require.NoError(t, err1) 71 gotMarshalIndentB2, err2 := fmi2(m1, prefix, indent) 72 require.NoError(t, err2) 73 require.Equal(t, gotMarshallIndentB1, gotMarshalIndentB2) 74 75 // Test json.Compact equivalence 76 fc1, fc2 := json.Compact, jsoncolor.Compact 77 buf1, buf2 := &bytes.Buffer{}, &bytes.Buffer{} 78 err1 = fc1(buf1, gotMarshallIndentB1) 79 require.NoError(t, err1) 80 err2 = fc2(buf2, gotMarshalIndentB2) 81 require.NoError(t, err2) 82 require.Equal(t, buf1.Bytes(), buf2.Bytes()) 83 // Double-check 84 require.Equal(t, buf1.Bytes(), gotMarshalB1) 85 require.Equal(t, buf2.Bytes(), gotMarshalB2) 86 buf1.Reset() 87 buf2.Reset() 88 89 // Test json.Indent equivalence 90 fi1, fi2 := json.Indent, jsoncolor.Indent 91 err1 = fi1(buf1, gotMarshalB1, prefix, indent) 92 require.NoError(t, err1) 93 err2 = fi2(buf2, gotMarshalB2, prefix, indent) 94 require.NoError(t, err2) 95 require.Equal(t, buf1.Bytes(), buf2.Bytes()) 96 buf1.Reset() 97 buf2.Reset() 98 99 // Test json.HTMLEscape equivalence 100 fh1, fh2 := json.HTMLEscape, jsoncolor.HTMLEscape 101 fh1(buf1, gotMarshalB1) 102 fh2(buf2, gotMarshalB2) 103 require.Equal(t, buf1.Bytes(), buf2.Bytes()) 104 }) 105 } 106 } 107 108 func TestEncode(t *testing.T) { 109 testCases := []struct { 110 name string 111 pretty bool 112 color bool 113 sortMap bool 114 v interface{} 115 want string 116 }{ 117 {name: "nil", pretty: false, v: nil, want: "null\n"}, 118 {name: "slice_empty", pretty: true, v: []int{}, want: "[]\n"}, 119 {name: "slice_1_pretty", pretty: true, v: []interface{}{1}, want: "[\n 1\n]\n"}, 120 {name: "slice_1_no_pretty", v: []interface{}{1}, want: "[1]\n"}, 121 {name: "slice_2_pretty", pretty: true, v: []interface{}{1, true}, want: "[\n 1,\n true\n]\n"}, 122 {name: "slice_2_no_pretty", v: []interface{}{1, true}, want: "[1,true]\n"}, 123 {name: "map_int_empty", pretty: true, v: map[string]int{}, want: "{}\n"}, 124 {name: "map_interface_empty", pretty: true, v: map[string]interface{}{}, want: "{}\n"}, 125 {name: "map_interface_empty_sorted", pretty: true, sortMap: true, v: map[string]interface{}{}, want: "{}\n"}, 126 {name: "map_1_pretty", pretty: true, sortMap: true, v: map[string]interface{}{"one": 1}, want: "{\n \"one\": 1\n}\n"}, 127 {name: "map_1_no_pretty", sortMap: true, v: map[string]interface{}{"one": 1}, want: "{\"one\":1}\n"}, 128 {name: "map_2_pretty", pretty: true, sortMap: true, v: map[string]interface{}{"one": 1, "two": 2}, want: "{\n \"one\": 1,\n \"two\": 2\n}\n"}, 129 {name: "map_2_no_pretty", sortMap: true, v: map[string]interface{}{"one": 1, "two": 2}, want: "{\"one\":1,\"two\":2}\n"}, 130 {name: "tinystruct", pretty: true, v: TinyStruct{FBool: true}, want: "{\n \"f_bool\": true\n}\n"}, 131 } 132 133 for _, tc := range testCases { 134 tc := tc 135 136 t.Run(tc.name, func(t *testing.T) { 137 buf := &bytes.Buffer{} 138 enc := jsoncolor.NewEncoder(buf) 139 enc.SetEscapeHTML(false) 140 enc.SetSortMapKeys(tc.sortMap) 141 if tc.pretty { 142 enc.SetIndent("", " ") 143 } 144 if tc.color { 145 clrs := jsoncolor.DefaultColors() 146 enc.SetColors(clrs) 147 } 148 149 require.NoError(t, enc.Encode(tc.v)) 150 require.True(t, stdjson.Valid(buf.Bytes())) 151 require.Equal(t, tc.want, buf.String()) 152 }) 153 } 154 } 155 156 func TestEncode_Slice(t *testing.T) { 157 testCases := []struct { 158 name string 159 pretty bool 160 color bool 161 v []interface{} 162 want string 163 }{ 164 {name: "nil", pretty: true, v: nil, want: "null\n"}, 165 {name: "empty", pretty: true, v: []interface{}{}, want: "[]\n"}, 166 {name: "one", pretty: true, v: []interface{}{1}, want: "[\n 1\n]\n"}, 167 {name: "two", pretty: true, v: []interface{}{1, true}, want: "[\n 1,\n true\n]\n"}, 168 {name: "three", pretty: true, v: []interface{}{1, true, "hello"}, want: "[\n 1,\n true,\n \"hello\"\n]\n"}, 169 } 170 171 for _, tc := range testCases { 172 tc := tc 173 174 t.Run(tc.name, func(t *testing.T) { 175 buf := &bytes.Buffer{} 176 enc := jsoncolor.NewEncoder(buf) 177 enc.SetEscapeHTML(false) 178 if tc.pretty { 179 enc.SetIndent("", " ") 180 } 181 if tc.color { 182 enc.SetColors(jsoncolor.DefaultColors()) 183 } 184 185 require.NoError(t, enc.Encode(tc.v)) 186 require.True(t, stdjson.Valid(buf.Bytes())) 187 require.Equal(t, tc.want, buf.String()) 188 }) 189 } 190 } 191 192 func TestEncode_SmallStruct(t *testing.T) { 193 v := SmallStruct{ 194 FInt: 7, 195 FSlice: []interface{}{64, true}, 196 FMap: map[string]interface{}{ 197 "m_float64": 64.64, 198 "m_string": "hello", 199 }, 200 FTinyStruct: TinyStruct{FBool: true}, 201 FString: "hello", 202 } 203 204 testCases := []struct { 205 pretty bool 206 color bool 207 want string 208 }{ 209 {pretty: false, color: false, want: "{\"f_int\":7,\"f_slice\":[64,true],\"f_map\":{\"m_float64\":64.64,\"m_string\":\"hello\"},\"f_tinystruct\":{\"f_bool\":true},\"f_string\":\"hello\"}\n"}, 210 {pretty: true, color: false, want: "{\n \"f_int\": 7,\n \"f_slice\": [\n 64,\n true\n ],\n \"f_map\": {\n \"m_float64\": 64.64,\n \"m_string\": \"hello\"\n },\n \"f_tinystruct\": {\n \"f_bool\": true\n },\n \"f_string\": \"hello\"\n}\n"}, 211 } 212 213 for _, tc := range testCases { 214 tc := tc 215 216 t.Run(fmt.Sprintf("pretty_%v__color_%v", tc.pretty, tc.color), func(t *testing.T) { 217 buf := &bytes.Buffer{} 218 enc := jsoncolor.NewEncoder(buf) 219 enc.SetEscapeHTML(false) 220 enc.SetSortMapKeys(true) 221 222 if tc.pretty { 223 enc.SetIndent("", " ") 224 } 225 if tc.color { 226 enc.SetColors(jsoncolor.DefaultColors()) 227 } 228 229 require.NoError(t, enc.Encode(v)) 230 require.True(t, stdjson.Valid(buf.Bytes())) 231 require.Equal(t, tc.want, buf.String()) 232 }) 233 } 234 } 235 236 func TestEncode_Map_Nested(t *testing.T) { 237 v := map[string]interface{}{ 238 "m_bool1": true, 239 "m_nest1": map[string]interface{}{ 240 "m_nest1_bool": true, 241 "m_nest2": map[string]interface{}{ 242 "m_nest2_bool": true, 243 "m_nest3": map[string]interface{}{ 244 "m_nest3_bool": true, 245 }, 246 }, 247 }, 248 "m_string1": "hello", 249 } 250 251 testCases := []struct { 252 pretty bool 253 color bool 254 want string 255 }{ 256 {pretty: false, want: "{\"m_bool1\":true,\"m_nest1\":{\"m_nest1_bool\":true,\"m_nest2\":{\"m_nest2_bool\":true,\"m_nest3\":{\"m_nest3_bool\":true}}},\"m_string1\":\"hello\"}\n"}, 257 {pretty: true, want: "{\n \"m_bool1\": true,\n \"m_nest1\": {\n \"m_nest1_bool\": true,\n \"m_nest2\": {\n \"m_nest2_bool\": true,\n \"m_nest3\": {\n \"m_nest3_bool\": true\n }\n }\n },\n \"m_string1\": \"hello\"\n}\n"}, 258 } 259 260 for _, tc := range testCases { 261 tc := tc 262 263 t.Run(fmt.Sprintf("pretty_%v__color_%v", tc.pretty, tc.color), func(t *testing.T) { 264 buf := &bytes.Buffer{} 265 enc := jsoncolor.NewEncoder(buf) 266 enc.SetEscapeHTML(false) 267 enc.SetSortMapKeys(true) 268 if tc.pretty { 269 enc.SetIndent("", " ") 270 } 271 if tc.color { 272 enc.SetColors(jsoncolor.DefaultColors()) 273 } 274 275 require.NoError(t, enc.Encode(v)) 276 require.True(t, stdjson.Valid(buf.Bytes())) 277 require.Equal(t, tc.want, buf.String()) 278 }) 279 } 280 } 281 282 // TestEncode_Map_StringNotInterface tests maps with a string key 283 // but the value type is not interface{}. 284 // For example, map[string]bool. This test is necessary because the 285 // encoder has a fast path for map[string]interface{} 286 func TestEncode_Map_StringNotInterface(t *testing.T) { 287 testCases := []struct { 288 pretty bool 289 color bool 290 sortMap bool 291 v map[string]bool 292 want string 293 }{ 294 {pretty: false, sortMap: true, v: map[string]bool{}, want: "{}\n"}, 295 {pretty: false, sortMap: false, v: map[string]bool{}, want: "{}\n"}, 296 {pretty: true, sortMap: true, v: map[string]bool{}, want: "{}\n"}, 297 {pretty: true, sortMap: false, v: map[string]bool{}, want: "{}\n"}, 298 {pretty: false, sortMap: true, v: map[string]bool{"one": true}, want: "{\"one\":true}\n"}, 299 {pretty: false, sortMap: false, v: map[string]bool{"one": true}, want: "{\"one\":true}\n"}, 300 {pretty: false, sortMap: true, v: map[string]bool{"one": true, "two": false}, want: "{\"one\":true,\"two\":false}\n"}, 301 {pretty: true, sortMap: true, v: map[string]bool{"one": true, "two": false}, want: "{\n \"one\": true,\n \"two\": false\n}\n"}, 302 } 303 304 for _, tc := range testCases { 305 tc := tc 306 307 t.Run(fmt.Sprintf("size_%d__pretty_%v__color_%v", len(tc.v), tc.pretty, tc.color), func(t *testing.T) { 308 buf := &bytes.Buffer{} 309 enc := jsoncolor.NewEncoder(buf) 310 enc.SetEscapeHTML(false) 311 enc.SetSortMapKeys(tc.sortMap) 312 if tc.pretty { 313 enc.SetIndent("", " ") 314 } 315 if tc.color { 316 enc.SetColors(jsoncolor.DefaultColors()) 317 } 318 319 require.NoError(t, enc.Encode(tc.v)) 320 require.True(t, stdjson.Valid(buf.Bytes())) 321 require.Equal(t, tc.want, buf.String()) 322 }) 323 } 324 } 325 326 func TestEncode_RawMessage(t *testing.T) { 327 type RawStruct struct { 328 FString string `json:"f_string"` 329 FRaw jsoncolor.RawMessage `json:"f_raw"` 330 } 331 332 raw := jsoncolor.RawMessage(`{"one":1,"two":2}`) 333 334 testCases := []struct { 335 name string 336 pretty bool 337 color bool 338 v interface{} 339 want string 340 }{ 341 {name: "empty", pretty: false, v: jsoncolor.RawMessage(`{}`), want: "{}\n"}, 342 {name: "no_pretty", pretty: false, v: raw, want: "{\"one\":1,\"two\":2}\n"}, 343 {name: "pretty", pretty: true, v: raw, want: "{\n \"one\": 1,\n \"two\": 2\n}\n"}, 344 {name: "pretty_struct", pretty: true, v: RawStruct{FString: "hello", FRaw: raw}, want: "{\n \"f_string\": \"hello\",\n \"f_raw\": {\n \"one\": 1,\n \"two\": 2\n }\n}\n"}, 345 } 346 347 for _, tc := range testCases { 348 tc := tc 349 350 t.Run(tc.name, func(t *testing.T) { 351 buf := &bytes.Buffer{} 352 enc := jsoncolor.NewEncoder(buf) 353 enc.SetEscapeHTML(false) 354 enc.SetSortMapKeys(true) 355 if tc.pretty { 356 enc.SetIndent("", " ") 357 } 358 if tc.color { 359 enc.SetColors(jsoncolor.DefaultColors()) 360 } 361 362 err := enc.Encode(tc.v) 363 require.NoError(t, err) 364 require.True(t, stdjson.Valid(buf.Bytes())) 365 require.Equal(t, tc.want, buf.String()) 366 }) 367 } 368 } 369 370 // TestEncode_Map_StringNotInterface tests map[string]json.RawMessage. 371 // This test is necessary because the encoder has a fast path 372 // for map[string]interface{}. 373 func TestEncode_Map_StringRawMessage(t *testing.T) { 374 t.Skipf(`Skipping due to intermittent behavior. 375 See: https://github.com/neilotoole/jsoncolor/issues/19`) 376 raw := jsoncolor.RawMessage(`{"one":1,"two":2}`) 377 378 testCases := []struct { 379 pretty bool 380 color bool 381 sortMap bool 382 v map[string]jsoncolor.RawMessage 383 want string 384 }{ 385 {pretty: false, sortMap: true, v: map[string]jsoncolor.RawMessage{}, want: "{}\n"}, 386 {pretty: false, sortMap: false, v: map[string]jsoncolor.RawMessage{}, want: "{}\n"}, 387 {pretty: true, sortMap: true, v: map[string]jsoncolor.RawMessage{}, want: "{}\n"}, 388 {pretty: true, sortMap: false, v: map[string]jsoncolor.RawMessage{}, want: "{}\n"}, 389 {pretty: false, sortMap: true, v: map[string]jsoncolor.RawMessage{"msg1": raw, "msg2": raw}, want: "{\"msg1\":{\"one\":1,\"two\":2},\"msg2\":{\"one\":1,\"two\":2}}\n"}, 390 {pretty: true, sortMap: true, v: map[string]jsoncolor.RawMessage{"msg1": raw, "msg2": raw}, want: "{\n \"msg1\": {\n \"one\": 1,\n \"two\": 2\n },\n \"msg2\": {\n \"one\": 1,\n \"two\": 2\n }\n}\n"}, 391 {pretty: true, sortMap: false, v: map[string]jsoncolor.RawMessage{"msg1": raw}, want: "{\n \"msg1\": {\n \"one\": 1,\n \"two\": 2\n }\n}\n"}, 392 } 393 394 for _, tc := range testCases { 395 tc := tc 396 397 name := fmt.Sprintf("size_%d__pretty_%v__color_%v__sort_%v", len(tc.v), tc.pretty, tc.color, tc.sortMap) 398 t.Run(name, func(t *testing.T) { 399 buf := &bytes.Buffer{} 400 enc := jsoncolor.NewEncoder(buf) 401 enc.SetEscapeHTML(false) 402 enc.SetSortMapKeys(tc.sortMap) 403 if tc.pretty { 404 enc.SetIndent("", " ") 405 } 406 if tc.color { 407 enc.SetColors(jsoncolor.DefaultColors()) 408 } 409 410 require.NoError(t, enc.Encode(tc.v)) 411 require.True(t, stdjson.Valid(buf.Bytes())) 412 require.Equal(t, tc.want, buf.String()) 413 }) 414 } 415 } 416 417 func TestEncode_BigStruct(t *testing.T) { 418 v := newBigStruct() 419 420 testCases := []struct { 421 pretty bool 422 color bool 423 want string 424 }{ 425 {pretty: false, want: "{\"f_int\":-7,\"f_int8\":-8,\"f_int16\":-16,\"f_int32\":-32,\"f_int64\":-64,\"f_uint\":7,\"f_uint8\":8,\"f_uint16\":16,\"f_uint32\":32,\"f_uint64\":64,\"f_float32\":32.32,\"f_float64\":64.64,\"f_bool\":true,\"f_bytes\":\"aGVsbG8=\",\"f_nil\":null,\"f_string\":\"hello\",\"f_map\":{\"m_bool\":true,\"m_int64\":64,\"m_nil\":null,\"m_smallstruct\":{\"f_int\":7,\"f_slice\":[64,true],\"f_map\":{\"m_float64\":64.64,\"m_string\":\"hello\"},\"f_tinystruct\":{\"f_bool\":true},\"f_string\":\"hello\"},\"m_string\":\"hello\"},\"f_smallstruct\":{\"f_int\":7,\"f_slice\":[64,true],\"f_map\":{\"m_float64\":64.64,\"m_string\":\"hello\"},\"f_tinystruct\":{\"f_bool\":true},\"f_string\":\"hello\"},\"f_interface\":\"hello\",\"f_interfaces\":[64,\"hello\",true]}\n"}, 426 {pretty: true, want: "{\n \"f_int\": -7,\n \"f_int8\": -8,\n \"f_int16\": -16,\n \"f_int32\": -32,\n \"f_int64\": -64,\n \"f_uint\": 7,\n \"f_uint8\": 8,\n \"f_uint16\": 16,\n \"f_uint32\": 32,\n \"f_uint64\": 64,\n \"f_float32\": 32.32,\n \"f_float64\": 64.64,\n \"f_bool\": true,\n \"f_bytes\": \"aGVsbG8=\",\n \"f_nil\": null,\n \"f_string\": \"hello\",\n \"f_map\": {\n \"m_bool\": true,\n \"m_int64\": 64,\n \"m_nil\": null,\n \"m_smallstruct\": {\n \"f_int\": 7,\n \"f_slice\": [\n 64,\n true\n ],\n \"f_map\": {\n \"m_float64\": 64.64,\n \"m_string\": \"hello\"\n },\n \"f_tinystruct\": {\n \"f_bool\": true\n },\n \"f_string\": \"hello\"\n },\n \"m_string\": \"hello\"\n },\n \"f_smallstruct\": {\n \"f_int\": 7,\n \"f_slice\": [\n 64,\n true\n ],\n \"f_map\": {\n \"m_float64\": 64.64,\n \"m_string\": \"hello\"\n },\n \"f_tinystruct\": {\n \"f_bool\": true\n },\n \"f_string\": \"hello\"\n },\n \"f_interface\": \"hello\",\n \"f_interfaces\": [\n 64,\n \"hello\",\n true\n ]\n}\n"}, 427 } 428 429 for _, tc := range testCases { 430 tc := tc 431 432 t.Run(fmt.Sprintf("pretty_%v__color_%v", tc.pretty, tc.color), func(t *testing.T) { 433 buf := &bytes.Buffer{} 434 enc := jsoncolor.NewEncoder(buf) 435 enc.SetEscapeHTML(false) 436 enc.SetSortMapKeys(true) 437 if tc.pretty { 438 enc.SetIndent("", " ") 439 } 440 if tc.color { 441 enc.SetColors(jsoncolor.DefaultColors()) 442 } 443 444 require.NoError(t, enc.Encode(v)) 445 require.True(t, stdjson.Valid(buf.Bytes())) 446 require.Equal(t, tc.want, buf.String()) 447 }) 448 } 449 } 450 451 // TestEncode_Map_Not_StringInterface tests map encoding where 452 // the map is not map[string]interface{} (for which the encoder 453 // has a fast path). 454 // 455 // NOTE: Currently the encoder is broken wrt colors enabled 456 // for non-string map keys, though that is kinda JSON-illegal anyway. 457 func TestEncode_Map_Not_StringInterface(t *testing.T) { 458 buf := &bytes.Buffer{} 459 enc := jsoncolor.NewEncoder(buf) 460 enc.SetEscapeHTML(false) 461 enc.SetSortMapKeys(true) 462 enc.SetColors(jsoncolor.DefaultColors()) 463 enc.SetIndent("", " ") 464 465 v := map[int32]string{ 466 0: "zero", 467 1: "one", 468 2: "two", 469 } 470 471 require.NoError(t, enc.Encode(v)) 472 require.False(t, stdjson.Valid(buf.Bytes()), 473 "expected to be invalid JSON because the encoder currently doesn't handle maps with non-string keys") 474 } 475 476 // BigStruct is a big test struct. 477 type BigStruct struct { 478 FInt int `json:"f_int"` 479 FInt8 int8 `json:"f_int8"` 480 FInt16 int16 `json:"f_int16"` 481 FInt32 int32 `json:"f_int32"` 482 FInt64 int64 `json:"f_int64"` 483 FUint uint `json:"f_uint"` 484 FUint8 uint8 `json:"f_uint8"` 485 FUint16 uint16 `json:"f_uint16"` 486 FUint32 uint32 `json:"f_uint32"` 487 FUint64 uint64 `json:"f_uint64"` 488 FFloat32 float32 `json:"f_float32"` 489 FFloat64 float64 `json:"f_float64"` 490 FBool bool `json:"f_bool"` 491 FBytes []byte `json:"f_bytes"` 492 FNil interface{} `json:"f_nil"` 493 FString string `json:"f_string"` 494 FMap map[string]interface{} `json:"f_map"` 495 FSmallStruct SmallStruct `json:"f_smallstruct"` 496 FInterface interface{} `json:"f_interface"` 497 FInterfaces []interface{} `json:"f_interfaces"` 498 } 499 500 // SmallStruct is a small test struct. 501 type SmallStruct struct { 502 FInt int `json:"f_int"` 503 FSlice []interface{} `json:"f_slice"` 504 FMap map[string]interface{} `json:"f_map"` 505 FTinyStruct TinyStruct `json:"f_tinystruct"` 506 FString string `json:"f_string"` 507 } 508 509 // Tiny Struct is a tiny test struct. 510 type TinyStruct struct { 511 FBool bool `json:"f_bool"` 512 } 513 514 func newBigStruct() BigStruct { 515 return BigStruct{ 516 FInt: -7, 517 FInt8: -8, 518 FInt16: -16, 519 FInt32: -32, 520 FInt64: -64, 521 FUint: 7, 522 FUint8: 8, 523 FUint16: 16, 524 FUint32: 32, 525 FUint64: 64, 526 FFloat32: 32.32, 527 FFloat64: 64.64, 528 FBool: true, 529 FBytes: []byte("hello"), 530 FNil: nil, 531 FString: "hello", 532 FMap: map[string]interface{}{ 533 "m_int64": int64(64), 534 "m_string": "hello", 535 "m_bool": true, 536 "m_nil": nil, 537 "m_smallstruct": newSmallStruct(), 538 }, 539 FSmallStruct: newSmallStruct(), 540 FInterface: interface{}("hello"), 541 FInterfaces: []interface{}{int64(64), "hello", true}, 542 } 543 } 544 545 func newSmallStruct() SmallStruct { 546 return SmallStruct{ 547 FInt: 7, 548 FSlice: []interface{}{64, true}, 549 FMap: map[string]interface{}{ 550 "m_float64": 64.64, 551 "m_string": "hello", 552 }, 553 FTinyStruct: TinyStruct{FBool: true}, 554 FString: "hello", 555 } 556 } 557 558 func TestEquivalenceRecords(t *testing.T) { 559 rec := makeRecords(t, 10000)[0] 560 561 bufStdj := &bytes.Buffer{} 562 err := stdjson.NewEncoder(bufStdj).Encode(rec) 563 require.NoError(t, err) 564 565 bufSegmentj := &bytes.Buffer{} 566 err = json.NewEncoder(bufSegmentj).Encode(rec) 567 require.NoError(t, err) 568 require.NotEqual(t, bufStdj.String(), bufSegmentj.String(), "segmentj encodes time.Duration to string; stdlib does not") 569 570 bufJ := &bytes.Buffer{} 571 err = jsoncolor.NewEncoder(bufJ).Encode(rec) 572 require.Equal(t, bufStdj.String(), bufJ.String()) 573 } 574 575 // TextMarshaler implements encoding.TextMarshaler 576 type TextMarshaler struct { 577 Text string 578 } 579 580 func (t TextMarshaler) MarshalText() ([]byte, error) { 581 return []byte(t.Text), nil 582 } 583 584 func TestEncode_TextMarshaler(t *testing.T) { 585 buf := &bytes.Buffer{} 586 enc := jsoncolor.NewEncoder(buf) 587 enc.SetColors(&jsoncolor.Colors{ 588 TextMarshaler: jsoncolor.Color("\x1b[36m"), 589 }) 590 591 text := TextMarshaler{Text: "example text"} 592 593 require.NoError(t, enc.Encode(text)) 594 require.Equal(t, "\x1b[36m\"example text\"\x1b[0m\n", buf.String(), 595 "expected TextMarshaler encoding to use Colors.TextMarshaler") 596 }