Source file src/encoding/json/internal/jsonopts/options_test.go

     1  // Copyright 2023 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  //go:build goexperiment.jsonv2
     6  
     7  package jsonopts_test
     8  
     9  import (
    10  	"reflect"
    11  	"testing"
    12  
    13  	"encoding/json/internal/jsonflags"
    14  	. "encoding/json/internal/jsonopts"
    15  	"encoding/json/jsontext"
    16  	"encoding/json/v2"
    17  )
    18  
    19  func makeFlags(f ...jsonflags.Bools) (fs jsonflags.Flags) {
    20  	for _, f := range f {
    21  		fs.Set(f)
    22  	}
    23  	return fs
    24  }
    25  
    26  func TestJoin(t *testing.T) {
    27  	tests := []struct {
    28  		in            Options
    29  		excludeCoders bool
    30  		want          *Struct
    31  	}{{
    32  		in:   jsonflags.AllowInvalidUTF8 | 1,
    33  		want: &Struct{Flags: makeFlags(jsonflags.AllowInvalidUTF8 | 1)},
    34  	}, {
    35  		in: jsonflags.Multiline | 0,
    36  		want: &Struct{
    37  			Flags: makeFlags(jsonflags.AllowInvalidUTF8|1, jsonflags.Multiline|0)},
    38  	}, {
    39  		in: Indent("\t"), // implicitly sets Multiline=true
    40  		want: &Struct{
    41  			Flags:       makeFlags(jsonflags.AllowInvalidUTF8 | jsonflags.Multiline | jsonflags.Indent | 1),
    42  			CoderValues: CoderValues{Indent: "\t"},
    43  		},
    44  	}, {
    45  		in: &Struct{
    46  			Flags: makeFlags(jsonflags.Multiline|jsonflags.EscapeForJS|0, jsonflags.AllowInvalidUTF8|1),
    47  		},
    48  		want: &Struct{
    49  			Flags:       makeFlags(jsonflags.AllowInvalidUTF8|jsonflags.Indent|1, jsonflags.Multiline|jsonflags.EscapeForJS|0),
    50  			CoderValues: CoderValues{Indent: "\t"},
    51  		},
    52  	}, {
    53  		in: &DefaultOptionsV1,
    54  		want: func() *Struct {
    55  			v1 := DefaultOptionsV1
    56  			v1.Flags.Set(jsonflags.Indent | 1)
    57  			v1.Flags.Set(jsonflags.Multiline | 0)
    58  			v1.Indent = "\t"
    59  			return &v1
    60  		}(), // v1 fully replaces before (except for whitespace related flags)
    61  	}, {
    62  		in: &DefaultOptionsV2,
    63  		want: func() *Struct {
    64  			v2 := DefaultOptionsV2
    65  			v2.Flags.Set(jsonflags.Indent | 1)
    66  			v2.Flags.Set(jsonflags.Multiline | 0)
    67  			v2.Indent = "\t"
    68  			return &v2
    69  		}(), // v2 fully replaces before (except for whitespace related flags)
    70  	}, {
    71  		in: jsonflags.Deterministic | jsonflags.AllowInvalidUTF8 | 1, excludeCoders: true,
    72  		want: func() *Struct {
    73  			v2 := DefaultOptionsV2
    74  			v2.Flags.Set(jsonflags.Deterministic | 1)
    75  			v2.Flags.Set(jsonflags.Indent | 1)
    76  			v2.Flags.Set(jsonflags.Multiline | 0)
    77  			v2.Indent = "\t"
    78  			return &v2
    79  		}(),
    80  	}, {
    81  		in: jsontext.WithIndentPrefix("    "), excludeCoders: true,
    82  		want: func() *Struct {
    83  			v2 := DefaultOptionsV2
    84  			v2.Flags.Set(jsonflags.Deterministic | 1)
    85  			v2.Flags.Set(jsonflags.Indent | 1)
    86  			v2.Flags.Set(jsonflags.Multiline | 0)
    87  			v2.Indent = "\t"
    88  			return &v2
    89  		}(),
    90  	}, {
    91  		in: jsontext.WithIndentPrefix("    "), excludeCoders: false,
    92  		want: func() *Struct {
    93  			v2 := DefaultOptionsV2
    94  			v2.Flags.Set(jsonflags.Deterministic | 1)
    95  			v2.Flags.Set(jsonflags.Indent | 1)
    96  			v2.Flags.Set(jsonflags.IndentPrefix | 1)
    97  			v2.Flags.Set(jsonflags.Multiline | 1)
    98  			v2.Indent = "\t"
    99  			v2.IndentPrefix = "    "
   100  			return &v2
   101  		}(),
   102  	}, {
   103  		in: &Struct{
   104  			Flags: jsonflags.Flags{
   105  				Presence: uint64(jsonflags.Deterministic | jsonflags.Indent | jsonflags.IndentPrefix),
   106  				Values:   uint64(jsonflags.Indent | jsonflags.IndentPrefix),
   107  			},
   108  			CoderValues: CoderValues{Indent: "  ", IndentPrefix: "  "},
   109  		},
   110  		excludeCoders: true,
   111  		want: func() *Struct {
   112  			v2 := DefaultOptionsV2
   113  			v2.Flags.Set(jsonflags.Indent | 1)
   114  			v2.Flags.Set(jsonflags.IndentPrefix | 1)
   115  			v2.Flags.Set(jsonflags.Multiline | 1)
   116  			v2.Indent = "\t"
   117  			v2.IndentPrefix = "    "
   118  			return &v2
   119  		}(),
   120  	}, {
   121  		in: &Struct{
   122  			Flags: jsonflags.Flags{
   123  				Presence: uint64(jsonflags.Deterministic | jsonflags.Indent | jsonflags.IndentPrefix),
   124  				Values:   uint64(jsonflags.Indent | jsonflags.IndentPrefix),
   125  			},
   126  			CoderValues: CoderValues{Indent: "  ", IndentPrefix: "  "},
   127  		},
   128  		excludeCoders: false,
   129  		want: func() *Struct {
   130  			v2 := DefaultOptionsV2
   131  			v2.Flags.Set(jsonflags.Indent | 1)
   132  			v2.Flags.Set(jsonflags.IndentPrefix | 1)
   133  			v2.Flags.Set(jsonflags.Multiline | 1)
   134  			v2.Indent = "  "
   135  			v2.IndentPrefix = "  "
   136  			return &v2
   137  		}(),
   138  	}}
   139  	got := new(Struct)
   140  	for i, tt := range tests {
   141  		if tt.excludeCoders {
   142  			got.JoinWithoutCoderOptions(tt.in)
   143  		} else {
   144  			got.Join(tt.in)
   145  		}
   146  		if !reflect.DeepEqual(got, tt.want) {
   147  			t.Fatalf("%d: Join:\n\tgot:  %+v\n\twant: %+v", i, got, tt.want)
   148  		}
   149  	}
   150  }
   151  
   152  func TestGet(t *testing.T) {
   153  	opts := &Struct{
   154  		Flags:        makeFlags(jsonflags.Indent|jsonflags.Deterministic|jsonflags.Marshalers|1, jsonflags.Multiline|0),
   155  		CoderValues:  CoderValues{Indent: "\t"},
   156  		ArshalValues: ArshalValues{Marshalers: new(json.Marshalers)},
   157  	}
   158  	if v, ok := json.GetOption(nil, jsontext.AllowDuplicateNames); v || ok {
   159  		t.Errorf("GetOption(..., AllowDuplicateNames) = (%v, %v), want (false, false)", v, ok)
   160  	}
   161  	if v, ok := json.GetOption(jsonflags.AllowInvalidUTF8|0, jsontext.AllowDuplicateNames); v || ok {
   162  		t.Errorf("GetOption(..., AllowDuplicateNames) = (%v, %v), want (false, false)", v, ok)
   163  	}
   164  	if v, ok := json.GetOption(jsonflags.AllowDuplicateNames|0, jsontext.AllowDuplicateNames); v || !ok {
   165  		t.Errorf("GetOption(..., AllowDuplicateNames) = (%v, %v), want (false, true)", v, ok)
   166  	}
   167  	if v, ok := json.GetOption(jsonflags.AllowDuplicateNames|1, jsontext.AllowDuplicateNames); !v || !ok {
   168  		t.Errorf("GetOption(..., AllowDuplicateNames) = (%v, %v), want (true, true)", v, ok)
   169  	}
   170  	if v, ok := json.GetOption(Indent(""), jsontext.AllowDuplicateNames); v || ok {
   171  		t.Errorf("GetOption(..., AllowDuplicateNames) = (%v, %v), want (false, false)", v, ok)
   172  	}
   173  	if v, ok := json.GetOption(Indent(" "), jsontext.WithIndent); v != " " || !ok {
   174  		t.Errorf(`GetOption(..., WithIndent) = (%q, %v), want (" ", true)`, v, ok)
   175  	}
   176  	if v, ok := json.GetOption(jsonflags.AllowDuplicateNames|1, jsontext.WithIndent); v != "" || ok {
   177  		t.Errorf(`GetOption(..., WithIndent) = (%q, %v), want ("", false)`, v, ok)
   178  	}
   179  	if v, ok := json.GetOption(opts, jsontext.AllowDuplicateNames); v || ok {
   180  		t.Errorf("GetOption(..., AllowDuplicateNames) = (%v, %v), want (false, false)", v, ok)
   181  	}
   182  	if v, ok := json.GetOption(opts, json.Deterministic); !v || !ok {
   183  		t.Errorf("GetOption(..., Deterministic) = (%v, %v), want (true, true)", v, ok)
   184  	}
   185  	if v, ok := json.GetOption(opts, jsontext.Multiline); v || !ok {
   186  		t.Errorf("GetOption(..., Multiline) = (%v, %v), want (false, true)", v, ok)
   187  	}
   188  	if v, ok := json.GetOption(opts, jsontext.AllowInvalidUTF8); v || ok {
   189  		t.Errorf("GetOption(..., AllowInvalidUTF8) = (%v, %v), want (false, false)", v, ok)
   190  	}
   191  	if v, ok := json.GetOption(opts, jsontext.WithIndent); v != "\t" || !ok {
   192  		t.Errorf(`GetOption(..., WithIndent) = (%q, %v), want ("\t", true)`, v, ok)
   193  	}
   194  	if v, ok := json.GetOption(opts, jsontext.WithIndentPrefix); v != "" || ok {
   195  		t.Errorf(`GetOption(..., WithIndentPrefix) = (%q, %v), want ("", false)`, v, ok)
   196  	}
   197  	if v, ok := json.GetOption(opts, json.WithMarshalers); v == nil || !ok {
   198  		t.Errorf(`GetOption(..., WithMarshalers) = (%v, %v), want (non-nil, true)`, v, ok)
   199  	}
   200  	if v, ok := json.GetOption(opts, json.WithUnmarshalers); v != nil || ok {
   201  		t.Errorf(`GetOption(..., WithUnmarshalers) = (%v, %v), want (nil, false)`, v, ok)
   202  	}
   203  }
   204  
   205  var sink struct {
   206  	Bool       bool
   207  	String     string
   208  	Marshalers *json.Marshalers
   209  }
   210  
   211  func BenchmarkGetBool(b *testing.B) {
   212  	b.ReportAllocs()
   213  	opts := json.DefaultOptionsV2()
   214  	for range b.N {
   215  		sink.Bool, sink.Bool = json.GetOption(opts, jsontext.AllowDuplicateNames)
   216  	}
   217  }
   218  
   219  func BenchmarkGetIndent(b *testing.B) {
   220  	b.ReportAllocs()
   221  	opts := json.DefaultOptionsV2()
   222  	for range b.N {
   223  		sink.String, sink.Bool = json.GetOption(opts, jsontext.WithIndent)
   224  	}
   225  }
   226  
   227  func BenchmarkGetMarshalers(b *testing.B) {
   228  	b.ReportAllocs()
   229  	opts := json.JoinOptions(json.DefaultOptionsV2(), json.WithMarshalers(nil))
   230  	for range b.N {
   231  		sink.Marshalers, sink.Bool = json.GetOption(opts, json.WithMarshalers)
   232  	}
   233  }
   234  

View as plain text