@@ -10,8 +10,8 @@ import (
10
10
"encoding/hex"
11
11
"encoding/json"
12
12
"fmt"
13
- "io/ioutil"
14
13
"math"
14
+ "os"
15
15
"path"
16
16
"strconv"
17
17
"strings"
@@ -60,11 +60,13 @@ type parseErrorTestCase struct {
60
60
61
61
const dataDir = "../testdata/bson-corpus/"
62
62
63
- func findJSONFilesInDir (t * testing. T , dir string ) []string {
63
+ func findJSONFilesInDir (dir string ) ( []string , error ) {
64
64
files := make ([]string , 0 )
65
65
66
- entries , err := ioutil .ReadDir (dir )
67
- require .NoError (t , err )
66
+ entries , err := os .ReadDir (dir )
67
+ if err != nil {
68
+ return nil , err
69
+ }
68
70
69
71
for _ , entry := range entries {
70
72
if entry .IsDir () || path .Ext (entry .Name ()) != ".json" {
@@ -74,7 +76,65 @@ func findJSONFilesInDir(t *testing.T, dir string) []string {
74
76
files = append (files , entry .Name ())
75
77
}
76
78
77
- return files
79
+ return files , nil
80
+ }
81
+
82
+ // seedExtJSON will add the byte representation of the "extJSON" string to the fuzzer's coprus.
83
+ func seedExtJSON (f * testing.F , extJSON string , extJSONType string , desc string ) {
84
+ jbytes , err := jsonToBytes (extJSON , extJSONType , desc )
85
+ if err != nil {
86
+ f .Fatalf ("failed to convert JSON to bytes: %v" , err )
87
+ }
88
+
89
+ f .Add (jbytes )
90
+ }
91
+
92
+ // seedTestCase will add the byte representation for each "extJSON" string of each valid test case to the fuzzer's
93
+ // corpus.
94
+ func seedTestCase (f * testing.F , tcase * testCase ) {
95
+ for _ , vtc := range tcase .Valid {
96
+ seedExtJSON (f , vtc .CanonicalExtJSON , "canonical" , vtc .Description )
97
+
98
+ // Seed the relaxed extended JSON.
99
+ if vtc .RelaxedExtJSON != nil {
100
+ seedExtJSON (f , * vtc .RelaxedExtJSON , "relaxed" , vtc .Description )
101
+ }
102
+
103
+ // Seed the degenerate extended JSON.
104
+ if vtc .DegenerateExtJSON != nil {
105
+ seedExtJSON (f , * vtc .DegenerateExtJSON , "degenerate" , vtc .Description )
106
+ }
107
+
108
+ // Seed the converted extended JSON.
109
+ if vtc .ConvertedExtJSON != nil {
110
+ seedExtJSON (f , * vtc .ConvertedExtJSON , "converted" , vtc .Description )
111
+ }
112
+ }
113
+ }
114
+
115
+ // seedBSONCorpus will unmarshal the data from "testdata/bson-corpus" into a slice of "testCase" structs and then
116
+ // marshal the "*_extjson" field of each "validityTestCase" into a slice of bytes to seed the fuzz corpus.
117
+ func seedBSONCorpus (f * testing.F ) {
118
+ fileNames , err := findJSONFilesInDir (dataDir )
119
+ if err != nil {
120
+ f .Fatalf ("failed to find JSON files in directory %q: %v" , dataDir , err )
121
+ }
122
+
123
+ for _ , fileName := range fileNames {
124
+ filePath := path .Join (dataDir , fileName )
125
+
126
+ file , err := os .Open (filePath )
127
+ if err != nil {
128
+ f .Fatalf ("failed to open file %q: %v" , filePath , err )
129
+ }
130
+
131
+ var tcase testCase
132
+ if err := json .NewDecoder (file ).Decode (& tcase ); err != nil {
133
+ f .Fatal (err )
134
+ }
135
+
136
+ seedTestCase (f , & tcase )
137
+ }
78
138
}
79
139
80
140
func needsEscapedUnicode (bsonType string ) bool {
@@ -196,11 +256,27 @@ func nativeToBSON(t *testing.T, cB []byte, doc D, testDesc, bType, docSrcDesc st
196
256
}
197
257
198
258
// jsonToNative decodes the extended JSON string (ej) into a native Document
199
- func jsonToNative (t * testing. T , ej , ejType , testDesc string ) D {
259
+ func jsonToNative (ej , ejType , testDesc string ) ( D , error ) {
200
260
var doc D
201
- err := UnmarshalExtJSON ([]byte (ej ), ejType != "relaxed" , & doc )
202
- expectNoError (t , err , fmt .Sprintf ("%s: decoding %s extended JSON" , testDesc , ejType ))
203
- return doc
261
+ if err := UnmarshalExtJSON ([]byte (ej ), ejType != "relaxed" , & doc ); err != nil {
262
+ return nil , fmt .Errorf ("%s: decoding %s extended JSON: %w" , testDesc , ejType , err )
263
+ }
264
+ return doc , nil
265
+ }
266
+
267
+ // jsonToBytes decodes the extended JSON string (ej) into canonical BSON and then encodes it into a byte slice.
268
+ func jsonToBytes (ej , ejType , testDesc string ) ([]byte , error ) {
269
+ native , err := jsonToNative (ej , ejType , testDesc )
270
+ if err != nil {
271
+ return nil , err
272
+ }
273
+
274
+ b , err := Marshal (native )
275
+ if err != nil {
276
+ return nil , fmt .Errorf ("%s: encoding %s BSON: %w" , testDesc , ejType , err )
277
+ }
278
+
279
+ return b , nil
204
280
}
205
281
206
282
// nativeToJSON encodes the native Document (doc) into an extended JSON string
@@ -217,7 +293,7 @@ func nativeToJSON(t *testing.T, ej string, doc D, testDesc, ejType, ejShortName,
217
293
218
294
func runTest (t * testing.T , file string ) {
219
295
filepath := path .Join (dataDir , file )
220
- content , err := ioutil .ReadFile (filepath )
296
+ content , err := os .ReadFile (filepath )
221
297
require .NoError (t , err )
222
298
223
299
// Remove ".json" from filename.
@@ -260,14 +336,16 @@ func runTest(t *testing.T, file string) {
260
336
nativeToJSON (t , rEJ , doc , v .Description , "relaxed" , "rEJ" , "bson_to_native(cB)" )
261
337
262
338
/*** relaxed extended JSON round-trip tests (if exists) ***/
263
- doc = jsonToNative (t , rEJ , "relaxed" , v .Description )
339
+ doc , err = jsonToNative (rEJ , "relaxed" , v .Description )
340
+ require .NoError (t , err )
264
341
265
342
// native_to_relaxed_extended_json(json_to_native(rEJ)) = rEJ
266
343
nativeToJSON (t , rEJ , doc , v .Description , "relaxed" , "eJR" , "json_to_native(rEJ)" )
267
344
}
268
345
269
346
/*** canonical extended JSON round-trip tests ***/
270
- doc = jsonToNative (t , cEJ , "canonical" , v .Description )
347
+ doc , err = jsonToNative (cEJ , "canonical" , v .Description )
348
+ require .NoError (t , err )
271
349
272
350
// native_to_canonical_extended_json(json_to_native(cEJ)) = cEJ
273
351
nativeToJSON (t , cEJ , doc , v .Description , "canonical" , "cEJ" , "json_to_native(cEJ)" )
@@ -295,7 +373,8 @@ func runTest(t *testing.T, file string) {
295
373
dEJ = normalizeCanonicalDouble (t , * test .TestKey , dEJ )
296
374
}
297
375
298
- doc = jsonToNative (t , dEJ , "degenerate canonical" , v .Description )
376
+ doc , err = jsonToNative (dEJ , "degenerate canonical" , v .Description )
377
+ require .NoError (t , err )
299
378
300
379
// native_to_canonical_extended_json(json_to_native(dEJ)) = cEJ
301
380
nativeToJSON (t , cEJ , doc , v .Description , "degenerate canonical" , "cEJ" , "json_to_native(dEJ)" )
@@ -366,7 +445,12 @@ func runTest(t *testing.T, file string) {
366
445
}
367
446
368
447
func Test_BsonCorpus (t * testing.T ) {
369
- for _ , file := range findJSONFilesInDir (t , dataDir ) {
448
+ jsonFiles , err := findJSONFilesInDir (dataDir )
449
+ if err != nil {
450
+ t .Fatalf ("error finding JSON files in %s: %v" , dataDir , err )
451
+ }
452
+
453
+ for _ , file := range jsonFiles {
370
454
runTest (t , file )
371
455
}
372
456
}
0 commit comments