1
2
3
4
5
6
7 package bsoncore
8
9 import (
10 "bytes"
11 "errors"
12 "io"
13 "strconv"
14 "testing"
15
16 "github.com/google/go-cmp/cmp"
17 )
18
19 func TestDocumentSequence(t *testing.T) {
20
21 genArrayStyle := func(num int) []byte {
22 idx, seq := AppendDocumentStart(nil)
23 for i := 0; i < num; i++ {
24 seq = AppendDocumentElement(
25 seq, strconv.Itoa(i),
26 BuildDocument(nil, AppendDoubleElement(nil, "pi", 3.14159)),
27 )
28 }
29 seq, _ = AppendDocumentEnd(seq, idx)
30 return seq
31 }
32 genSequenceStyle := func(num int) []byte {
33 var seq []byte
34 for i := 0; i < num; i++ {
35 seq = append(seq, BuildDocument(nil, AppendDoubleElement(nil, "pi", 3.14159))...)
36 }
37 return seq
38 }
39
40 idx, arrayStyle := AppendDocumentStart(nil)
41 idx2, arrayStyle := AppendDocumentElementStart(arrayStyle, "0")
42 arrayStyle = AppendDoubleElement(arrayStyle, "pi", 3.14159)
43 arrayStyle, _ = AppendDocumentEnd(arrayStyle, idx2)
44 idx2, arrayStyle = AppendDocumentElementStart(arrayStyle, "1")
45 arrayStyle = AppendStringElement(arrayStyle, "hello", "world")
46 arrayStyle, _ = AppendDocumentEnd(arrayStyle, idx2)
47 arrayStyle, _ = AppendDocumentEnd(arrayStyle, idx)
48
49 t.Run("Documents", func(t *testing.T) {
50 testCases := []struct {
51 name string
52 style DocumentSequenceStyle
53 data []byte
54 documents []Document
55 err error
56 }{
57 {
58 "SequenceStle/corrupted document",
59 SequenceStyle,
60 []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
61 nil,
62 ErrCorruptedDocument,
63 },
64 {
65 "SequenceStyle/success",
66 SequenceStyle,
67 BuildDocument(
68 BuildDocument(
69 nil,
70 AppendStringElement(AppendDoubleElement(nil, "pi", 3.14159), "hello", "world"),
71 ),
72 AppendDoubleElement(AppendStringElement(nil, "hello", "world"), "pi", 3.14159),
73 ),
74 []Document{
75 BuildDocument(nil, AppendStringElement(AppendDoubleElement(nil, "pi", 3.14159), "hello", "world")),
76 BuildDocument(nil, AppendDoubleElement(AppendStringElement(nil, "hello", "world"), "pi", 3.14159)),
77 },
78 nil,
79 },
80 {
81 "ArrayStyle/insufficient bytes",
82 ArrayStyle,
83 []byte{0x01, 0x02, 0x03, 0x04, 0x05},
84 nil,
85 ErrCorruptedDocument,
86 },
87 {
88 "ArrayStyle/non-document",
89 ArrayStyle,
90 BuildDocument(nil, AppendDoubleElement(nil, "0", 12345.67890)),
91 nil,
92 ErrNonDocument,
93 },
94 {
95 "ArrayStyle/success",
96 ArrayStyle,
97 arrayStyle,
98 []Document{
99 BuildDocument(nil, AppendDoubleElement(nil, "pi", 3.14159)),
100 BuildDocument(nil, AppendStringElement(nil, "hello", "world")),
101 },
102 nil,
103 },
104 {"Invalid DocumentSequenceStyle", 0, nil, nil, ErrInvalidDocumentSequenceStyle},
105 }
106
107 for _, tc := range testCases {
108 t.Run(tc.name, func(t *testing.T) {
109 ds := &DocumentSequence{
110 Style: tc.style,
111 Data: tc.data,
112 }
113 documents, err := ds.Documents()
114 if !cmp.Equal(documents, tc.documents) {
115 t.Errorf("Documents do not match. got %v; want %v", documents, tc.documents)
116 }
117 if !errors.Is(err, tc.err) {
118 t.Errorf("Errors do not match. got %v; want %v", err, tc.err)
119 }
120 })
121 }
122 })
123 t.Run("Next", func(t *testing.T) {
124 seqDoc := BuildDocument(
125 BuildDocument(
126 nil,
127 AppendDoubleElement(nil, "pi", 3.14159),
128 ),
129 AppendStringElement(nil, "hello", "world"),
130 )
131
132 idx, arrayStyle := AppendDocumentStart(nil)
133 idx2, arrayStyle := AppendDocumentElementStart(arrayStyle, "0")
134 arrayStyle = AppendDoubleElement(arrayStyle, "pi", 3.14159)
135 arrayStyle, _ = AppendDocumentEnd(arrayStyle, idx2)
136 idx2, arrayStyle = AppendDocumentElementStart(arrayStyle, "1")
137 arrayStyle = AppendStringElement(arrayStyle, "hello", "world")
138 arrayStyle, _ = AppendDocumentEnd(arrayStyle, idx2)
139 arrayStyle, _ = AppendDocumentEnd(arrayStyle, idx)
140
141 testCases := []struct {
142 name string
143 style DocumentSequenceStyle
144 data []byte
145 pos int
146 document Document
147 err error
148 }{
149 {"io.EOF", 0, make([]byte, 10), 10, nil, io.EOF},
150 {
151 "SequenceStyle/corrupted document",
152 SequenceStyle,
153 []byte{0x01, 0x02, 0x03, 0x04},
154 0,
155 nil,
156 ErrCorruptedDocument,
157 },
158 {
159 "SequenceStyle/success/first",
160 SequenceStyle,
161 seqDoc,
162 0,
163 BuildDocument(nil, AppendDoubleElement(nil, "pi", 3.14159)),
164 nil,
165 },
166 {
167 "SequenceStyle/success/second",
168 SequenceStyle,
169 seqDoc,
170 17,
171 BuildDocument(nil, AppendStringElement(nil, "hello", "world")),
172 nil,
173 },
174 {
175 "ArrayStyle/corrupted document/too short",
176 ArrayStyle,
177 []byte{0x01, 0x02, 0x03},
178 0,
179 nil,
180 ErrCorruptedDocument,
181 },
182 {
183 "ArrayStyle/corrupted document/invalid element",
184 ArrayStyle,
185 []byte{0x00, 0x00, 0x00, 0x00, 0x01, '0', 0x00, 0x01, 0x02},
186 0,
187 nil,
188 ErrCorruptedDocument,
189 },
190 {
191 "ArrayStyle/non-document",
192 ArrayStyle,
193 BuildDocument(nil, AppendDoubleElement(nil, "0", 12345.67890)),
194 0,
195 nil,
196 ErrNonDocument,
197 },
198 {
199 "ArrayStyle/success/first",
200 ArrayStyle,
201 arrayStyle,
202 0,
203 BuildDocument(nil, AppendDoubleElement(nil, "pi", 3.14159)),
204 nil,
205 },
206 {
207 "ArrayStyle/success/second",
208 ArrayStyle,
209 arrayStyle,
210 24,
211 BuildDocument(nil, AppendStringElement(nil, "hello", "world")),
212 nil,
213 },
214 {"Invalid DocumentSequenceStyle", 0, make([]byte, 4), 0, nil, ErrInvalidDocumentSequenceStyle},
215 }
216
217 for _, tc := range testCases {
218 t.Run(tc.name, func(t *testing.T) {
219 ds := &DocumentSequence{
220 Style: tc.style,
221 Data: tc.data,
222 Pos: tc.pos,
223 }
224 document, err := ds.Next()
225 if !bytes.Equal(document, tc.document) {
226 t.Errorf("Documents do not match. got %v; want %v", document, tc.document)
227 }
228 if !errors.Is(err, tc.err) {
229 t.Errorf("Errors do not match. got %v; want %v", err, tc.err)
230 }
231 })
232 }
233 })
234
235 t.Run("Full Iteration", func(t *testing.T) {
236 testCases := []struct {
237 name string
238 style DocumentSequenceStyle
239 data []byte
240 count int
241 }{
242 {"SequenceStyle/success/nil", SequenceStyle, nil, 0},
243 {"SequenceStyle/success/0", SequenceStyle, []byte{}, 0},
244 {"SequenceStyle/success/1", SequenceStyle, genSequenceStyle(1), 1},
245 {"SequenceStyle/success/2", SequenceStyle, genSequenceStyle(2), 2},
246 {"SequenceStyle/success/10", SequenceStyle, genSequenceStyle(10), 10},
247 {"SequenceStyle/success/100", SequenceStyle, genSequenceStyle(100), 100},
248 {"ArrayStyle/success/nil", ArrayStyle, nil, 0},
249 {"ArrayStyle/success/0", ArrayStyle, []byte{0x05, 0x00, 0x00, 0x00, 0x00}, 0},
250 {"ArrayStyle/success/1", ArrayStyle, genArrayStyle(1), 1},
251 {"ArrayStyle/success/2", ArrayStyle, genArrayStyle(2), 2},
252 {"ArrayStyle/success/10", ArrayStyle, genArrayStyle(10), 10},
253 {"ArrayStyle/success/100", ArrayStyle, genArrayStyle(100), 100},
254 }
255
256 for _, tc := range testCases {
257 t.Run("Documents/"+tc.name, func(t *testing.T) {
258 ds := &DocumentSequence{
259 Style: tc.style,
260 Data: tc.data,
261 }
262 docs, err := ds.Documents()
263 if err != nil {
264 t.Fatalf("Unexpected error: %v", err)
265 }
266 count := len(docs)
267 if count != tc.count {
268 t.Errorf("Coun't fully iterate documents, wrong count. got %v; want %v", count, tc.count)
269 }
270 })
271 t.Run("Next/"+tc.name, func(t *testing.T) {
272 ds := &DocumentSequence{
273 Style: tc.style,
274 Data: tc.data,
275 }
276 var docs []Document
277 for {
278 doc, err := ds.Next()
279 if errors.Is(err, io.EOF) {
280 break
281 }
282 if err != nil {
283 t.Fatalf("Unexpected error: %v", err)
284 }
285 docs = append(docs, doc)
286 }
287 count := len(docs)
288 if count != tc.count {
289 t.Errorf("Coun't fully iterate documents, wrong count. got %v; want %v", count, tc.count)
290 }
291 })
292 }
293 })
294 t.Run("DocumentCount", func(t *testing.T) {
295 testCases := []struct {
296 name string
297 style DocumentSequenceStyle
298 data []byte
299 count int
300 }{
301 {
302 "SequenceStyle/corrupt document/first",
303 SequenceStyle,
304 []byte{0x01, 0x02, 0x03},
305 0,
306 },
307 {
308 "SequenceStyle/corrupt document/second",
309 SequenceStyle,
310 []byte{0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03},
311 0,
312 },
313 {"SequenceStyle/success/nil", SequenceStyle, nil, 0},
314 {"SequenceStyle/success/0", SequenceStyle, []byte{}, 0},
315 {"SequenceStyle/success/1", SequenceStyle, genSequenceStyle(1), 1},
316 {"SequenceStyle/success/2", SequenceStyle, genSequenceStyle(2), 2},
317 {"SequenceStyle/success/10", SequenceStyle, genSequenceStyle(10), 10},
318 {"SequenceStyle/success/100", SequenceStyle, genSequenceStyle(100), 100},
319 {
320 "ArrayStyle/corrupt document/length",
321 ArrayStyle,
322 []byte{0x01, 0x02, 0x03},
323 0,
324 },
325 {
326 "ArrayStyle/corrupt element/first",
327 ArrayStyle,
328 BuildDocument(nil, []byte{0x01, 0x00, 0x03, 0x04, 0x05}),
329 0,
330 },
331 {
332 "ArrayStyle/corrupt element/second",
333 ArrayStyle,
334 BuildDocument(nil, []byte{0x0A, 0x00, 0x01, 0x00, 0x03, 0x04, 0x05}),
335 0,
336 },
337 {"ArrayStyle/success/nil", ArrayStyle, nil, 0},
338 {"ArrayStyle/success/0", ArrayStyle, []byte{0x05, 0x00, 0x00, 0x00, 0x00}, 0},
339 {"ArrayStyle/success/1", ArrayStyle, genArrayStyle(1), 1},
340 {"ArrayStyle/success/2", ArrayStyle, genArrayStyle(2), 2},
341 {"ArrayStyle/success/10", ArrayStyle, genArrayStyle(10), 10},
342 {"ArrayStyle/success/100", ArrayStyle, genArrayStyle(100), 100},
343 {"Invalid DocumentSequenceStyle", 0, nil, 0},
344 }
345
346 for _, tc := range testCases {
347 t.Run(tc.name, func(t *testing.T) {
348 ds := &DocumentSequence{
349 Style: tc.style,
350 Data: tc.data,
351 }
352 count := ds.DocumentCount()
353 if count != tc.count {
354 t.Errorf("Document counts don't match. got %v; want %v", count, tc.count)
355 }
356 })
357 }
358 })
359 t.Run("Empty", func(t *testing.T) {
360 testCases := []struct {
361 name string
362 ds *DocumentSequence
363 isEmpty bool
364 }{
365 {"ArrayStyle/is empty/nil", nil, true},
366 {"ArrayStyle/is empty/0", &DocumentSequence{Style: ArrayStyle, Data: []byte{0x05, 0x00, 0x00, 0x00, 0x00}}, true},
367 {"ArrayStyle/is not empty/non-0", &DocumentSequence{Style: ArrayStyle, Data: genArrayStyle(10)}, false},
368 {"SequenceStyle/is empty/nil", nil, true},
369 {"SequenceStyle/is empty/0", &DocumentSequence{Style: SequenceStyle, Data: []byte{}}, true},
370 {"SequenceStyle/is not empty/non-0", &DocumentSequence{Style: SequenceStyle, Data: genSequenceStyle(10)}, false},
371 }
372
373 for _, tc := range testCases {
374 t.Run(tc.name, func(t *testing.T) {
375 isEmpty := tc.ds.Empty()
376 if isEmpty != tc.isEmpty {
377 t.Errorf("Unexpected Empty result. got %v; want %v", isEmpty, tc.isEmpty)
378 }
379 })
380 }
381 })
382 t.Run("ResetIterator", func(t *testing.T) {
383 ds := &DocumentSequence{Pos: 1234567890}
384 want := 0
385 ds.ResetIterator()
386 if ds.Pos != want {
387 t.Errorf("Unexpected position after ResetIterator. got %d; want %d", ds.Pos, want)
388 }
389 })
390 t.Run("no panic on nil", func(t *testing.T) {
391 capturePanic := func() {
392 if err := recover(); err != nil {
393 t.Errorf("Unexpected panic. got %v; want <nil>", err)
394 }
395 }
396 t.Run("DocumentCount", func(t *testing.T) {
397 defer capturePanic()
398 var ds *DocumentSequence
399 _ = ds.DocumentCount()
400 })
401 t.Run("Empty", func(t *testing.T) {
402 defer capturePanic()
403 var ds *DocumentSequence
404 _ = ds.Empty()
405 })
406 t.Run("ResetIterator", func(t *testing.T) {
407 defer capturePanic()
408 var ds *DocumentSequence
409 ds.ResetIterator()
410 })
411 t.Run("Documents", func(t *testing.T) {
412 defer capturePanic()
413 var ds *DocumentSequence
414 _, _ = ds.Documents()
415 })
416 t.Run("Next", func(t *testing.T) {
417 defer capturePanic()
418 var ds *DocumentSequence
419 _, _ = ds.Next()
420 })
421 })
422 }
423
View as plain text