Source file src/encoding/json/scanner_test.go

     1  // Copyright 2010 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 json
     8  
     9  import (
    10  	"bytes"
    11  	"math"
    12  	"math/rand"
    13  	"reflect"
    14  	"strings"
    15  	"testing"
    16  )
    17  
    18  func indentNewlines(s string) string {
    19  	return strings.Join(strings.Split(s, "\n"), "\n\t")
    20  }
    21  
    22  func stripWhitespace(s string) string {
    23  	return strings.Map(func(r rune) rune {
    24  		if r == ' ' || r == '\n' || r == '\r' || r == '\t' {
    25  			return -1
    26  		}
    27  		return r
    28  	}, s)
    29  }
    30  
    31  func TestValid(t *testing.T) {
    32  	tests := []struct {
    33  		CaseName
    34  		data string
    35  		ok   bool
    36  	}{
    37  		{Name(""), `foo`, false},
    38  		{Name(""), `}{`, false},
    39  		{Name(""), `{]`, false},
    40  		{Name(""), `{}`, true},
    41  		{Name(""), `{"foo":"bar"}`, true},
    42  		{Name(""), `{"foo":"bar","bar":{"baz":["qux"]}}`, true},
    43  	}
    44  	for _, tt := range tests {
    45  		t.Run(tt.Name, func(t *testing.T) {
    46  			if ok := Valid([]byte(tt.data)); ok != tt.ok {
    47  				t.Errorf("%s: Valid(`%s`) = %v, want %v", tt.Where, tt.data, ok, tt.ok)
    48  			}
    49  		})
    50  	}
    51  }
    52  
    53  func TestCompactAndIndent(t *testing.T) {
    54  	tests := []struct {
    55  		CaseName
    56  		compact string
    57  		indent  string
    58  	}{
    59  		{Name(""), `1`, `1`},
    60  		{Name(""), `{}`, `{}`},
    61  		{Name(""), `[]`, `[]`},
    62  		{Name(""), `{"":2}`, "{\n\t\"\": 2\n}"},
    63  		{Name(""), `[3]`, "[\n\t3\n]"},
    64  		{Name(""), `[1,2,3]`, "[\n\t1,\n\t2,\n\t3\n]"},
    65  		{Name(""), `{"x":1}`, "{\n\t\"x\": 1\n}"},
    66  		{Name(""), `[true,false,null,"x",1,1.5,0,-5e+2]`, `[
    67  	true,
    68  	false,
    69  	null,
    70  	"x",
    71  	1,
    72  	1.5,
    73  	0,
    74  	-5e+2
    75  ]`},
    76  		{Name(""), "{\"\":\"<>&\u2028\u2029\"}", "{\n\t\"\": \"<>&\u2028\u2029\"\n}"}, // See golang.org/issue/34070
    77  		{Name(""), `null`, "null \n\r\t"},                                             // See golang.org/issue/13520 and golang.org/issue/74806
    78  	}
    79  	var buf bytes.Buffer
    80  	for _, tt := range tests {
    81  		t.Run(tt.Name, func(t *testing.T) {
    82  			buf.Reset()
    83  			if err := Compact(&buf, []byte(tt.compact)); err != nil {
    84  				t.Errorf("%s: Compact error: %v", tt.Where, err)
    85  			} else if got := buf.String(); got != tt.compact {
    86  				t.Errorf("%s: Compact:\n\tgot:  %s\n\twant: %s", tt.Where, indentNewlines(got), indentNewlines(tt.compact))
    87  			}
    88  
    89  			buf.Reset()
    90  			if err := Compact(&buf, []byte(tt.indent)); err != nil {
    91  				t.Errorf("%s: Compact error: %v", tt.Where, err)
    92  			} else if got := buf.String(); got != tt.compact {
    93  				t.Errorf("%s: Compact:\n\tgot:  %s\n\twant: %s", tt.Where, indentNewlines(got), indentNewlines(tt.compact))
    94  			}
    95  
    96  			buf.Reset()
    97  			if err := Indent(&buf, []byte(tt.indent), "", "\t"); err != nil {
    98  				t.Errorf("%s: Indent error: %v", tt.Where, err)
    99  			} else if got := buf.String(); got != tt.indent {
   100  				t.Errorf("%s: Compact:\n\tgot:  %s\n\twant: %s", tt.Where, indentNewlines(got), indentNewlines(tt.indent))
   101  			}
   102  
   103  			buf.Reset()
   104  			if err := Indent(&buf, []byte(tt.compact), "", "\t"); err != nil {
   105  				t.Errorf("%s: Indent error: %v", tt.Where, err)
   106  			} else if got := buf.String(); got != strings.TrimRight(tt.indent, " \n\r\t") {
   107  				t.Errorf("%s: Compact:\n\tgot:  %s\n\twant: %s", tt.Where, indentNewlines(got), indentNewlines(tt.indent))
   108  			}
   109  		})
   110  	}
   111  }
   112  
   113  func TestCompactSeparators(t *testing.T) {
   114  	// U+2028 and U+2029 should be escaped inside strings.
   115  	// They should not appear outside strings.
   116  	tests := []struct {
   117  		CaseName
   118  		in, compact string
   119  	}{
   120  		{Name(""), "{\"\u2028\": 1}", "{\"\u2028\":1}"},
   121  		{Name(""), "{\"\u2029\" :2}", "{\"\u2029\":2}"},
   122  	}
   123  	for _, tt := range tests {
   124  		t.Run(tt.Name, func(t *testing.T) {
   125  			var buf bytes.Buffer
   126  			if err := Compact(&buf, []byte(tt.in)); err != nil {
   127  				t.Errorf("%s: Compact error: %v", tt.Where, err)
   128  			} else if got := buf.String(); got != tt.compact {
   129  				t.Errorf("%s: Compact:\n\tgot:  %s\n\twant: %s", tt.Where, indentNewlines(got), indentNewlines(tt.compact))
   130  			}
   131  		})
   132  	}
   133  }
   134  
   135  // Tests of a large random structure.
   136  
   137  func TestCompactBig(t *testing.T) {
   138  	initBig()
   139  	var buf bytes.Buffer
   140  	if err := Compact(&buf, jsonBig); err != nil {
   141  		t.Fatalf("Compact error: %v", err)
   142  	}
   143  	b := buf.Bytes()
   144  	if !bytes.Equal(b, jsonBig) {
   145  		t.Error("Compact:")
   146  		diff(t, b, jsonBig)
   147  		return
   148  	}
   149  }
   150  
   151  func TestIndentBig(t *testing.T) {
   152  	t.Parallel()
   153  	initBig()
   154  	var buf bytes.Buffer
   155  	if err := Indent(&buf, jsonBig, "", "\t"); err != nil {
   156  		t.Fatalf("Indent error: %v", err)
   157  	}
   158  	b := buf.Bytes()
   159  	if len(b) == len(jsonBig) {
   160  		// jsonBig is compact (no unnecessary spaces);
   161  		// indenting should make it bigger
   162  		t.Fatalf("Indent did not expand the input")
   163  	}
   164  
   165  	// should be idempotent
   166  	var buf1 bytes.Buffer
   167  	if err := Indent(&buf1, b, "", "\t"); err != nil {
   168  		t.Fatalf("Indent error: %v", err)
   169  	}
   170  	b1 := buf1.Bytes()
   171  	if !bytes.Equal(b1, b) {
   172  		t.Error("Indent(Indent(jsonBig)) != Indent(jsonBig):")
   173  		diff(t, b1, b)
   174  		return
   175  	}
   176  
   177  	// should get back to original
   178  	buf1.Reset()
   179  	if err := Compact(&buf1, b); err != nil {
   180  		t.Fatalf("Compact error: %v", err)
   181  	}
   182  	b1 = buf1.Bytes()
   183  	if !bytes.Equal(b1, jsonBig) {
   184  		t.Error("Compact(Indent(jsonBig)) != jsonBig:")
   185  		diff(t, b1, jsonBig)
   186  		return
   187  	}
   188  }
   189  
   190  func TestIndentErrors(t *testing.T) {
   191  	tests := []struct {
   192  		CaseName
   193  		in  string
   194  		err error
   195  	}{
   196  		{Name(""), `{"X": "foo", "Y"}`, &SyntaxError{"invalid character '}' after object key", 17}},
   197  		{Name(""), `{"X": "foo" "Y": "bar"}`, &SyntaxError{"invalid character '\"' after object key:value pair", 13}},
   198  	}
   199  	for _, tt := range tests {
   200  		t.Run(tt.Name, func(t *testing.T) {
   201  			slice := make([]uint8, 0)
   202  			buf := bytes.NewBuffer(slice)
   203  			if err := Indent(buf, []uint8(tt.in), "", ""); err != nil {
   204  				if !reflect.DeepEqual(err, tt.err) {
   205  					t.Fatalf("%s: Indent error:\n\tgot:  %v\n\twant: %v", tt.Where, err, tt.err)
   206  				}
   207  			}
   208  		})
   209  	}
   210  }
   211  
   212  func diff(t *testing.T, a, b []byte) {
   213  	t.Helper()
   214  	for i := 0; ; i++ {
   215  		if i >= len(a) || i >= len(b) || a[i] != b[i] {
   216  			j := i - 10
   217  			if j < 0 {
   218  				j = 0
   219  			}
   220  			t.Errorf("diverge at %d: «%s» vs «%s»", i, trim(a[j:]), trim(b[j:]))
   221  			return
   222  		}
   223  	}
   224  }
   225  
   226  func trim(b []byte) []byte {
   227  	return b[:min(len(b), 20)]
   228  }
   229  
   230  // Generate a random JSON object.
   231  
   232  var jsonBig []byte
   233  
   234  func initBig() {
   235  	n := 10000
   236  	if testing.Short() {
   237  		n = 100
   238  	}
   239  	b, err := Marshal(genValue(n))
   240  	if err != nil {
   241  		panic(err)
   242  	}
   243  	jsonBig = b
   244  }
   245  
   246  func genValue(n int) any {
   247  	if n > 1 {
   248  		switch rand.Intn(2) {
   249  		case 0:
   250  			return genArray(n)
   251  		case 1:
   252  			return genMap(n)
   253  		}
   254  	}
   255  	switch rand.Intn(3) {
   256  	case 0:
   257  		return rand.Intn(2) == 0
   258  	case 1:
   259  		return rand.NormFloat64()
   260  	case 2:
   261  		return genString(30)
   262  	}
   263  	panic("unreachable")
   264  }
   265  
   266  func genString(stddev float64) string {
   267  	n := int(math.Abs(rand.NormFloat64()*stddev + stddev/2))
   268  	c := make([]rune, n)
   269  	for i := range c {
   270  		f := math.Abs(rand.NormFloat64()*64 + 32)
   271  		if f > 0x10ffff {
   272  			f = 0x10ffff
   273  		}
   274  		c[i] = rune(f)
   275  	}
   276  	return string(c)
   277  }
   278  
   279  func genArray(n int) []any {
   280  	f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2)))
   281  	if f > n {
   282  		f = n
   283  	}
   284  	if f < 1 {
   285  		f = 1
   286  	}
   287  	x := make([]any, f)
   288  	for i := range x {
   289  		x[i] = genValue(((i+1)*n)/f - (i*n)/f)
   290  	}
   291  	return x
   292  }
   293  
   294  func genMap(n int) map[string]any {
   295  	f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2)))
   296  	if f > n {
   297  		f = n
   298  	}
   299  	if n > 0 && f == 0 {
   300  		f = 1
   301  	}
   302  	x := make(map[string]any)
   303  	for i := 0; i < f; i++ {
   304  		x[genString(10)] = genValue(((i+1)*n)/f - (i*n)/f)
   305  	}
   306  	return x
   307  }
   308  

View as plain text