1
2
3
4
5
6
7 package jsontext
8
9 import (
10 "bytes"
11 "errors"
12 "io"
13 "math/rand"
14 "slices"
15 "testing"
16
17 "encoding/json/internal/jsontest"
18 )
19
20 func FuzzCoder(f *testing.F) {
21
22 for _, td := range coderTestdata {
23 f.Add(int64(0), []byte(td.in))
24 }
25 for _, td := range decoderErrorTestdata {
26 f.Add(int64(0), []byte(td.in))
27 }
28 for _, td := range encoderErrorTestdata {
29 f.Add(int64(0), []byte(td.wantOut))
30 }
31 for _, td := range jsontest.Data {
32 f.Add(int64(0), td.Data())
33 }
34
35 f.Fuzz(func(t *testing.T, seed int64, b []byte) {
36 var tokVals []tokOrVal
37 rn := rand.NewSource(seed)
38
39
40
41 src := bytes.NewReader(b)
42 dec := NewDecoder(src)
43 for {
44 if rn.Int63()%8 > 0 {
45 tok, err := dec.ReadToken()
46 if err != nil {
47 if err == io.EOF {
48 break
49 }
50 t.Skipf("Decoder.ReadToken error: %v", err)
51 }
52 tokVals = append(tokVals, tok.Clone())
53 } else {
54 val, err := dec.ReadValue()
55 if err != nil {
56 if expectError := dec.PeekKind() == '}' || dec.PeekKind() == ']'; expectError {
57 if _, ok := errors.AsType[*SyntacticError](err); ok {
58 continue
59 }
60 }
61 if err == io.EOF {
62 break
63 }
64 t.Skipf("Decoder.ReadValue error: %v", err)
65 }
66 tokVals = append(tokVals, append(zeroValue, val...))
67 }
68 }
69
70
71
72 dst := new(bytes.Buffer)
73 enc := NewEncoder(dst)
74 for _, tokVal := range tokVals {
75 switch tokVal := tokVal.(type) {
76 case Token:
77 if err := enc.WriteToken(tokVal); err != nil {
78 t.Fatalf("Encoder.WriteToken error: %v", err)
79 }
80 case Value:
81 if err := enc.WriteValue(tokVal); err != nil {
82 t.Fatalf("Encoder.WriteValue error: %v", err)
83 }
84 }
85 }
86
87
88 var got, want []Token
89 for dec := NewDecoder(bytes.NewReader(b)); dec.PeekKind() > 0; {
90 tok, err := dec.ReadToken()
91 if err != nil {
92 t.Fatalf("Decoder.ReadToken error: %v", err)
93 }
94 got = append(got, tok.Clone())
95 }
96 for dec := NewDecoder(dst); dec.PeekKind() > 0; {
97 tok, err := dec.ReadToken()
98 if err != nil {
99 t.Fatalf("Decoder.ReadToken error: %v", err)
100 }
101 want = append(want, tok.Clone())
102 }
103 if !equalTokens(got, want) {
104 t.Fatalf("mismatching output:\ngot %v\nwant %v", got, want)
105 }
106 })
107 }
108
109 func FuzzResumableDecoder(f *testing.F) {
110 for _, td := range resumableDecoderTestdata {
111 f.Add(int64(0), []byte(td))
112 }
113
114 f.Fuzz(func(t *testing.T, seed int64, b []byte) {
115 rn := rand.NewSource(seed)
116
117
118
119 t.Run("ReadToken", func(t *testing.T) {
120 decGot := NewDecoder(&FaultyBuffer{B: b, MaxBytes: 8, Rand: rn})
121 decWant := NewDecoder(bytes.NewReader(b))
122 gotTok, gotErr := decGot.ReadToken()
123 wantTok, wantErr := decWant.ReadToken()
124 if gotTok.String() != wantTok.String() || !equalError(gotErr, wantErr) {
125 t.Errorf("Decoder.ReadToken = (%v, %v), want (%v, %v)", gotTok, gotErr, wantTok, wantErr)
126 }
127 })
128 t.Run("ReadValue", func(t *testing.T) {
129 decGot := NewDecoder(&FaultyBuffer{B: b, MaxBytes: 8, Rand: rn})
130 decWant := NewDecoder(bytes.NewReader(b))
131 gotVal, gotErr := decGot.ReadValue()
132 wantVal, wantErr := decWant.ReadValue()
133 if !slices.Equal(gotVal, wantVal) || !equalError(gotErr, wantErr) {
134 t.Errorf("Decoder.ReadValue = (%s, %v), want (%s, %v)", gotVal, gotErr, wantVal, wantErr)
135 }
136 })
137 })
138 }
139
140 func FuzzValueFormat(f *testing.F) {
141 for _, td := range valueTestdata {
142 f.Add(int64(0), []byte(td.in))
143 }
144
145
146 isValid := func(b []byte, opts ...Options) bool {
147 d := NewDecoder(bytes.NewReader(b), opts...)
148 _, errVal := d.ReadValue()
149 _, errEOF := d.ReadToken()
150 return errVal == nil && errEOF == io.EOF
151 }
152
153
154 stripWhitespace := func(in []byte) (out []byte) {
155 out = make([]byte, 0, len(in))
156 for _, c := range in {
157 switch c {
158 case ' ', '\n', '\r', '\t':
159 default:
160 out = append(out, c)
161 }
162 }
163 return out
164 }
165
166 allOptions := []Options{
167 AllowDuplicateNames(true),
168 AllowInvalidUTF8(true),
169 EscapeForHTML(true),
170 EscapeForJS(true),
171 PreserveRawStrings(true),
172 CanonicalizeRawInts(true),
173 CanonicalizeRawFloats(true),
174 ReorderRawObjects(true),
175 SpaceAfterColon(true),
176 SpaceAfterComma(true),
177 Multiline(true),
178 WithIndent("\t"),
179 WithIndentPrefix(" "),
180 }
181
182 f.Fuzz(func(t *testing.T, seed int64, b []byte) {
183 validRFC7159 := isValid(b, AllowInvalidUTF8(true), AllowDuplicateNames(true))
184 validRFC8259 := isValid(b, AllowInvalidUTF8(false), AllowDuplicateNames(true))
185 validRFC7493 := isValid(b, AllowInvalidUTF8(false), AllowDuplicateNames(false))
186 switch {
187 case !validRFC7159 && validRFC8259:
188 t.Errorf("invalid input per RFC 7159 implies invalid per RFC 8259")
189 case !validRFC8259 && validRFC7493:
190 t.Errorf("invalid input per RFC 8259 implies invalid per RFC 7493")
191 }
192
193 gotValid := Value(b).IsValid()
194 wantValid := validRFC7493
195 if gotValid != wantValid {
196 t.Errorf("Value.IsValid = %v, want %v", gotValid, wantValid)
197 }
198
199 gotCompacted := Value(string(b))
200 gotCompactOk := gotCompacted.Compact() == nil
201 wantCompactOk := validRFC7159
202 if !bytes.Equal(stripWhitespace(gotCompacted), stripWhitespace(b)) {
203 t.Errorf("stripWhitespace(Value.Compact) = %s, want %s", stripWhitespace(gotCompacted), stripWhitespace(b))
204 }
205 if gotCompactOk != wantCompactOk {
206 t.Errorf("Value.Compact success mismatch: got %v, want %v", gotCompactOk, wantCompactOk)
207 }
208
209 gotIndented := Value(string(b))
210 gotIndentOk := gotIndented.Indent() == nil
211 wantIndentOk := validRFC7159
212 if !bytes.Equal(stripWhitespace(gotIndented), stripWhitespace(b)) {
213 t.Errorf("stripWhitespace(Value.Indent) = %s, want %s", stripWhitespace(gotIndented), stripWhitespace(b))
214 }
215 if gotIndentOk != wantIndentOk {
216 t.Errorf("Value.Indent success mismatch: got %v, want %v", gotIndentOk, wantIndentOk)
217 }
218
219 gotCanonicalized := Value(string(b))
220 gotCanonicalizeOk := gotCanonicalized.Canonicalize() == nil
221 wantCanonicalizeOk := validRFC7493
222 if gotCanonicalizeOk != wantCanonicalizeOk {
223 t.Errorf("Value.Canonicalize success mismatch: got %v, want %v", gotCanonicalizeOk, wantCanonicalizeOk)
224 }
225
226
227 var opts []Options
228 rn := rand.New(rand.NewSource(seed))
229 for _, opt := range allOptions {
230 if rn.Intn(len(allOptions)/4) == 0 {
231 opts = append(opts, opt)
232 }
233 }
234 v := Value(b)
235 v.Format(opts...)
236 })
237 }
238
View as plain text