Source file src/encoding/json/v2_stream_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  	"io"
    12  	"log"
    13  	"net"
    14  	"net/http"
    15  	"net/http/httptest"
    16  	"reflect"
    17  	"runtime/debug"
    18  	"strings"
    19  	"testing"
    20  
    21  	"encoding/json/internal/jsontest"
    22  )
    23  
    24  type CaseName = jsontest.CaseName
    25  type CasePos = jsontest.CasePos
    26  
    27  var Name = jsontest.Name
    28  
    29  // Test values for the stream test.
    30  // One of each JSON kind.
    31  var streamTest = []any{
    32  	0.1,
    33  	"hello",
    34  	nil,
    35  	true,
    36  	false,
    37  	[]any{"a", "b", "c"},
    38  	map[string]any{"K": "Kelvin", "ß": "long s"},
    39  	3.14, // another value to make sure something can follow map
    40  }
    41  
    42  var streamEncoded = `0.1
    43  "hello"
    44  null
    45  true
    46  false
    47  ["a","b","c"]
    48  {"ß":"long s","K":"Kelvin"}
    49  3.14
    50  `
    51  
    52  func TestEncoder(t *testing.T) {
    53  	for i := 0; i <= len(streamTest); i++ {
    54  		var buf strings.Builder
    55  		enc := NewEncoder(&buf)
    56  		// Check that enc.SetIndent("", "") turns off indentation.
    57  		enc.SetIndent(">", ".")
    58  		enc.SetIndent("", "")
    59  		for j, v := range streamTest[0:i] {
    60  			if err := enc.Encode(v); err != nil {
    61  				t.Fatalf("#%d.%d Encode error: %v", i, j, err)
    62  			}
    63  		}
    64  		if got, want := buf.String(), nlines(streamEncoded, i); got != want {
    65  			t.Errorf("encoding %d items: mismatch:", i)
    66  			diff(t, []byte(got), []byte(want))
    67  			break
    68  		}
    69  	}
    70  }
    71  
    72  func TestEncoderErrorAndReuseEncodeState(t *testing.T) {
    73  	// Disable the GC temporarily to prevent encodeState's in Pool being cleaned away during the test.
    74  	percent := debug.SetGCPercent(-1)
    75  	defer debug.SetGCPercent(percent)
    76  
    77  	// Trigger an error in Marshal with cyclic data.
    78  	type Dummy struct {
    79  		Name string
    80  		Next *Dummy
    81  	}
    82  	dummy := Dummy{Name: "Dummy"}
    83  	dummy.Next = &dummy
    84  
    85  	var buf bytes.Buffer
    86  	enc := NewEncoder(&buf)
    87  	if err := enc.Encode(dummy); err == nil {
    88  		t.Errorf("Encode(dummy) error: got nil, want non-nil")
    89  	}
    90  
    91  	type Data struct {
    92  		A string
    93  		I int
    94  	}
    95  	want := Data{A: "a", I: 1}
    96  	if err := enc.Encode(want); err != nil {
    97  		t.Errorf("Marshal error: %v", err)
    98  	}
    99  
   100  	var got Data
   101  	if err := Unmarshal(buf.Bytes(), &got); err != nil {
   102  		t.Errorf("Unmarshal error: %v", err)
   103  	}
   104  	if got != want {
   105  		t.Errorf("Marshal/Unmarshal roundtrip:\n\tgot:  %v\n\twant: %v", got, want)
   106  	}
   107  }
   108  
   109  var streamEncodedIndent = `0.1
   110  "hello"
   111  null
   112  true
   113  false
   114  [
   115  >."a",
   116  >."b",
   117  >."c"
   118  >]
   119  {
   120  >."ß": "long s",
   121  >."K": "Kelvin"
   122  >}
   123  3.14
   124  `
   125  
   126  func TestEncoderIndent(t *testing.T) {
   127  	var buf strings.Builder
   128  	enc := NewEncoder(&buf)
   129  	enc.SetIndent(">", ".")
   130  	for _, v := range streamTest {
   131  		enc.Encode(v)
   132  	}
   133  	if got, want := buf.String(), streamEncodedIndent; got != want {
   134  		t.Errorf("Encode mismatch:\ngot:\n%s\n\nwant:\n%s", got, want)
   135  		diff(t, []byte(got), []byte(want))
   136  	}
   137  }
   138  
   139  type strMarshaler string
   140  
   141  func (s strMarshaler) MarshalJSON() ([]byte, error) {
   142  	return []byte(s), nil
   143  }
   144  
   145  type strPtrMarshaler string
   146  
   147  func (s *strPtrMarshaler) MarshalJSON() ([]byte, error) {
   148  	return []byte(*s), nil
   149  }
   150  
   151  func TestEncoderSetEscapeHTML(t *testing.T) {
   152  	var c C
   153  	var ct CText
   154  	var tagStruct struct {
   155  		Valid   int `json:"<>&#! "`
   156  		Invalid int `json:"\\"`
   157  	}
   158  
   159  	// This case is particularly interesting, as we force the encoder to
   160  	// take the address of the Ptr field to use its MarshalJSON method. This
   161  	// is why the '&' is important.
   162  	marshalerStruct := &struct {
   163  		NonPtr strMarshaler
   164  		Ptr    strPtrMarshaler
   165  	}{`"<str>"`, `"<str>"`}
   166  
   167  	// https://golang.org/issue/34154
   168  	stringOption := struct {
   169  		Bar string `json:"bar,string"`
   170  	}{`<html>foobar</html>`}
   171  
   172  	tests := []struct {
   173  		CaseName
   174  		v          any
   175  		wantEscape string
   176  		want       string
   177  	}{
   178  		{Name("c"), c, `"\u003c\u0026\u003e"`, `"<&>"`},
   179  		{Name("ct"), ct, `"\"\u003c\u0026\u003e\""`, `"\"<&>\""`},
   180  		{Name(`"<&>"`), "<&>", `"\u003c\u0026\u003e"`, `"<&>"`},
   181  		{
   182  			Name("tagStruct"), tagStruct,
   183  			`{"\u003c\u003e\u0026#! ":0,"Invalid":0}`,
   184  			`{"<>&#! ":0,"Invalid":0}`,
   185  		},
   186  		{
   187  			Name(`"<str>"`), marshalerStruct,
   188  			`{"NonPtr":"\u003cstr\u003e","Ptr":"\u003cstr\u003e"}`,
   189  			`{"NonPtr":"<str>","Ptr":"<str>"}`,
   190  		},
   191  		{
   192  			Name("stringOption"), stringOption,
   193  			`{"bar":"\"\\u003chtml\\u003efoobar\\u003c/html\\u003e\""}`,
   194  			`{"bar":"\"<html>foobar</html>\""}`,
   195  		},
   196  	}
   197  	for _, tt := range tests {
   198  		t.Run(tt.Name, func(t *testing.T) {
   199  			var buf strings.Builder
   200  			enc := NewEncoder(&buf)
   201  			if err := enc.Encode(tt.v); err != nil {
   202  				t.Fatalf("%s: Encode(%s) error: %s", tt.Where, tt.Name, err)
   203  			}
   204  			if got := strings.TrimSpace(buf.String()); got != tt.wantEscape {
   205  				t.Errorf("%s: Encode(%s):\n\tgot:  %s\n\twant: %s", tt.Where, tt.Name, got, tt.wantEscape)
   206  			}
   207  			buf.Reset()
   208  			enc.SetEscapeHTML(false)
   209  			if err := enc.Encode(tt.v); err != nil {
   210  				t.Fatalf("%s: SetEscapeHTML(false) Encode(%s) error: %s", tt.Where, tt.Name, err)
   211  			}
   212  			if got := strings.TrimSpace(buf.String()); got != tt.want {
   213  				t.Errorf("%s: SetEscapeHTML(false) Encode(%s):\n\tgot:  %s\n\twant: %s",
   214  					tt.Where, tt.Name, got, tt.want)
   215  			}
   216  		})
   217  	}
   218  }
   219  
   220  func TestDecoder(t *testing.T) {
   221  	for i := 0; i <= len(streamTest); i++ {
   222  		// Use stream without newlines as input,
   223  		// just to stress the decoder even more.
   224  		// Our test input does not include back-to-back numbers.
   225  		// Otherwise stripping the newlines would
   226  		// merge two adjacent JSON values.
   227  		var buf bytes.Buffer
   228  		for _, c := range nlines(streamEncoded, i) {
   229  			if c != '\n' {
   230  				buf.WriteRune(c)
   231  			}
   232  		}
   233  		out := make([]any, i)
   234  		dec := NewDecoder(&buf)
   235  		for j := range out {
   236  			if err := dec.Decode(&out[j]); err != nil {
   237  				t.Fatalf("decode #%d/%d error: %v", j, i, err)
   238  			}
   239  		}
   240  		if !reflect.DeepEqual(out, streamTest[0:i]) {
   241  			t.Errorf("decoding %d items: mismatch:", i)
   242  			for j := range out {
   243  				if !reflect.DeepEqual(out[j], streamTest[j]) {
   244  					t.Errorf("#%d:\n\tgot:  %v\n\twant: %v", j, out[j], streamTest[j])
   245  				}
   246  			}
   247  			break
   248  		}
   249  	}
   250  }
   251  
   252  func TestDecoderBuffered(t *testing.T) {
   253  	r := strings.NewReader(`{"Name": "Gopher"} extra `)
   254  	var m struct {
   255  		Name string
   256  	}
   257  	d := NewDecoder(r)
   258  	err := d.Decode(&m)
   259  	if err != nil {
   260  		t.Fatal(err)
   261  	}
   262  	if m.Name != "Gopher" {
   263  		t.Errorf("Name = %s, want Gopher", m.Name)
   264  	}
   265  	rest, err := io.ReadAll(d.Buffered())
   266  	if err != nil {
   267  		t.Fatal(err)
   268  	}
   269  	if got, want := string(rest), " extra "; got != want {
   270  		t.Errorf("Remaining = %s, want %s", got, want)
   271  	}
   272  }
   273  
   274  func nlines(s string, n int) string {
   275  	if n <= 0 {
   276  		return ""
   277  	}
   278  	for i, c := range s {
   279  		if c == '\n' {
   280  			if n--; n == 0 {
   281  				return s[0 : i+1]
   282  			}
   283  		}
   284  	}
   285  	return s
   286  }
   287  
   288  func TestRawMessage(t *testing.T) {
   289  	var data struct {
   290  		X  float64
   291  		Id RawMessage
   292  		Y  float32
   293  	}
   294  	const raw = `["\u0056",null]`
   295  	const want = `{"X":0.1,"Id":["\u0056",null],"Y":0.2}`
   296  	err := Unmarshal([]byte(want), &data)
   297  	if err != nil {
   298  		t.Fatalf("Unmarshal error: %v", err)
   299  	}
   300  	if string([]byte(data.Id)) != raw {
   301  		t.Fatalf("Unmarshal:\n\tgot:  %s\n\twant: %s", []byte(data.Id), raw)
   302  	}
   303  	got, err := Marshal(&data)
   304  	if err != nil {
   305  		t.Fatalf("Marshal error: %v", err)
   306  	}
   307  	if string(got) != want {
   308  		t.Fatalf("Marshal:\n\tgot:  %s\n\twant: %s", got, want)
   309  	}
   310  }
   311  
   312  func TestNullRawMessage(t *testing.T) {
   313  	var data struct {
   314  		X     float64
   315  		Id    RawMessage
   316  		IdPtr *RawMessage
   317  		Y     float32
   318  	}
   319  	const want = `{"X":0.1,"Id":null,"IdPtr":null,"Y":0.2}`
   320  	err := Unmarshal([]byte(want), &data)
   321  	if err != nil {
   322  		t.Fatalf("Unmarshal error: %v", err)
   323  	}
   324  	if want, got := "null", string(data.Id); want != got {
   325  		t.Fatalf("Unmarshal:\n\tgot:  %s\n\twant: %s", got, want)
   326  	}
   327  	if data.IdPtr != nil {
   328  		t.Fatalf("pointer mismatch: got non-nil, want nil")
   329  	}
   330  	got, err := Marshal(&data)
   331  	if err != nil {
   332  		t.Fatalf("Marshal error: %v", err)
   333  	}
   334  	if string(got) != want {
   335  		t.Fatalf("Marshal:\n\tgot:  %s\n\twant: %s", got, want)
   336  	}
   337  }
   338  
   339  func TestBlocking(t *testing.T) {
   340  	tests := []struct {
   341  		CaseName
   342  		in string
   343  	}{
   344  		{Name(""), `{"x": 1}`},
   345  		{Name(""), `[1, 2, 3]`},
   346  	}
   347  	for _, tt := range tests {
   348  		t.Run(tt.Name, func(t *testing.T) {
   349  			r, w := net.Pipe()
   350  			go w.Write([]byte(tt.in))
   351  			var val any
   352  
   353  			// If Decode reads beyond what w.Write writes above,
   354  			// it will block, and the test will deadlock.
   355  			if err := NewDecoder(r).Decode(&val); err != nil {
   356  				t.Errorf("%s: NewDecoder(%s).Decode error: %v", tt.Where, tt.in, err)
   357  			}
   358  			r.Close()
   359  			w.Close()
   360  		})
   361  	}
   362  }
   363  
   364  type decodeThis struct {
   365  	v any
   366  }
   367  
   368  func TestDecodeInStream(t *testing.T) {
   369  	tests := []struct {
   370  		CaseName
   371  		json      string
   372  		expTokens []any
   373  	}{
   374  		// streaming token cases
   375  		{CaseName: Name(""), json: `10`, expTokens: []any{float64(10)}},
   376  		{CaseName: Name(""), json: ` [10] `, expTokens: []any{
   377  			Delim('['), float64(10), Delim(']')}},
   378  		{CaseName: Name(""), json: ` [false,10,"b"] `, expTokens: []any{
   379  			Delim('['), false, float64(10), "b", Delim(']')}},
   380  		{CaseName: Name(""), json: `{ "a": 1 }`, expTokens: []any{
   381  			Delim('{'), "a", float64(1), Delim('}')}},
   382  		{CaseName: Name(""), json: `{"a": 1, "b":"3"}`, expTokens: []any{
   383  			Delim('{'), "a", float64(1), "b", "3", Delim('}')}},
   384  		{CaseName: Name(""), json: ` [{"a": 1},{"a": 2}] `, expTokens: []any{
   385  			Delim('['),
   386  			Delim('{'), "a", float64(1), Delim('}'),
   387  			Delim('{'), "a", float64(2), Delim('}'),
   388  			Delim(']')}},
   389  		{CaseName: Name(""), json: `{"obj": {"a": 1}}`, expTokens: []any{
   390  			Delim('{'), "obj", Delim('{'), "a", float64(1), Delim('}'),
   391  			Delim('}')}},
   392  		{CaseName: Name(""), json: `{"obj": [{"a": 1}]}`, expTokens: []any{
   393  			Delim('{'), "obj", Delim('['),
   394  			Delim('{'), "a", float64(1), Delim('}'),
   395  			Delim(']'), Delim('}')}},
   396  
   397  		// streaming tokens with intermittent Decode()
   398  		{CaseName: Name(""), json: `{ "a": 1 }`, expTokens: []any{
   399  			Delim('{'), "a",
   400  			decodeThis{float64(1)},
   401  			Delim('}')}},
   402  		{CaseName: Name(""), json: ` [ { "a" : 1 } ] `, expTokens: []any{
   403  			Delim('['),
   404  			decodeThis{map[string]any{"a": float64(1)}},
   405  			Delim(']')}},
   406  		{CaseName: Name(""), json: ` [{"a": 1},{"a": 2}] `, expTokens: []any{
   407  			Delim('['),
   408  			decodeThis{map[string]any{"a": float64(1)}},
   409  			decodeThis{map[string]any{"a": float64(2)}},
   410  			Delim(']')}},
   411  		{CaseName: Name(""), json: `{ "obj" : [ { "a" : 1 } ] }`, expTokens: []any{
   412  			Delim('{'), "obj", Delim('['),
   413  			decodeThis{map[string]any{"a": float64(1)}},
   414  			Delim(']'), Delim('}')}},
   415  
   416  		{CaseName: Name(""), json: `{"obj": {"a": 1}}`, expTokens: []any{
   417  			Delim('{'), "obj",
   418  			decodeThis{map[string]any{"a": float64(1)}},
   419  			Delim('}')}},
   420  		{CaseName: Name(""), json: `{"obj": [{"a": 1}]}`, expTokens: []any{
   421  			Delim('{'), "obj",
   422  			decodeThis{[]any{
   423  				map[string]any{"a": float64(1)},
   424  			}},
   425  			Delim('}')}},
   426  		{CaseName: Name(""), json: ` [{"a": 1} {"a": 2}] `, expTokens: []any{
   427  			Delim('['),
   428  			decodeThis{map[string]any{"a": float64(1)}},
   429  			decodeThis{&SyntaxError{"invalid character '{' after array element", len64(` [{"a": 1} `)}},
   430  		}},
   431  		{CaseName: Name(""), json: `{ "` + strings.Repeat("a", 513) + `" 1 }`, expTokens: []any{
   432  			Delim('{'), strings.Repeat("a", 513),
   433  			decodeThis{&SyntaxError{"invalid character '1' after object key", len64(`{ "` + strings.Repeat("a", 513) + `" `)}},
   434  		}},
   435  		{CaseName: Name(""), json: `{ "\a" }`, expTokens: []any{
   436  			Delim('{'),
   437  			&SyntaxError{"invalid escape sequence `\\a` in string", len64(`{ "`)},
   438  		}},
   439  		{CaseName: Name(""), json: ` \a`, expTokens: []any{
   440  			&SyntaxError{"invalid character '\\\\' looking for beginning of value", len64(` `)},
   441  		}},
   442  	}
   443  	for _, tt := range tests {
   444  		t.Run(tt.Name, func(t *testing.T) {
   445  			dec := NewDecoder(strings.NewReader(tt.json))
   446  			for i, want := range tt.expTokens {
   447  				var got any
   448  				var err error
   449  
   450  				if dt, ok := want.(decodeThis); ok {
   451  					want = dt.v
   452  					err = dec.Decode(&got)
   453  				} else {
   454  					got, err = dec.Token()
   455  				}
   456  				if errWant, ok := want.(error); ok {
   457  					if err == nil || !reflect.DeepEqual(err, errWant) {
   458  						t.Fatalf("%s:\n\tinput: %s\n\tgot error:  %v\n\twant error: %v", tt.Where, tt.json, err, errWant)
   459  					}
   460  					break
   461  				} else if err != nil {
   462  					t.Fatalf("%s:\n\tinput: %s\n\tgot error:  %v\n\twant error: nil", tt.Where, tt.json, err)
   463  				}
   464  				if !reflect.DeepEqual(got, want) {
   465  					t.Fatalf("%s: token %d:\n\tinput: %s\n\tgot:  %T(%v)\n\twant: %T(%v)", tt.Where, i, tt.json, got, got, want, want)
   466  				}
   467  			}
   468  		})
   469  	}
   470  }
   471  
   472  // Test from golang.org/issue/11893
   473  func TestHTTPDecoding(t *testing.T) {
   474  	const raw = `{ "foo": "bar" }`
   475  
   476  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   477  		w.Write([]byte(raw))
   478  	}))
   479  	defer ts.Close()
   480  	res, err := http.Get(ts.URL)
   481  	if err != nil {
   482  		log.Fatalf("http.Get error: %v", err)
   483  	}
   484  	defer res.Body.Close()
   485  
   486  	foo := struct {
   487  		Foo string
   488  	}{}
   489  
   490  	d := NewDecoder(res.Body)
   491  	err = d.Decode(&foo)
   492  	if err != nil {
   493  		t.Fatalf("Decode error: %v", err)
   494  	}
   495  	if foo.Foo != "bar" {
   496  		t.Errorf(`Decode: got %q, want "bar"`, foo.Foo)
   497  	}
   498  
   499  	// make sure we get the EOF the second time
   500  	err = d.Decode(&foo)
   501  	if err != io.EOF {
   502  		t.Errorf("Decode error:\n\tgot:  %v\n\twant: io.EOF", err)
   503  	}
   504  }
   505  
   506  func TestTokenTruncation(t *testing.T) {
   507  	tests := []struct {
   508  		in  string
   509  		err error
   510  	}{
   511  		{in: ``, err: io.EOF},
   512  		{in: `{`, err: io.EOF},
   513  		{in: `{"`, err: io.ErrUnexpectedEOF},
   514  		{in: `{"k"`, err: io.EOF},
   515  		{in: `{"k":`, err: io.EOF},
   516  		{in: `{"k",`, err: &SyntaxError{"invalid character ',' after object key", int64(len(`{"k"`))}},
   517  		{in: `{"k"}`, err: &SyntaxError{"invalid character '}' after object key", int64(len(`{"k"`))}},
   518  		{in: ` [0`, err: io.EOF},
   519  		{in: `[0.`, err: io.ErrUnexpectedEOF},
   520  		{in: `[0. `, err: &SyntaxError{"invalid character ' ' in numeric literal", int64(len(`[0.`))}},
   521  		{in: `[0,`, err: io.EOF},
   522  		{in: `[0:`, err: &SyntaxError{"invalid character ':' after array element", int64(len(`[0`))}},
   523  		{in: `n`, err: io.ErrUnexpectedEOF},
   524  		{in: `nul`, err: io.ErrUnexpectedEOF},
   525  		{in: `fal `, err: &SyntaxError{"invalid character ' ' in literal false (expecting 's')", int64(len(`fal`))}},
   526  		{in: `false`, err: io.EOF},
   527  	}
   528  	for _, tt := range tests {
   529  		d := NewDecoder(strings.NewReader(tt.in))
   530  		for i := 0; true; i++ {
   531  			if _, err := d.Token(); err != nil {
   532  				if !reflect.DeepEqual(err, tt.err) {
   533  					t.Errorf("`%s`: %d.Token error = %#v, want %v", tt.in, i, err, tt.err)
   534  				}
   535  				break
   536  			}
   537  		}
   538  	}
   539  }
   540  
   541  func TestDecoderInputOffset(t *testing.T) {
   542  	const input = ` [
   543  		[ ] , [ "one" ] , [ "one" , "two" ] ,
   544  		{ } , { "alpha" : "bravo" } , { "alpha" : "bravo" , "fizz" : "buzz" }
   545  	] `
   546  	wantOffsets := []int64{
   547  		0, 1, 2, 5, 6, 7, 8, 9, 12, 13, 18, 19, 20, 21, 24, 25, 30, 31,
   548  		38, 39, 40, 41, 46, 47, 48, 49, 52, 53, 60, 61, 70, 71, 72, 73,
   549  		76, 77, 84, 85, 94, 95, 103, 104, 112, 113, 114, 116, 117, 117,
   550  		117, 117,
   551  	}
   552  	wantMores := []bool{
   553  		true, true, false, true, true, false, true, true, true, false,
   554  		true, false, true, true, true, false, true, true, true, true,
   555  		true, false, false, false, false,
   556  	}
   557  
   558  	d := NewDecoder(strings.NewReader(input))
   559  	checkOffset := func() {
   560  		t.Helper()
   561  		got := d.InputOffset()
   562  		if len(wantOffsets) == 0 {
   563  			t.Fatalf("InputOffset = %d, want nil", got)
   564  		}
   565  		want := wantOffsets[0]
   566  		if got != want {
   567  			t.Fatalf("InputOffset = %d, want %d", got, want)
   568  		}
   569  		wantOffsets = wantOffsets[1:]
   570  	}
   571  	checkMore := func() {
   572  		t.Helper()
   573  		got := d.More()
   574  		if len(wantMores) == 0 {
   575  			t.Fatalf("More = %v, want nil", got)
   576  		}
   577  		want := wantMores[0]
   578  		if got != want {
   579  			t.Fatalf("More = %v, want %v", got, want)
   580  		}
   581  		wantMores = wantMores[1:]
   582  	}
   583  	checkOffset()
   584  	checkMore()
   585  	checkOffset()
   586  	for {
   587  		if _, err := d.Token(); err == io.EOF {
   588  			break
   589  		} else if err != nil {
   590  			t.Fatalf("Token error: %v", err)
   591  		}
   592  		checkOffset()
   593  		checkMore()
   594  		checkOffset()
   595  	}
   596  	checkOffset()
   597  	checkMore()
   598  	checkOffset()
   599  
   600  	if len(wantOffsets)+len(wantMores) > 0 {
   601  		t.Fatal("unconsumed testdata")
   602  	}
   603  }
   604  

View as plain text