tag_test.mx raw

   1  package tag
   2  
   3  import (
   4  	"bytes"
   5  	"testing"
   6  
   7  	"git.smesh.lol/smesh/pkg/nostr/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