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