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

View as plain text