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