v0.7.0 4c16dd8

scan_test.go RawHistoryFolder

1package wire
2
3import (
4	"errors"
5	"math"
6	"testing"
7
8	"google.golang.org/protobuf/encoding/protowire"
9	"google.golang.org/protobuf/proto"
10)
11
12// TestScanEnvelopeTable covers every scanner family: valid envelopes,
13// unknown fields, wrong wire types, duplicates, truncation, trailing
14// garbage, oversize lengths, and repeated-element abuse.
15func TestScanEnvelopeTable(t *testing.T) {
16	mustMarshal := func(message proto.Message) []byte {
17		raw, err := proto.MarshalOptions{Deterministic: true}.Marshal(message)
18		if err != nil {
19			t.Fatal(err)
20		}
21		return raw
22	}
23	ping := mustMarshal(&ClientEnvelope{Payload: &ClientEnvelope_Ping{Ping: &Ping{}}})
24
25	input := mustMarshal(&ClientEnvelope{Payload: &ClientEnvelope_Input{Input: &Input{InputSeq: 1, Data: []byte("x")}}})
26	snapshot := mustMarshal(&ServerEnvelope{Payload: &ServerEnvelope_PickerSnapshot{PickerSnapshot: &PickerSnapshot{
27		InteractionId: 1, SourceId: "serving", SourceRevision: 1, Status: 1,
28		Recent: &PickerProjection{}, Grouped: &PickerProjection{},
29	}}})
30
31	// Wrong wire type: Hello.version (field 1, varint) as fixed32.
32	wrongType := []byte{0x0D, 0x01, 0x00, 0x00, 0x00}
33	// Truncated varint tag.
34	truncatedTag := []byte{0xFF}
35	// Length-delimited field claiming more than available.
36	overlong := protowire.AppendTag(nil, 2, protowire.BytesType)
37	overlong = protowire.AppendBytes(overlong, make([]byte, 8))
38	overlong = overlong[:len(overlong)-4]
39
40	tests := []struct {
41		name    string
42		message proto.Message
43		payload []byte
44		wantErr error
45	}{
46		{name: "valid ping", message: &ClientEnvelope{}, payload: ping},
47		{name: "valid input", message: &ClientEnvelope{}, payload: input},
48		{name: "valid snapshot", message: &ServerEnvelope{}, payload: snapshot},
49		{name: "nil message", message: nil, payload: ping, wantErr: ErrScanNotMessage},
50		{name: "empty payload", message: &ClientEnvelope{}, payload: nil, wantErr: ErrScanEmpty},
51		{name: "empty envelope", message: &ClientEnvelope{}, payload: []byte{}, wantErr: ErrScanEmpty},
52		{name: "truncated", message: &ClientEnvelope{}, payload: ping[:len(ping)-1], wantErr: ErrScanTruncated},
53		{name: "truncated tag", message: &ClientEnvelope{}, payload: truncatedTag, wantErr: ErrScanTruncated},
54		{name: "concatenated envelopes", message: &ClientEnvelope{}, payload: append(append([]byte(nil), ping...), ping...), wantErr: ErrScanDuplicate},
55		{name: "wrong wire type", message: &ClientEnvelope{}, payload: wrongType, wantErr: ErrScanWireType},
56		{name: "overlong bytes", message: &ClientEnvelope{}, payload: overlong, wantErr: ErrScanTruncated},
57		{name: "duplicate variant", message: &ClientEnvelope{}, payload: append(append([]byte(nil), ping...), ping...), wantErr: ErrScanDuplicate},
58		{name: "oversize envelope", message: &ClientEnvelope{}, payload: make([]byte, AbsoluteEnvelopeLimit+1), wantErr: ErrScanLength},
59	}
60	for _, tt := range tests {
61		t.Run(tt.name, func(t *testing.T) {
62			err := ScanEnvelope(tt.message, tt.payload)
63			if tt.wantErr == nil {
64				if err != nil {
65					t.Fatalf("ScanEnvelope() = %v, want nil", err)
66				}
67				return
68			}
69			if !errors.Is(err, tt.wantErr) {
70				t.Fatalf("ScanEnvelope() = %v, want %v", err, tt.wantErr)
71			}
72		})
73	}
74}
75
76// TestScanEnvelopeUnknownField appends an unknown varint field to a valid
77// envelope and requires ErrScanUnknown.
78func TestScanEnvelopeUnknownField(t *testing.T) {
79	valid, err := proto.Marshal(&ClientEnvelope{Payload: &ClientEnvelope_Ping{Ping: &Ping{}}})
80	if err != nil {
81		t.Fatal(err)
82	}
83	unknown := append([]byte(nil), valid...)
84	unknown = protowire.AppendTag(unknown, 99, protowire.VarintType)
85	unknown = protowire.AppendVarint(unknown, 1)
86	if err := ScanEnvelope(&ClientEnvelope{}, unknown); !errors.Is(err, ErrScanUnknown) {
87		t.Fatalf("ScanEnvelope() = %v, want ErrScanUnknown", err)
88	}
89}
90
91// TestScanEnvelopeRejectsUint32Overflow proves a raw varint wider than the
92// declared uint32 field is rejected before generated unmarshal can truncate
93// its high bits into a legitimate value.
94func TestScanEnvelopeRejectsUint32Overflow(t *testing.T) {
95	hello := protowire.AppendTag(nil, 1, protowire.VarintType) // Hello.version
96	hello = protowire.AppendVarint(hello, math.MaxUint32+1)
97	raw := protowire.AppendTag(nil, 1, protowire.BytesType) // ClientEnvelope.hello
98	raw = protowire.AppendBytes(raw, hello)
99	if err := ScanEnvelope(&ClientEnvelope{}, raw); !errors.Is(err, ErrScanVarintRange) {
100		t.Fatalf("ScanEnvelope() = %v, want ErrScanVarintRange", err)
101	}
102}
103
104// TestScanEnvelopeAllowsSignExtendedInt32 proves a canonical negative int32
105// (ten-byte sign-extended varint) still passes: it is a real value, not a
106// truncating overflow.
107func TestScanEnvelopeAllowsSignExtendedInt32(t *testing.T) {
108	for _, value := range []uint64{math.MaxUint64, math.MaxUint64 - uint64(math.MaxInt32)} {
109		style := protowire.AppendTag(nil, 5, protowire.VarintType) // CellStyle.foreground
110		style = protowire.AppendVarint(style, value)
111		if err := ScanEnvelope(&CellStyle{}, style); err != nil {
112			t.Fatalf("ScanEnvelope(value %d) = %v, want nil", value, err)
113		}
114	}
115}
116
117// TestScanEnvelopeRejectsBoolAboveOne proves the strict scanner refuses the
118// non-canonical bool encodings that generated unmarshal would coerce to true.
119func TestScanEnvelopeRejectsBoolAboveOne(t *testing.T) {
120	accepted := protowire.AppendTag(nil, 8, protowire.VarintType) // PreambleResponse.accepted
121	accepted = protowire.AppendVarint(accepted, 2)
122	if err := ScanEnvelope(&PreambleResponse{}, accepted); !errors.Is(err, ErrScanVarintRange) {
123		t.Fatalf("ScanEnvelope() = %v, want ErrScanVarintRange", err)
124	}
125}
126
127// TestScanEnvelopeRejectsInt32Overflow proves a varint that truncates to a
128// different int32 is rejected while canonical sign-extended negatives pass.
129func TestScanEnvelopeRejectsInt32Overflow(t *testing.T) {
130	for _, value := range []uint64{math.MaxUint32, 1 << 32} {
131		style := protowire.AppendTag(nil, 5, protowire.VarintType) // CellStyle.foreground
132		style = protowire.AppendVarint(style, value)
133		if err := ScanEnvelope(&CellStyle{}, style); !errors.Is(err, ErrScanVarintRange) {
134			t.Fatalf("ScanEnvelope(value %d) = %v, want ErrScanVarintRange", value, err)
135		}
136	}
137}
138
139// TestScanEnvelopeRepeatedElements fills a flat repeated field past the
140// scanner budget with minimal one-byte elements.
141func TestScanEnvelopeRepeatedElements(t *testing.T) {
142	hello := &Hello{Version: 1, Intent: 1}
143	for range maxScanRepeated + 1 {
144		hello.Env = append(hello.Env, "x")
145	}
146	raw, err := proto.Marshal(&ClientEnvelope{Payload: &ClientEnvelope_Hello{Hello: hello}})
147	if err != nil {
148		t.Fatal(err)
149	}
150	if err := ScanEnvelope(&ClientEnvelope{}, raw); !errors.Is(err, ErrScanRepeated) {
151		t.Fatalf("ScanEnvelope() = %v, want ErrScanRepeated", err)
152	}
153}
154
155// TestValidatedUnmarshalRoundTrip proves scan + unmarshal accept a valid
156// envelope and reject a mutated one.
157func TestValidatedUnmarshalRoundTrip(t *testing.T) {
158	raw, err := proto.Marshal(&ClientEnvelope{Payload: &ClientEnvelope_Ack{Ack: &Ack{Epoch: 1, State: 2}}})
159	if err != nil {
160		t.Fatal(err)
161	}
162	decoded := &ClientEnvelope{}
163	if err := validatedUnmarshal(decoded, raw); err != nil {
164		t.Fatalf("validatedUnmarshal() = %v", err)
165	}
166	bad := append([]byte(nil), raw...)
167	bad[len(bad)-1] ^= 0xFF
168	if err := validatedUnmarshal(&ClientEnvelope{}, bad); err == nil {
169		t.Fatal("validatedUnmarshal accepted mutated payload")
170	}
171	for size := range len(raw) {
172		decoded := &ClientEnvelope{}
173		if err := validatedUnmarshal(decoded, raw[:size]); err == nil {
174			t.Fatalf("validatedUnmarshal accepted prefix %d", size)
175		}
176	}
177	pong, err := proto.Marshal(&ClientEnvelope{Payload: &ClientEnvelope_Ping{Ping: &Ping{}}})
178	if err != nil {
179		t.Fatal(err)
180	}
181	if err := validatedUnmarshal(&ClientEnvelope{}, append(append([]byte(nil), raw...), pong...)); err == nil {
182		t.Fatal("validatedUnmarshal accepted trailing envelope")
183	}
184}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close