varint_test.mx raw

   1  package marmot
   2  
   3  // QUIC varint codec tests (RFC 9000 §16, Appendix A).
   4  //
   5  // The critical test in this file is TestDecodeVarint8ByteAbove2p31 — an
   6  // 8-byte-encoded value above 2^31 must survive decoding as uint64 without
   7  // silent truncation. This would regress if anyone typed the API as uint
   8  // or int, since Moxie's uint/int are 32-bit.
   9  
  10  import "testing"
  11  
  12  // roundTrip encodes v, decodes it back, and checks size class + value.
  13  func roundTrip(t *testing.T, v uint64, wantN int) {
  14  	var buf [8]byte
  15  	n, err := EncodeVarint(v, buf[:])
  16  	if err != nil {
  17  		t.Fatalf("EncodeVarint(%d): %s", int64(v), err.Error())
  18  	}
  19  	if n != wantN {
  20  		t.Errorf("EncodeVarint(%d): wrote %d bytes, want %d", int64(v), n, wantN)
  21  	}
  22  	got, gotN, err := DecodeVarint(buf[:n])
  23  	if err != nil {
  24  		t.Fatalf("DecodeVarint(%d bytes): %s", n, err.Error())
  25  	}
  26  	if gotN != n {
  27  		t.Errorf("DecodeVarint(%d): consumed %d bytes, want %d", int64(v), gotN, n)
  28  	}
  29  	if got != v {
  30  		t.Errorf("DecodeVarint round-trip: got %d, want %d", int64(got), int64(v))
  31  	}
  32  }
  33  
  34  func TestVarintSizeClasses(t *testing.T) {
  35  	// 1-byte class: 0..63
  36  	roundTrip(t, 0, 1)
  37  	roundTrip(t, 63, 1)
  38  	// 2-byte class: 64..16383
  39  	roundTrip(t, 64, 2)
  40  	roundTrip(t, 16383, 2)
  41  	// 4-byte class: 16384..1073741823 (2^30-1)
  42  	roundTrip(t, 16384, 4)
  43  	roundTrip(t, 1073741823, 4)
  44  	// 8-byte class: 1073741824..VarintMax
  45  	roundTrip(t, 1073741824, 8)
  46  }
  47  
  48  // TestDecodeVarint8ByteAbove2p31 is the regression guard for the whole
  49  // uint64-in-the-API rule. An 8-byte varint encoding a value > 2^31 must
  50  // decode to the correct uint64. If someone refactored this to return uint
  51  // (32-bit in Moxie), the value would silently truncate to its low 32 bits
  52  // and every caller would get wrong data with no error.
  53  func TestDecodeVarint8ByteAbove2p31(t *testing.T) {
  54  	// 2^31 exactly (first value that overflows signed int32).
  55  	v := uint64(0x80000000)
  56  	roundTrip(t, v, 8)
  57  
  58  	// 2^31 + 1 (first value that overflows unsigned int32 if signed is used).
  59  	roundTrip(t, uint64(0x80000001), 8)
  60  
  61  	// 2^32 (first value above uint32 range — critical test).
  62  	roundTrip(t, uint64(0x100000000), 8)
  63  
  64  	// 2^40 — solidly in the upper range.
  65  	roundTrip(t, uint64(0x10000000000), 8)
  66  
  67  	// VarintMax (2^62-1) — largest legal QUIC varint.
  68  	roundTrip(t, VarintMax, 8)
  69  }
  70  
  71  func TestEncodeVarintOverflow(t *testing.T) {
  72  	// Values above VarintMax must be rejected.
  73  	var buf [8]byte
  74  	_, err := EncodeVarint(VarintMax+1, buf[:])
  75  	if err == nil {
  76  		t.Error("EncodeVarint(VarintMax+1) should error")
  77  	}
  78  	_, err = EncodeVarint(uint64(0xffffffffffffffff), buf[:])
  79  	if err == nil {
  80  		t.Error("EncodeVarint(max uint64) should error")
  81  	}
  82  }
  83  
  84  func TestEncodeVarintBufferTooSmall(t *testing.T) {
  85  	// 8-byte value into 4-byte buffer.
  86  	var small [4]byte
  87  	_, err := EncodeVarint(uint64(0x100000000), small[:])
  88  	if err == nil {
  89  		t.Error("EncodeVarint into small buffer should error")
  90  	}
  91  	// 4-byte value into 2-byte buffer.
  92  	_, err = EncodeVarint(16384, small[:1])
  93  	if err == nil {
  94  		t.Error("EncodeVarint into 1-byte buffer should error")
  95  	}
  96  }
  97  
  98  func TestDecodeVarintTruncated(t *testing.T) {
  99  	// Advertise 8-byte class but supply 3 bytes.
 100  	_, _, err := DecodeVarint([]byte{0xc0, 0x00, 0x00})
 101  	if err == nil {
 102  		t.Error("DecodeVarint(truncated 8-byte) should error")
 103  	}
 104  	// Advertise 4-byte class but supply 2 bytes.
 105  	_, _, err = DecodeVarint([]byte{0x80, 0x00})
 106  	if err == nil {
 107  		t.Error("DecodeVarint(truncated 4-byte) should error")
 108  	}
 109  	// Advertise 2-byte class but supply 1 byte.
 110  	_, _, err = DecodeVarint([]byte{0x40})
 111  	if err == nil {
 112  		t.Error("DecodeVarint(truncated 2-byte) should error")
 113  	}
 114  	// Empty.
 115  	_, _, err = DecodeVarint(nil)
 116  	if err == nil {
 117  		t.Error("DecodeVarint(nil) should error")
 118  	}
 119  }
 120  
 121  // readQuicVec narrows uint64 to int at the call site. A varint length above
 122  // int32 range must be rejected with an error, not silently truncated.
 123  func TestReadQuicVecRejectsOversizedLength(t *testing.T) {
 124  	// Build a QUIC varint encoding 2^32 (well above int32 max).
 125  	var buf [8]byte
 126  	n, _ := EncodeVarint(uint64(0x100000000), buf[:])
 127  	_, _, err := readQuicVec(buf[:n])
 128  	if err == nil {
 129  		t.Error("readQuicVec(length=2^32) should error — cannot fit in int32")
 130  	}
 131  }
 132