tag_test.mx raw
1 package tag
2
3 import (
4 "bytes"
5 "testing"
6
7 "git.smesh.lol/nostr/pkg/hex"
8 )
9
10 // upperHex renders src as uppercase hex. Equals carries separate branches for
11 // the 'A'-'F' nibbles that a lowercase encoder never reaches.
12 func upperHex(src []byte) (out []byte) {
13 tbl := []byte("0123456789ABCDEF")
14 out = []byte{:len(src) * 2}
15 for i := range src {
16 out[i*2] = tbl[int32(src[i]>>4)]
17 out[i*2+1] = tbl[int32(src[i]&0x0F)]
18 }
19 return
20 }
21
22 const zeroHex64 = "0000000000000000000000000000000000000000000000000000000000000000"
23
24 func TestNewAndFree(t *testing.T) {
25 nt := New()
26 if nt == nil {
27 t.Fatal("New returned nil")
28 }
29 if nt.Len() != 0 {
30 t.Fatalf("New().Len() = %d, want 0", nt.Len())
31 }
32 nt.T = push(nt.T, []byte("k"))
33 if nt.Len() != 1 {
34 t.Fatalf("Len after push = %d, want 1", nt.Len())
35 }
36 nt.Free()
37 if nt.T != nil {
38 t.Fatal("Free did not clear the slice")
39 }
40 if nt.Len() != 0 {
41 t.Fatalf("Len after Free = %d, want 0", nt.Len())
42 }
43 capped := NewWithCap(8)
44 if capped.Len() != 0 {
45 t.Fatalf("NewWithCap(8).Len() = %d, want 0", capped.Len())
46 }
47 }
48
49 func TestLenNilReceiver(t *testing.T) {
50 var nt *T
51 if nt.Len() != 0 {
52 t.Fatalf("nil Len = %d, want 0", nt.Len())
53 }
54 }
55
56 func TestLenLessSwapContains(t *testing.T) {
57 a := NewFromBytesSlice([]byte("a"), []byte("one"))
58 b := NewFromBytesSlice([]byte("b"), []byte("two"))
59 if a.Len() != 2 || b.Len() != 2 {
60 t.Fatalf("Len: a=%d b=%d, want 2 each", a.Len(), b.Len())
61 }
62 if !a.Less(0, 1) {
63 t.Fatal("Less(0,1) should be true for a<one")
64 }
65 if a.Less(1, 0) {
66 t.Fatal("Less(1,0) should be false")
67 }
68 b.Swap(0, 1)
69 if !bytes.Equal(b.T[0], []byte("two")) {
70 t.Fatalf("Swap left value = %s", b.T[0])
71 }
72 if !bytes.Equal(b.T[1], []byte("b")) {
73 t.Fatalf("Swap left key = %s", b.T[1])
74 }
75 if !a.Contains([]byte("a")) {
76 t.Fatal("Contains should find the key")
77 }
78 if !a.Contains([]byte("one")) {
79 t.Fatal("Contains should find the value")
80 }
81 if a.Contains([]byte("zzz")) {
82 t.Fatal("Contains should not find an absent field")
83 }
84 if New().Contains([]byte("a")) {
85 t.Fatal("empty tag should contain nothing")
86 }
87 }
88
89 func TestAccessors(t *testing.T) {
90 empty := New()
91 if empty.Key() != nil {
92 t.Fatal("empty Key should be nil")
93 }
94 if empty.Value() != nil {
95 t.Fatal("empty Value should be nil")
96 }
97 if empty.Relay() != nil {
98 t.Fatal("empty Relay should be nil")
99 }
100 one := NewFromBytesSlice([]byte("k"))
101 if !bytes.Equal(one.Key(), []byte("k")) {
102 t.Fatalf("Key = %s, want k", one.Key())
103 }
104 if one.Value() != nil {
105 t.Fatal("one-field Value should be nil")
106 }
107 if one.Relay() != nil {
108 t.Fatal("one-field Relay should be nil")
109 }
110 two := NewFromBytesSlice([]byte("k"), []byte("v"))
111 if !bytes.Equal(two.Key(), []byte("k")) {
112 t.Fatalf("two-field Key = %s", two.Key())
113 }
114 if !bytes.Equal(two.Value(), []byte("v")) {
115 t.Fatalf("two-field Value = %s", two.Value())
116 }
117 if two.Relay() != nil {
118 t.Fatal("two-field Relay should be nil")
119 }
120 three := NewFromBytesSlice([]byte("k"), []byte("v"), []byte("r"))
121 if !bytes.Equal(three.Relay(), []byte("r")) {
122 t.Fatalf("three-field Relay = %s", three.Relay())
123 }
124 var nt *T
125 if nt.Value() != nil {
126 t.Fatal("nil receiver Value should be nil")
127 }
128 }
129
130 func TestMarshalTag(t *testing.T) {
131 tv := NewFromBytesSlice([]byte("t"), []byte("test"))
132 got := string(tv.Marshal(nil))
133 if got != "[\"t\",\"test\"]" {
134 t.Fatalf("Marshal = %s", got)
135 }
136 gotDst := string(tv.Marshal([]byte("pre")))
137 if gotDst != "pre[\"t\",\"test\"]" {
138 t.Fatalf("Marshal into dst = %s", gotDst)
139 }
140 if string(New().Marshal(nil)) != "[]" {
141 t.Fatalf("empty Marshal = %s", string(New().Marshal(nil)))
142 }
143 esc := NewFromBytesSlice([]byte("t"), []byte("a\"b\\c"))
144 gotEsc := string(esc.Marshal(nil))
145 if gotEsc != "[\"t\",\"a\\\"b\\\\c\"]" {
146 t.Fatalf("escaped Marshal = %s", gotEsc)
147 }
148 jb, jerr := tv.MarshalJSON()
149 if jerr != nil {
150 t.Fatal(jerr)
151 return
152 }
153 if string(jb) != "[\"t\",\"test\"]" {
154 t.Fatalf("MarshalJSON = %s", string(jb))
155 }
156 }
157
158 func TestMarshalBinaryValue(t *testing.T) {
159 bin := []byte{:33}
160 bin[0] = 0x5a
161 bin[1] = 0xa5
162 bt := NewFromBytesSlice([]byte("e"), bin)
163 hx := hex.EncAppend(nil, bin[:HashLen])
164 want := string([]byte("[\"e\",\"") | hx | []byte("\"]"))
165 got := string(bt.Marshal(nil))
166 if got != want {
167 t.Fatalf("binary Marshal = %s, want %s", got, want)
168 }
169 // A 33-byte value whose last byte is not zero is not binary-encoded, so it
170 // must be quoted raw rather than rendered as hex.
171 notBin := []byte{:33}
172 notBin[32] = 1
173 nb := NewFromBytesSlice([]byte("e"), notBin)
174 gotNb := string(nb.Marshal(nil))
175 if gotNb == want {
176 t.Fatal("non-binary 33-byte value must not be hex-encoded")
177 }
178 }
179
180 func TestUnmarshalTag(t *testing.T) {
181 tt := New()
182 rem, err := tt.Unmarshal([]byte("[\"t\",\"test\"],tail"))
183 if err != nil {
184 t.Fatal(err)
185 return
186 }
187 if tt.Len() != 2 {
188 t.Fatalf("Len = %d, want 2", tt.Len())
189 }
190 if !bytes.Equal(tt.Key(), []byte("t")) {
191 t.Fatalf("Key = %s", tt.Key())
192 }
193 if !bytes.Equal(tt.Value(), []byte("test")) {
194 t.Fatalf("Value = %s", tt.Value())
195 }
196 if string(rem) != ",tail" {
197 t.Fatalf("remainder = %s, want ,tail", string(rem))
198 }
199 et := New()
200 remEmpty, errEmpty := et.Unmarshal([]byte("[]"))
201 if errEmpty != nil {
202 t.Fatal(errEmpty)
203 return
204 }
205 if et.Len() != 0 || len(remEmpty) != 0 {
206 t.Fatalf("empty parse: Len=%d rem=%s", et.Len(), string(remEmpty))
207 }
208 esc := New()
209 _, errEsc := esc.Unmarshal([]byte("[\"t\",\"a\\nb\"]"))
210 if errEsc != nil {
211 t.Fatal(errEsc)
212 return
213 }
214 if !bytes.Equal(esc.Value(), []byte("a\nb")) {
215 t.Fatalf("unescaped Value = %s", esc.Value())
216 }
217 three := New()
218 _, errThree := three.Unmarshal([]byte("[\"k\",\"v\",\"r\"]"))
219 if errThree != nil {
220 t.Fatal(errThree)
221 return
222 }
223 if !bytes.Equal(three.Relay(), []byte("r")) {
224 t.Fatalf("Relay = %s", three.Relay())
225 }
226 }
227
228 func TestUnmarshalTagBinary(t *testing.T) {
229 raw := []byte{:32}
230 raw[0] = 0x5a
231 raw[1] = 0xa5
232 hx := hex.EncAppend(nil, raw)
233 ej := New()
234 _, ejErr := ej.Unmarshal([]byte("[\"e\",\"") | hx | []byte("\"]"))
235 if ejErr != nil {
236 t.Fatal(ejErr)
237 return
238 }
239 if !isBinaryEncoded(ej.Value()) {
240 t.Fatal("e tag with 64-hex value should be binary-encoded")
241 }
242 if !bytes.Equal(ej.ValueHex(), hx) {
243 t.Fatalf("ValueHex = %s, want %s", ej.ValueHex(), hx)
244 }
245 if !bytes.Equal(ej.ValueBinary(), raw) {
246 t.Fatalf("ValueBinary = %s, want %s", ej.ValueBinary(), raw)
247 }
248 pj := New()
249 _, pjErr := pj.Unmarshal([]byte("[\"p\",\"") | hx | []byte("\"]"))
250 if pjErr != nil {
251 t.Fatal(pjErr)
252 return
253 }
254 if !isBinaryEncoded(pj.Value()) {
255 t.Fatal("p tag with 64-hex value should be binary-encoded")
256 }
257 // A key whose length is not one is never optimized.
258 kj := New()
259 _, kjErr := kj.Unmarshal([]byte("[\"ab\",\"") | hx | []byte("\"]"))
260 if kjErr != nil {
261 t.Fatal(kjErr)
262 return
263 }
264 if isBinaryEncoded(kj.Value()) {
265 t.Fatal("multi-char key must not be optimized")
266 }
267 // A single-char key that is neither e nor p is never optimized.
268 tj := New()
269 _, tjErr := tj.Unmarshal([]byte("[\"t\",\"") | hx | []byte("\"]"))
270 if tjErr != nil {
271 t.Fatal(tjErr)
272 return
273 }
274 if isBinaryEncoded(tj.Value()) {
275 t.Fatal("t tag must not be optimized")
276 }
277 // A value of the wrong length is never optimized.
278 sj := New()
279 _, sjErr := sj.Unmarshal([]byte("[\"e\",\"short\"]"))
280 if sjErr != nil {
281 t.Fatal(sjErr)
282 return
283 }
284 if isBinaryEncoded(sj.Value()) {
285 t.Fatal("short e value must not be optimized")
286 }
287 // A 64-byte value with a non-hex character is stored raw.
288 badHex := []byte("z" | zeroHex64[1:])
289 bj := New()
290 _, bjErr := bj.Unmarshal([]byte("[\"e\",\"") | badHex | []byte("\"]"))
291 if bjErr != nil {
292 t.Fatal(bjErr)
293 return
294 }
295 if isBinaryEncoded(bj.Value()) {
296 t.Fatal("non-hex 64-byte value must not be optimized")
297 }
298 }
299
300 func TestUnmarshalTagMalformed(t *testing.T) {
301 uq := New()
302 if _, uqErr := uq.Unmarshal([]byte("[\"t")); uqErr == nil {
303 t.Fatal("unterminated quote should error")
304 }
305 // A closing bracket with no opening one used to be accepted: the ']'
306 // early-return skipped the !openedBracket guard at the end of Unmarshal.
307 qonly := New()
308 if _, qonlyErr := qonly.Unmarshal([]byte("\"t\",\"v\"]")); qonlyErr == nil {
309 t.Fatal("closing bracket without an opening one should error")
310 }
311 nb := New()
312 if _, nbErr := nb.Unmarshal([]byte("\"t\",\"v\"")); nbErr == nil {
313 t.Fatal("input with no brackets should error")
314 }
315 jt := New()
316 if jtErr := jt.UnmarshalJSON([]byte("[\"t")); jtErr == nil {
317 t.Fatal("UnmarshalJSON should propagate parse errors")
318 }
319 jok := New()
320 if jokErr := jok.UnmarshalJSON([]byte("[\"a\",\"b\"]")); jokErr != nil {
321 t.Fatal(jokErr)
322 return
323 }
324 if !bytes.Equal(jok.Value(), []byte("b")) {
325 t.Fatalf("UnmarshalJSON Value = %s", jok.Value())
326 }
327 }
328
329 func TestParseHelpers(t *testing.T) {
330 if !isBinaryOptimizedTag('e') {
331 t.Fatal("e should be binary-optimized")
332 }
333 if !isBinaryOptimizedTag('p') {
334 t.Fatal("p should be binary-optimized")
335 }
336 if isBinaryOptimizedTag('t') {
337 t.Fatal("t should not be binary-optimized")
338 }
339 if !isValidHex([]byte("0123456789abcdefABCDEF")) {
340 t.Fatal("valid hex rejected")
341 }
342 var noHex []byte
343 if !isValidHex(noHex) {
344 t.Fatal("empty input is valid hex")
345 }
346 if isValidHex([]byte("xyz")) {
347 t.Fatal("invalid hex accepted")
348 }
349 shortVal := []byte("short")
350 if isBinaryEncoded(shortVal) {
351 t.Fatal("short value should not be binary-encoded")
352 }
353 exact := []byte{:33}
354 if !isBinaryEncoded(exact) {
355 t.Fatal("33-byte zero-tailed value should be binary-encoded")
356 }
357 exact[32] = 1
358 if isBinaryEncoded(exact) {
359 t.Fatal("33-byte value with non-zero tail should not be binary-encoded")
360 }
361 zeroHex := []byte(zeroHex64)
362 badHex := []byte("z" | zeroHex64[1:])
363 if shouldOptimize([]byte("ab"), zeroHex) {
364 t.Fatal("multi-char key should not optimize")
365 }
366 if shouldOptimize([]byte("t"), zeroHex) {
367 t.Fatal("non-e/p key should not optimize")
368 }
369 if shouldOptimize([]byte("e"), shortVal) {
370 t.Fatal("short value should not optimize")
371 }
372 if !shouldOptimize([]byte("e"), zeroHex) {
373 t.Fatal("64-hex e value should optimize")
374 }
375 if shouldOptimize([]byte("e"), badHex) {
376 t.Fatal("non-hex value should not optimize")
377 }
378 }
379
380 func TestFieldHelpers(t *testing.T) {
381 fv := tagFieldValue([]byte("a\\nb"), 0, 4)
382 if !bytes.Equal(fv, []byte("a\nb")) {
383 t.Fatalf("tagFieldValue = %s", fv)
384 }
385 sub := tagFieldValue([]byte("xxyy"), 2, 4)
386 if !bytes.Equal(sub, []byte("yy")) {
387 t.Fatalf("tagFieldValue slice = %s", sub)
388 }
389 bin, ok := tagFieldBinary([]byte(zeroHex64))
390 if !ok || len(bin) != BinaryEncodedLen {
391 t.Fatalf("tagFieldBinary ok=%v len=%d", ok, len(bin))
392 }
393 if bin[HashLen] != 0 {
394 t.Fatalf("tagFieldBinary tail = %d, want 0", bin[HashLen])
395 }
396 bad, badOk := tagFieldBinary([]byte("xyz"))
397 if badOk || bad != nil {
398 t.Fatal("tagFieldBinary should reject non-hex input")
399 }
400 }
401
402 func TestParseTags(t *testing.T) {
403 tsA, remA, errA := parseTags([]byte(",[[\"a\",\"b\"]]"))
404 if errA != nil {
405 t.Fatal(errA)
406 return
407 }
408 if len(tsA) != 1 || len(remA) != 0 {
409 t.Fatalf("leading comma: tags=%d rem=%s", len(tsA), string(remA))
410 }
411 tsB, _, errB := parseTags([]byte("x[[\"a\",\"b\"]]"))
412 if errB != nil {
413 t.Fatal(errB)
414 return
415 }
416 if len(tsB) != 1 {
417 t.Fatalf("leading junk: tags=%d", len(tsB))
418 }
419 tsC, remC, errC := parseTags([]byte("]junk"))
420 if errC != nil {
421 t.Fatal(errC)
422 return
423 }
424 if len(tsC) != 0 || string(remC) != "junk" {
425 t.Fatalf("leading close: tags=%d rem=%s", len(tsC), string(remC))
426 }
427 tsD, _, errD := parseTags([]byte("[[\"a\",\"b\"] junk]"))
428 if errD != nil {
429 t.Fatal(errD)
430 return
431 }
432 if len(tsD) != 1 {
433 t.Fatalf("inner junk: tags=%d", len(tsD))
434 }
435 tsE, _, errE := parseTags([]byte("[[\"a\",\"b\"]"))
436 if errE != nil {
437 t.Fatal(errE)
438 return
439 }
440 if len(tsE) != 1 {
441 t.Fatalf("missing outer close: tags=%d", len(tsE))
442 }
443 tsF, _, errF := parseTags([]byte("[[\"t"))
444 if errF == nil {
445 t.Fatalf("unterminated tag should error, got tags=%d", len(tsF))
446 }
447 }
448
449 func TestValueHexBinary(t *testing.T) {
450 var nt *T
451 if nt.ValueHex() != nil {
452 t.Fatal("nil ValueHex should be nil")
453 }
454 if nt.ValueBinary() != nil {
455 t.Fatal("nil ValueBinary should be nil")
456 }
457 one := NewFromBytesSlice([]byte("k"))
458 if one.ValueHex() != nil {
459 t.Fatal("one-field ValueHex should be nil")
460 }
461 if one.ValueBinary() != nil {
462 t.Fatal("one-field ValueBinary should be nil")
463 }
464 plain := NewFromBytesSlice([]byte("k"), []byte("plain"))
465 if !bytes.Equal(plain.ValueHex(), []byte("plain")) {
466 t.Fatalf("plain ValueHex = %s", plain.ValueHex())
467 }
468 if plain.ValueBinary() != nil {
469 t.Fatal("plain ValueBinary should be nil")
470 }
471 bin := []byte{:33}
472 bin[0] = 0x5a
473 bin[1] = 0xa5
474 bt := NewFromBytesSlice([]byte("e"), bin)
475 hx := hex.EncAppend(nil, bin[:HashLen])
476 if !bytes.Equal(bt.ValueHex(), hx) {
477 t.Fatalf("binary ValueHex = %s, want %s", bt.ValueHex(), hx)
478 }
479 if !bytes.Equal(bt.ValueBinary(), bin[:HashLen]) {
480 t.Fatalf("binary ValueBinary = %s, want %s", bt.ValueBinary(), bin[:HashLen])
481 }
482 }
483
484 func TestNewFromAny(t *testing.T) {
485 tt := NewFromAny("a", "b", "c")
486 if tt.Len() != 3 {
487 t.Fatalf("Len = %d, want 3", tt.Len())
488 }
489 if !bytes.Equal(tt.Key(), []byte("a")) {
490 t.Fatalf("Key = %s", tt.Key())
491 }
492 if !bytes.Equal(tt.Value(), []byte("b")) {
493 t.Fatalf("Value = %s", tt.Value())
494 }
495 if !bytes.Equal(tt.Relay(), []byte("c")) {
496 t.Fatalf("Relay = %s", tt.Relay())
497 }
498 if NewFromAny().Len() != 0 {
499 t.Fatal("NewFromAny() should be empty")
500 }
501 }
502
503 func TestEquals(t *testing.T) {
504 var na, nb *T
505 if !na.Equals(nb) {
506 t.Fatal("nil.Equals(nil) should be true")
507 }
508 a1 := NewFromBytesSlice([]byte("k"))
509 if a1.Equals(nb) {
510 t.Fatal("tag.Equals(nil) should be false")
511 }
512 if na.Equals(a1) {
513 t.Fatal("nil.Equals(tag) should be false")
514 }
515 a2 := NewFromBytesSlice([]byte("k"), []byte("v"))
516 a3 := NewFromBytesSlice([]byte("k"))
517 if a2.Equals(a3) {
518 t.Fatal("different lengths should not be equal")
519 }
520 a4 := NewFromBytesSlice([]byte("k"), []byte("v"))
521 if !a2.Equals(a4) {
522 t.Fatal("plain equal tags should be equal")
523 }
524 a5 := NewFromBytesSlice([]byte("k"), []byte("w"))
525 if a2.Equals(a5) {
526 t.Fatal("different plain values should not be equal")
527 }
528 a6 := NewFromBytesSlice([]byte("z"), []byte("v"))
529 if a2.Equals(a6) {
530 t.Fatal("different keys should not be equal")
531 }
532
533 binA := []byte{:33}
534 binA[0] = 0x5a
535 binA[1] = 0xa5
536 binB := []byte{:33}
537 binB[0] = 0x5a
538 binB[1] = 0xa5
539 binC := []byte{:33}
540 binC[0] = 0x5a
541 binC[1] = 0xa5
542 binC[2] = 1
543 tagA := NewFromBytesSlice([]byte("e"), binA)
544 tagB := NewFromBytesSlice([]byte("e"), binB)
545 tagC := NewFromBytesSlice([]byte("e"), binC)
546 if !tagA.Equals(tagB) {
547 t.Fatal("identical binary tags should be equal")
548 }
549 if tagA.Equals(tagC) {
550 t.Fatal("differing binary tags should not be equal")
551 }
552
553 hx := hex.EncAppend(nil, binA[:HashLen])
554 hexTag := NewFromBytesSlice([]byte("e"), hx)
555 if !tagA.Equals(hexTag) {
556 t.Fatal("binary should equal its lowercase hex form")
557 }
558 if !hexTag.Equals(tagA) {
559 t.Fatal("lowercase hex should equal its binary form")
560 }
561 up := upperHex(binA[:HashLen])
562 upTag := NewFromBytesSlice([]byte("e"), up)
563 if !tagA.Equals(upTag) {
564 t.Fatal("binary should equal its uppercase hex form")
565 }
566
567 wrongHex := NewFromBytesSlice([]byte("e"), []byte(zeroHex64))
568 if tagA.Equals(wrongHex) {
569 t.Fatal("binary vs differing valid hex should not be equal")
570 }
571 shortRaw := NewFromBytesSlice([]byte("e"), []byte("short"))
572 if tagA.Equals(shortRaw) {
573 t.Fatal("binary vs short raw should not be equal")
574 }
575 badHi := NewFromBytesSlice([]byte("e"), []byte("z" | zeroHex64[1:]))
576 if tagA.Equals(badHi) {
577 t.Fatal("invalid high nibble should not be equal")
578 }
579 badLo := NewFromBytesSlice([]byte("e"), []byte(zeroHex64[:1] | "z" | zeroHex64[2:]))
580 if tagA.Equals(badLo) {
581 t.Fatal("invalid low nibble should not be equal")
582 }
583 }
584
585 func TestSAppendLenLessSwap(t *testing.T) {
586 s := NewS()
587 if s.Len() != 0 {
588 t.Fatalf("empty S Len = %d", s.Len())
589 }
590 ta := NewFromBytesSlice([]byte("a"))
591 tb := NewFromBytesSlice([]byte("b"))
592 s.Append(ta)
593 s.Append(tb)
594 if s.Len() != 2 {
595 t.Fatalf("S Len = %d, want 2", s.Len())
596 }
597 if !s.Less(0, 1) {
598 t.Fatal("S.Less(0,1) should be true")
599 }
600 if s.Less(1, 0) {
601 t.Fatal("S.Less(1,0) should be false")
602 }
603 s.Swap(0, 1)
604 if !bytes.Equal(s.T[0].T[0], []byte("b")) {
605 t.Fatalf("S.Swap[0] = %s", s.T[0].T[0])
606 }
607 if !bytes.Equal(s.T[1].T[0], []byte("a")) {
608 t.Fatalf("S.Swap[1] = %s", s.T[1].T[0])
609 }
610 var ns *S
611 if ns.Len() != 0 {
612 t.Fatalf("nil S Len = %d", ns.Len())
613 }
614 capped := NewSWithCap(4)
615 if capped.Len() != 0 {
616 t.Fatalf("NewSWithCap Len = %d", capped.Len())
617 }
618 }
619
620 func TestSContainsAny(t *testing.T) {
621 bin := []byte{:33}
622 bin[0] = 0x5a
623 bin[1] = 0xa5
624 eTag := NewFromBytesSlice([]byte("e"), bin)
625 tTag := NewFromBytesSlice([]byte("t"), []byte("test"))
626 short := NewFromBytesSlice([]byte("x"))
627 s := NewS(eTag, tTag, short)
628 if !s.ContainsAny([]byte("t"), [][]byte{[]byte("te")}) {
629 t.Fatal("prefix of t value should match")
630 }
631 if s.ContainsAny([]byte("t"), [][]byte{[]byte("zz")}) {
632 t.Fatal("absent prefix should not match")
633 }
634 hx := hex.EncAppend(nil, bin[:HashLen])
635 if !s.ContainsAny([]byte("e"), [][]byte{hx[:4]}) {
636 t.Fatal("hex prefix of binary e value should match")
637 }
638 if s.ContainsAny([]byte("e"), [][]byte{[]byte("ff")}) {
639 t.Fatal("absent hex prefix should not match")
640 }
641 if s.ContainsAny([]byte("x"), [][]byte{[]byte("")}) {
642 t.Fatal("one-field tag should be skipped")
643 }
644 if s.ContainsAny([]byte("q"), [][]byte{[]byte("")}) {
645 t.Fatal("unknown tag should not match")
646 }
647 var noName []byte
648 if s.ContainsAny(noName, [][]byte{[]byte("a")}) {
649 t.Fatal("empty tag name should not match")
650 }
651 var ns *S
652 if ns.ContainsAny([]byte("t"), [][]byte{[]byte("t")}) {
653 t.Fatal("nil S should not match")
654 }
655 }
656
657 func TestSGetFirstGetAll(t *testing.T) {
658 var ns *S
659 if ns.GetFirst([]byte("a")) != nil {
660 t.Fatal("nil S GetFirst should be nil")
661 }
662 if ns.GetAll([]byte("a")) != nil {
663 t.Fatal("nil S GetAll should be nil")
664 }
665 es := NewS()
666 if es.GetFirst([]byte("a")) != nil {
667 t.Fatal("empty S GetFirst should be nil")
668 }
669 if es.GetAll([]byte("a")) != nil {
670 t.Fatal("empty S GetAll should be nil")
671 }
672 emptyTag := New()
673 a1 := NewFromBytesSlice([]byte("a"), []byte("1"))
674 a2 := NewFromBytesSlice([]byte("a"), []byte("2"))
675 b1 := NewFromBytesSlice([]byte("b"), []byte("1"))
676 s := NewS(emptyTag, a1, b1, a2)
677 f := s.GetFirst([]byte("a"))
678 if f == nil || !bytes.Equal(f.Value(), []byte("1")) {
679 t.Fatal("GetFirst should return the first a tag")
680 }
681 if s.GetFirst([]byte("z")) != nil {
682 t.Fatal("GetFirst of absent key should be nil")
683 }
684 all := s.GetAll([]byte("a"))
685 if len(all) != 2 {
686 t.Fatalf("GetAll len = %d, want 2", len(all))
687 }
688 if !bytes.Equal(all[0].Value(), []byte("1")) || !bytes.Equal(all[1].Value(), []byte("2")) {
689 t.Fatal("GetAll should preserve order")
690 }
691 if len(s.GetAll([]byte("z"))) != 0 {
692 t.Fatal("GetAll of absent key should be empty")
693 }
694 // The zero-length tag must be skipped by both accessors.
695 if s.GetFirst([]byte("")) != nil {
696 t.Fatal("GetFirst should skip zero-length tags")
697 }
698 if len(s.GetAll([]byte(""))) != 0 {
699 t.Fatal("GetAll should skip zero-length tags")
700 }
701 // More matches than the initial capacity forces mxutil.Ensure to grow.
702 many := NewS()
703 for i := 0; i < 6; i++ {
704 many.Append(NewFromBytesSlice([]byte("m"), []byte("v")))
705 }
706 mall := many.GetAll([]byte("m"))
707 if len(mall) != 6 {
708 t.Fatalf("GetAll growth len = %d, want 6", len(mall))
709 }
710 }
711
712 func TestSGetTagElement(t *testing.T) {
713 var ns *S
714 if ns.GetTagElement(0) != nil {
715 t.Fatal("nil S GetTagElement should be nil")
716 }
717 a1 := NewFromBytesSlice([]byte("a"), []byte("1"))
718 a2 := NewFromBytesSlice([]byte("b"), []byte("2"))
719 s := NewS(a1, a2)
720 if s.GetTagElement(0) != a1 {
721 t.Fatal("GetTagElement(0) should be the first tag")
722 }
723 if s.GetTagElement(1) != a2 {
724 t.Fatal("GetTagElement(1) should be the second tag")
725 }
726 if s.GetTagElement(5) != nil {
727 t.Fatal("out-of-range GetTagElement should be nil")
728 }
729 // The bound used to be `len(s.T) < i`, so i == Len() indexed one past the
730 // end and panicked instead of returning nil.
731 if s.GetTagElement(s.Len()) != nil {
732 t.Fatal("GetTagElement(Len()) should be nil, not a panic")
733 }
734 if s.GetTagElement(-1) != nil {
735 t.Fatal("negative GetTagElement should be nil")
736 }
737 }
738
739 func TestSMarshal(t *testing.T) {
740 s := NewS(
741 NewFromBytesSlice([]byte("t"), []byte("test")),
742 NewFromBytesSlice([]byte("p"), []byte("abc")),
743 )
744 want := "[[\"t\",\"test\"],[\"p\",\"abc\"]]"
745 got := string(s.Marshal(nil))
746 if got != want {
747 t.Fatalf("S.Marshal = %s, want %s", got, want)
748 }
749 gotDst := string(s.Marshal([]byte("x")))
750 if gotDst != ("x" | want) {
751 t.Fatalf("S.Marshal into dst = %s", gotDst)
752 }
753 if string(NewS().Marshal(nil)) != "[]" {
754 t.Fatalf("empty S.Marshal = %s", string(NewS().Marshal(nil)))
755 }
756 var ns *S
757 if ns.Marshal(nil) != nil {
758 t.Fatal("nil S.Marshal should return nil")
759 }
760 }
761
762 func TestSMarshalJSON(t *testing.T) {
763 s1 := NewS(NewFromBytesSlice([]byte("t"), []byte("test")))
764 jb, jerr := s1.MarshalJSON()
765 if jerr != nil {
766 t.Fatal(jerr)
767 return
768 }
769 if string(jb) != "[[\"t\",\"test\"]]" {
770 t.Fatalf("S.MarshalJSON = %s", string(jb))
771 }
772 s2 := NewS(
773 NewFromBytesSlice([]byte("a"), []byte("1")),
774 NewFromBytesSlice([]byte("b"), []byte("2")),
775 )
776 jb2, jerr2 := s2.MarshalJSON()
777 if jerr2 != nil {
778 t.Fatal(jerr2)
779 return
780 }
781 if string(jb2) != "[[\"a\",\"1\"],[\"b\",\"2\"]]" {
782 t.Fatalf("S.MarshalJSON two = %s", string(jb2))
783 }
784 }
785
786 func TestSUnmarshal(t *testing.T) {
787 raw := []byte("[[\"t\",\"test\"],[\"p\",\"abc\"]]")
788 s := NewS()
789 rem, err := s.Unmarshal(raw)
790 if err != nil {
791 t.Fatal(err)
792 return
793 }
794 if s.Len() != 2 {
795 t.Fatalf("S.Len = %d, want 2", s.Len())
796 }
797 if len(rem) != 0 {
798 t.Fatalf("S.Unmarshal remainder = %s", string(rem))
799 }
800 if !bytes.Equal(s.T[0].Value(), []byte("test")) {
801 t.Fatalf("first value = %s", s.T[0].Value())
802 }
803 if !bytes.Equal(s.T[1].Key(), []byte("p")) {
804 t.Fatalf("second key = %s", s.T[1].Key())
805 }
806 sj := NewS()
807 if sjErr := sj.UnmarshalJSON(raw); sjErr != nil {
808 t.Fatal(sjErr)
809 return
810 }
811 if sj.Len() != 2 {
812 t.Fatalf("UnmarshalJSON Len = %d", sj.Len())
813 }
814 sr := NewS()
815 remR, errR := sr.Unmarshal([]byte("[[\"a\",\"b\"]]tail"))
816 if errR != nil {
817 t.Fatal(errR)
818 return
819 }
820 if string(remR) != "tail" {
821 t.Fatalf("S.Unmarshal remainder = %s, want tail", string(remR))
822 }
823 se := NewS()
824 _, errE := se.Unmarshal([]byte("[]"))
825 if errE != nil {
826 t.Fatal(errE)
827 return
828 }
829 if se.Len() != 0 {
830 t.Fatalf("empty array Len = %d", se.Len())
831 }
832 sm := NewS()
833 if _, errM := sm.Unmarshal([]byte("[[\"t")); errM == nil {
834 t.Fatal("unterminated tag should error")
835 }
836 smj := NewS()
837 if errMJ := smj.UnmarshalJSON([]byte("[[\"t")); errMJ == nil {
838 t.Fatal("UnmarshalJSON should propagate parse errors")
839 }
840 }
841