Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
v2_stream_test.go616 linesDownload Raw Back to json
1// Copyright 2010 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.4 5//go:build goexperiment.jsonv26 7package json8 9import (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 24type CaseName = jsontest.CaseName25type CasePos = jsontest.CasePos26 27var Name = jsontest.Name28 29// Test values for the stream test.30// One of each JSON kind.31var 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 map40}41 42var streamEncoded = `0.143"hello"44null45true46false47["a","b","c"]48{"ß":"long s","K":"Kelvin"}493.1450`51 52func TestEncoder(t *testing.T) {53	for i := 0; i <= len(streamTest); i++ {54		var buf strings.Builder55		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			break68		}69	}70}71 72func 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 string80		Next *Dummy81	}82	dummy := Dummy{Name: "Dummy"}83	dummy.Next = &dummy84 85	var buf bytes.Buffer86	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 string93		I int94	}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 Data101	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 109var streamEncodedIndent = `0.1110"hello"111null112true113false114[115>."a",116>."b",117>."c"118>]119{120>."ß": "long s",121>."K": "Kelvin"122>}1233.14124`125 126func TestEncoderIndent(t *testing.T) {127	var buf strings.Builder128	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 139type strMarshaler string140 141func (s strMarshaler) MarshalJSON() ([]byte, error) {142	return []byte(s), nil143}144 145type strPtrMarshaler string146 147func (s *strPtrMarshaler) MarshalJSON() ([]byte, error) {148	return []byte(*s), nil149}150 151func TestEncoderSetEscapeHTML(t *testing.T) {152	var c C153	var ct CText154	var tagStruct struct {155		Valid   int `json:"<>&#! "`156		Invalid int `json:"\\"`157	}158 159	// This case is particularly interesting, as we force the encoder to160	// take the address of the Ptr field to use its MarshalJSON method. This161	// is why the '&' is important.162	marshalerStruct := &struct {163		NonPtr strMarshaler164		Ptr    strPtrMarshaler165	}{`"<str>"`, `"<str>"`}166 167	// https://golang.org/issue/34154168	stringOption := struct {169		Bar string `json:"bar,string"`170	}{`<html>foobar</html>`}171 172	tests := []struct {173		CaseName174		v          any175		wantEscape string176		want       string177	}{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.Builder200			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 220func 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 would226		// merge two adjacent JSON values.227		var buf bytes.Buffer228		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			break248		}249	}250}251 252func TestDecoderBuffered(t *testing.T) {253	r := strings.NewReader(`{"Name": "Gopher"} extra `)254	var m struct {255		Name string256	}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 274func 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 s286}287 288func TestRawMessage(t *testing.T) {289	var data struct {290		X  float64291		Id RawMessage292		Y  float32293	}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 312func TestNullRawMessage(t *testing.T) {313	var data struct {314		X     float64315		Id    RawMessage316		IdPtr *RawMessage317		Y     float32318	}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 339func TestBlocking(t *testing.T) {340	tests := []struct {341		CaseName342		in string343	}{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 any352 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 364type decodeThis struct {365	v any366}367 368func TestDecodeInStream(t *testing.T) {369	tests := []struct {370		CaseName371		json      string372		expTokens []any373	}{374		// streaming token cases375		{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		{CaseName: Name(""), json: `,`, expTokens: []any{443			&SyntaxError{"invalid character ',' looking for beginning of value", 0},444		}},445	}446	for _, tt := range tests {447		t.Run(tt.Name, func(t *testing.T) {448			dec := NewDecoder(strings.NewReader(tt.json))449			for i, want := range tt.expTokens {450				var got any451				var err error452 453				wantMore := true454				switch want {455				case Delim(']'), Delim('}'):456					wantMore = false457				}458				if got := dec.More(); got != wantMore {459					t.Fatalf("%s:\n\tinput: %s\n\tdec.More() = %v, want %v (next token: %T(%v)) rem:%q", tt.Where, tt.json, got, wantMore, want, want, tt.json[dec.InputOffset():])460				}461 462				if dt, ok := want.(decodeThis); ok {463					want = dt.v464					err = dec.Decode(&got)465				} else {466					got, err = dec.Token()467				}468				if errWant, ok := want.(error); ok {469					if err == nil || !reflect.DeepEqual(err, errWant) {470						t.Fatalf("%s:\n\tinput: %s\n\tgot error:  %v\n\twant error: %v", tt.Where, tt.json, err, errWant)471					}472					break473				} else if err != nil {474					t.Fatalf("%s:\n\tinput: %s\n\tgot error:  %v\n\twant error: nil", tt.Where, tt.json, err)475				}476				if !reflect.DeepEqual(got, want) {477					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)478				}479			}480		})481	}482}483 484// Test from golang.org/issue/11893485func TestHTTPDecoding(t *testing.T) {486	const raw = `{ "foo": "bar" }`487 488	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {489		w.Write([]byte(raw))490	}))491	defer ts.Close()492	res, err := http.Get(ts.URL)493	if err != nil {494		log.Fatalf("http.Get error: %v", err)495	}496	defer res.Body.Close()497 498	foo := struct {499		Foo string500	}{}501 502	d := NewDecoder(res.Body)503	err = d.Decode(&foo)504	if err != nil {505		t.Fatalf("Decode error: %v", err)506	}507	if foo.Foo != "bar" {508		t.Errorf(`Decode: got %q, want "bar"`, foo.Foo)509	}510 511	// make sure we get the EOF the second time512	err = d.Decode(&foo)513	if err != io.EOF {514		t.Errorf("Decode error:\n\tgot:  %v\n\twant: io.EOF", err)515	}516}517 518func TestTokenTruncation(t *testing.T) {519	tests := []struct {520		in  string521		err error522	}{523		{in: ``, err: io.EOF},524		{in: `{`, err: io.EOF},525		{in: `{"`, err: io.ErrUnexpectedEOF},526		{in: `{"k"`, err: io.EOF},527		{in: `{"k":`, err: io.EOF},528		{in: `{"k",`, err: &SyntaxError{"invalid character ',' after object key", int64(len(`{"k"`))}},529		{in: `{"k"}`, err: &SyntaxError{"invalid character '}' after object key", int64(len(`{"k"`))}},530		{in: ` [0`, err: io.EOF},531		{in: `[0.`, err: io.ErrUnexpectedEOF},532		{in: `[0. `, err: &SyntaxError{"invalid character ' ' in numeric literal", int64(len(`[0.`))}},533		{in: `[0,`, err: io.EOF},534		{in: `[0:`, err: &SyntaxError{"invalid character ':' after array element", int64(len(`[0`))}},535		{in: `n`, err: io.ErrUnexpectedEOF},536		{in: `nul`, err: io.ErrUnexpectedEOF},537		{in: `fal `, err: &SyntaxError{"invalid character ' ' in literal false (expecting 's')", int64(len(`fal`))}},538		{in: `false`, err: io.EOF},539	}540	for _, tt := range tests {541		d := NewDecoder(strings.NewReader(tt.in))542		for i := 0; true; i++ {543			if _, err := d.Token(); err != nil {544				if !reflect.DeepEqual(err, tt.err) {545					t.Errorf("`%s`: %d.Token error = %#v, want %v", tt.in, i, err, tt.err)546				}547				break548			}549		}550	}551}552 553func TestDecoderInputOffset(t *testing.T) {554	const input = ` [555		[ ] , [ "one" ] , [ "one" , "two" ] ,556		{ } , { "alpha" : "bravo" } , { "alpha" : "bravo" , "fizz" : "buzz" }557	] `558	wantOffsets := []int64{559		0, 1, 2, 5, 6, 7, 8, 9, 12, 13, 18, 19, 20, 21, 24, 25, 30, 31,560		38, 39, 40, 41, 46, 47, 48, 49, 52, 53, 60, 61, 70, 71, 72, 73,561		76, 77, 84, 85, 94, 95, 103, 104, 112, 113, 114, 116, 117, 117,562		117, 117,563	}564	wantMores := []bool{565		true, true, false, true, true, false, true, true, true, false,566		true, false, true, true, true, false, true, true, true, true,567		true, false, false, false, false,568	}569 570	d := NewDecoder(strings.NewReader(input))571	checkOffset := func() {572		t.Helper()573		got := d.InputOffset()574		if len(wantOffsets) == 0 {575			t.Fatalf("InputOffset = %d, want nil", got)576		}577		want := wantOffsets[0]578		if got != want {579			t.Fatalf("InputOffset = %d, want %d", got, want)580		}581		wantOffsets = wantOffsets[1:]582	}583	checkMore := func() {584		t.Helper()585		got := d.More()586		if len(wantMores) == 0 {587			t.Fatalf("More = %v, want nil", got)588		}589		want := wantMores[0]590		if got != want {591			t.Fatalf("More = %v, want %v", got, want)592		}593		wantMores = wantMores[1:]594	}595	checkOffset()596	checkMore()597	checkOffset()598	for {599		if _, err := d.Token(); err == io.EOF {600			break601		} else if err != nil {602			t.Fatalf("Token error: %v", err)603		}604		checkOffset()605		checkMore()606		checkOffset()607	}608	checkOffset()609	checkMore()610	checkOffset()611 612	if len(wantOffsets)+len(wantMores) > 0 {613		t.Fatal("unconsumed testdata")614	}615}616 
codekingpro/portable-devtools · Team Ai