-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathreaderParser_test.go
More file actions
309 lines (293 loc) · 10.6 KB
/
readerParser_test.go
File metadata and controls
309 lines (293 loc) · 10.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
package bencode_test
import (
"crypto/rand"
"fmt"
"io"
"reflect"
"strings"
"testing"
"github.com/stefanovazzocell/bencode"
)
// A reader that never returns anything
type NoProgressReader struct{}
func (r *NoProgressReader) Read(p []byte) (n int, err error) {
return 0, nil
}
// A reader that always returns a negative number
type NegativeReader struct{}
func (r *NegativeReader) Read(p []byte) (n int, err error) {
return -1, nil
}
// A reader that might or might not misbehave
type ChaosReader struct {
sr strings.Reader
}
func (r *ChaosReader) Read(p []byte) (n int, err error) {
b := make([]byte, 1)
n, err = rand.Reader.Read(b)
if n != 1 || err != nil {
panic(fmt.Sprintf("ChaosReader: got (%d, %v) from rand.Reader.Read", n, err))
}
if b[0] <= 60 {
return 0, nil
}
if b[0] <= 120 && len(p) > 10 {
return r.sr.Read(p[:10])
}
return r.sr.Read(p)
}
func newChaosReader(str string) *ChaosReader {
return &ChaosReader{
sr: *strings.NewReader(str),
}
}
// Helper to automatically perform all testing
func ReaderParserTestHelper(t *testing.T, testCase string, expected interface{}) {
t.Logf("Test case: %q", testCase)
// Attempt parsing
actual, err := bencode.NewParserFromReader(strings.NewReader(testCase)).AsInterface()
if err != nil {
t.Fatalf("Failed to parse as interface, got error: %v", err)
}
// Compare results
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("Expected %v, but got %v", expected, actual)
}
// Attempt specific parsing
t.Logf("Attempting specific parsing as %T", actual)
switch actual := actual.(type) {
case string:
newActual, err := bencode.NewParserFromReader(strings.NewReader(testCase)).AsString()
if err != nil {
t.Fatalf("Failed to parse as string, got error: %v", err)
}
if newActual != actual {
t.Fatalf("Specific parsing as string returned %q, but originally was parsed as %q", newActual, actual)
}
case int:
newActual, err := bencode.NewParserFromReader(strings.NewReader(testCase)).AsInt()
if err != nil {
t.Fatalf("Failed to parse as int, got error: %v", err)
}
if newActual != actual {
t.Fatalf("Specific parsing as int returned %d, but originally was parsed as %d", newActual, actual)
}
case []interface{}:
newActual, err := bencode.NewParserFromReader(strings.NewReader(testCase)).AsList()
if err != nil {
t.Fatalf("Failed to parse as []interface{}, got error: %v", err)
}
if !reflect.DeepEqual(actual, newActual) {
t.Fatalf("Specific parsing as []interface{} returned %v, but originally was parsed as %v", newActual, actual)
}
case map[string]interface{}:
newActual, err := bencode.NewParserFromReader(strings.NewReader(testCase)).AsDict()
if err != nil {
t.Fatalf("Failed to parse as map[string]interface{}, got error: %v", err)
}
if !reflect.DeepEqual(actual, newActual) {
t.Fatalf("Specific parsing as map[string]interface{} returned %v, but originally was parsed as %v", newActual, actual)
}
default:
t.Fatalf("Got an unexpected type back: %T", actual)
}
}
func TestReaderParser(t *testing.T) {
// Test strings
for _, actual := range stringsTestCases {
ReaderParserTestHelper(t, fmt.Sprintf("%d:%s", len(actual), actual), actual)
}
// Test int
for test, actual := range intsTestCases {
ReaderParserTestHelper(t, test, actual)
}
// Test slices
for test, actual := range slicesTestCases {
ReaderParserTestHelper(t, test, actual)
}
// Test maps
for test, actual := range complexMapTestCases {
ReaderParserTestHelper(t, test, actual)
}
// Test Invalid Parse
for _, invalid := range invalidParserInputs {
if out, err := bencode.NewParserFromReader(strings.NewReader(invalid)).AsInterface(); err == nil {
t.Fatalf("Expected invalid %q (AsInterface) to fail.\nInstead got %v", invalid, out)
}
if out, err := bencode.NewParserFromReader(strings.NewReader(invalid)).AsInt(); err == nil {
t.Fatalf("Expected invalid %q (AsInt) to fail.\nInstead got %v", invalid, out)
}
if out, err := bencode.NewParserFromReader(strings.NewReader(invalid)).AsString(); err == nil {
t.Fatalf("Expected invalid %q (AsString) to fail.\nInstead got %v", invalid, out)
}
if out, err := bencode.NewParserFromReader(strings.NewReader(invalid)).AsList(); err == nil {
t.Fatalf("Expected invalid %q (AsList) to fail.\nInstead got %v", invalid, out)
}
if out, err := bencode.NewParserFromReader(strings.NewReader(invalid)).AsDict(); err == nil {
t.Fatalf("Expected invalid %q (AsDict) to fail.\nInstead got %v", invalid, out)
}
}
// Test Invalid Type
for invalid, trueType := range invalidTypeParse {
if trueType != 'i' {
if out, err := bencode.NewParserFromReader(strings.NewReader(invalid)).AsInt(); err == nil {
t.Fatalf("Expected invalid %q (AsInt) to fail.\nInstead got %v", invalid, out)
}
}
if trueType != 's' {
if out, err := bencode.NewParserFromReader(strings.NewReader(invalid)).AsString(); err == nil {
t.Fatalf("Expected invalid %q (AsString) to fail.\nInstead got %v", invalid, out)
}
}
if trueType != 'l' {
if out, err := bencode.NewParserFromReader(strings.NewReader(invalid)).AsList(); err == nil {
t.Fatalf("Expected invalid %q (AsList) to fail.\nInstead got %v", invalid, out)
}
}
if trueType != 'd' {
if out, err := bencode.NewParserFromReader(strings.NewReader(invalid)).AsDict(); err == nil {
t.Fatalf("Expected invalid %q (AsDict) to fail.\nInstead got %v", invalid, out)
}
}
}
// Test NoProgressReader
if obj, err := bencode.NewParserFromReader(&NoProgressReader{}).AsInterface(); err != io.ErrNoProgress {
t.Fatalf("Got (%v, %v) from NewParserFromReader(&NoProgressReader{}).AsInterface()", obj, err)
}
if obj, err := bencode.NewParserFromReader(&NoProgressReader{}).AsInt(); err != io.ErrNoProgress {
t.Fatalf("Got (%v, %v) from NewParserFromReader(&NoProgressReader{}).AsInt()", obj, err)
}
if obj, err := bencode.NewParserFromReader(&NoProgressReader{}).AsList(); err != io.ErrNoProgress {
t.Fatalf("Got (%v, %v) from NewParserFromReader(&NoProgressReader{}).AsList()", obj, err)
}
if obj, err := bencode.NewParserFromReader(&NoProgressReader{}).AsDict(); err != io.ErrNoProgress {
t.Fatalf("Got (%v, %v) from NewParserFromReader(&NoProgressReader{}).AsDict()", obj, err)
}
if obj, err := bencode.NewParserFromReader(&NoProgressReader{}).AsString(); err != io.ErrNoProgress {
t.Fatalf("Got (%v, %v) from NewParserFromReader(&NoProgressReader{}).AsString()", obj, err)
}
}
func TestReaderParserPanic(t *testing.T) {
// Test NegativeReader
defer func() {
if r := recover(); r == nil {
t.Fatalf("NewParserFromReader(&NegativeReader{}).AsInterface() was expected to panic, but did not")
}
}()
bencode.NewParserFromReader(&NegativeReader{}).AsInterface()
t.Fatalf("NewParserFromReader(&NegativeReader{}).AsInterface() was expected to panic, but did not")
}
func TestReaderParserChaos(t *testing.T) {
tester := func(testCase string, expected interface{}) {
obj, err := bencode.NewParserFromReader(newChaosReader(testCase)).AsInterface()
if err == nil && !reflect.DeepEqual(obj, expected) {
t.Fatalf("Got (%v, %v) from newChaosReader(%q) in AsInterface()", obj, err, testCase)
}
// Try with specific types
switch expected := expected.(type) {
case string:
obj, err := bencode.NewParserFromReader(newChaosReader(testCase)).AsString()
if err == nil && obj != expected {
t.Fatalf("Got (%s, %v) from newChaosReader(%q) in AsString()", obj, err, testCase)
}
case int:
obj, err := bencode.NewParserFromReader(newChaosReader(testCase)).AsInt()
if err == nil && obj != expected {
t.Fatalf("Got (%d, %v) from newChaosReader(%q) in AsInt()", obj, err, testCase)
}
case []interface{}:
obj, err := bencode.NewParserFromReader(newChaosReader(testCase)).AsList()
if err == nil && !reflect.DeepEqual(obj, expected) {
t.Fatalf("Got (%v, %v) from newChaosReader(%q) in AsList()", obj, err, testCase)
}
case map[string]interface{}:
obj, err := bencode.NewParserFromReader(newChaosReader(testCase)).AsDict()
if err == nil && !reflect.DeepEqual(obj, expected) {
t.Fatalf("Got (%v, %v) from newChaosReader(%q) in AsList()", obj, err, testCase)
}
}
}
// Run cases (multiple times to hit different breakage points)
for i := 0; i < 1024; i++ {
for _, actual := range stringsTestCases {
tester(fmt.Sprintf("%d:%s", len(actual), actual), actual)
}
for test, expected := range intsTestCases {
tester(test, expected)
}
for test, expected := range slicesTestCases {
tester(test, expected)
}
for test, expected := range complexMapTestCases {
tester(test, expected)
}
}
}
func BenchmarkReaderParser(b *testing.B) {
for benchName, testString := range parserBenchmarks {
b.Run(benchName, func(b *testing.B) {
for i := 0; i < b.N; i++ {
bencode.NewParserFromReader(strings.NewReader(testString)).AsInterface()
}
})
}
b.Run("torrentStringAsString", func(b *testing.B) {
for i := 0; i < b.N; i++ {
bencode.NewParserFromReader(strings.NewReader(fedoraMagnetParsed)).AsString()
}
})
}
func FuzzReaderParser(f *testing.F) {
// > Add examples
// Add strings examples
for _, actual := range stringsTestCases {
f.Add(fmt.Sprintf("%d:%s", len(actual), actual))
}
// Add int examples
for test := range intsTestCases {
f.Add(test)
}
// Add slices examples
for test := range slicesTestCases {
f.Add(test)
}
// Add maps examples
for test := range complexMapTestCases {
f.Add(test)
}
// Add Invalid Parse examples
for _, invalid := range invalidParserInputs {
f.Add(invalid)
}
// Add Invalid Type examples
for invalid := range invalidTypeParse {
f.Add(invalid)
}
// > Setup test
f.Fuzz(func(t *testing.T, test string) {
// Look for panics in type-specific parsing
bencode.NewParserFromReader(strings.NewReader(test)).AsInt()
bencode.NewParserFromReader(strings.NewReader(test)).AsString()
bencode.NewParserFromReader(strings.NewReader(test)).AsList()
bencode.NewParserFromReader(strings.NewReader(test)).AsDict()
// Now parse as generic interface
parsedObj, err := bencode.NewParserFromReader(strings.NewReader(test)).AsInterface()
if err != nil {
t.SkipNow()
}
// Try to re-encode
reEncoded, err := bencode.NewEncoderFromInterface(parsedObj)
if err != nil {
t.Fatalf("Failed to re-encode %v: %v", parsedObj, err)
}
// Parse encoded for final check
reParsedObj, err := bencode.NewParserFromReader(strings.NewReader(reEncoded.String())).AsInterface()
if err != nil {
t.Fatalf("Failed to re-decode %q: %v", reEncoded.String(), err)
}
// Compare original
if !reflect.DeepEqual(parsedObj, reParsedObj) {
t.Fatalf("Originally parsed as %v, encoded as %q, and re-parsed as different %v", parsedObj, reEncoded, reParsedObj)
}
})
}