1package vimkeys
2
3import (
4 "testing"
5)
6
7func testTrie(t *testing.T) *Trie {
8 t.Helper()
9 trie := NewTrie()
10 bindings := []struct {
11 seq Sequence
12 action string
13 }{
14 {seq: Sequence{{Sym: "]"}, {Sym: "]"}}, action: "double-bracket"},
15 {seq: Sequence{{Sym: "]"}, {Sym: "c"}}, action: "bracket-c"},
16 {seq: Sequence{{Sym: "g"}, {Sym: "o", Mods: ModShift}}, action: "gO"},
17 {seq: Sequence{{Sym: "y"}, {Sym: "a"}, {Sym: "h"}}, action: "yah"},
18 {seq: Sequence{{Sym: "]"}}, action: "single-bracket"},
19 {seq: Sequence{{Sym: "j"}}, action: "down"},
20 {seq: Sequence{{Sym: "0"}}, action: "zero"},
21 }
22 for _, binding := range bindings {
23 if err := trie.Insert(binding.seq, binding.action); err != nil {
24 t.Fatalf("Insert(%v) error = %v", binding.seq, err)
25 }
26 }
27 return trie
28}
29
30func TestMatcher_FeedPrefixesPending(t *testing.T) {
31 m := NewMatcher(testTrie(t))
32
33 cases := []struct {
34 name string
35 keys []Key
36 want ResultKind
37 pending string
38 }{
39 {
40 name: "]] first bracket",
41 keys: []Key{{Sym: "]"}},
42 want: ResultPending,
43 pending: "]",
44 },
45 {
46 name: "gO prefix g",
47 keys: []Key{{Sym: "g"}},
48 want: ResultPending,
49 pending: "g",
50 },
51 {
52 name: "yah prefix ya",
53 keys: []Key{{Sym: "y"}, {Sym: "a"}},
54 want: ResultPending,
55 pending: "ya",
56 },
57 }
58 for _, tt := range cases {
59 t.Run(tt.name, func(t *testing.T) {
60 m.Reset()
61 var result Result
62 for _, key := range tt.keys {
63 result = m.Feed(key)
64 }
65 if result.Kind != tt.want {
66 t.Fatalf("Feed kind = %v, want %v", result.Kind, tt.want)
67 }
68 if result.Pending != tt.pending {
69 t.Fatalf("Pending = %q, want %q", result.Pending, tt.pending)
70 }
71 if m.Pending() != tt.pending {
72 t.Fatalf("Matcher.Pending() = %q, want %q", m.Pending(), tt.pending)
73 }
74 if result.Count != 0 {
75 t.Fatalf("Count = %d, want 0 (absent)", result.Count)
76 }
77 })
78 }
79}
80
81func TestMatcher_FeedComplete(t *testing.T) {
82 m := NewMatcher(testTrie(t))
83
84 cases := []struct {
85 name string
86 keys []Key
87 action string
88 }{
89 {name: "]]", keys: []Key{{Sym: "]"}, {Sym: "]"}}, action: "double-bracket"},
90 {name: "gO", keys: []Key{{Sym: "g"}, {Sym: "o", Mods: ModShift}}, action: "gO"},
91 {name: "yah", keys: []Key{{Sym: "y"}, {Sym: "a"}, {Sym: "h"}}, action: "yah"},
92 }
93 for _, tt := range cases {
94 t.Run(tt.name, func(t *testing.T) {
95 m.Reset()
96 var result Result
97 for _, key := range tt.keys {
98 result = m.Feed(key)
99 }
100 if result.Kind != ResultComplete {
101 t.Fatalf("Feed kind = %v, want %v", result.Kind, ResultComplete)
102 }
103 if result.Action != tt.action {
104 t.Fatalf("Action = %q, want %q", result.Action, tt.action)
105 }
106 if result.Count != 0 {
107 t.Fatalf("Count = %d, want 0", result.Count)
108 }
109 })
110 }
111}
112
113func TestMatcher_Counts(t *testing.T) {
114 m := NewMatcher(testTrie(t))
115
116 cases := []struct {
117 name string
118 keys []Key
119 count int
120 action string
121 }{
122 {
123 name: "2]]",
124 keys: []Key{{Sym: "2"}, {Sym: "]"}, {Sym: "]"}},
125 count: 2,
126 action: "double-bracket",
127 },
128 {
129 name: "3j",
130 keys: []Key{{Sym: "3"}, {Sym: "j"}},
131 count: 3,
132 action: "down",
133 },
134 {
135 name: "10j",
136 keys: []Key{{Sym: "1"}, {Sym: "0"}, {Sym: "j"}},
137 count: 10,
138 action: "down",
139 },
140 {
141 name: "0 binding",
142 keys: []Key{{Sym: "0"}},
143 count: 0,
144 action: "zero",
145 },
146 }
147 for _, tt := range cases {
148 t.Run(tt.name, func(t *testing.T) {
149 m.Reset()
150 var result Result
151 for _, key := range tt.keys {
152 result = m.Feed(key)
153 }
154 if result.Kind != ResultComplete {
155 t.Fatalf("Feed kind = %v, want %v", result.Kind, ResultComplete)
156 }
157 if result.Count != tt.count {
158 t.Fatalf("Count = %d, want %d", result.Count, tt.count)
159 }
160 if result.Action != tt.action {
161 t.Fatalf("Action = %q, want %q", result.Action, tt.action)
162 }
163 })
164 }
165}
166
167func TestMatcher_ZeroThenCount(t *testing.T) {
168 m := NewMatcher(testTrie(t))
169
170 zero := m.Feed(Key{Sym: "0"})
171 if zero.Kind != ResultComplete || zero.Action != "zero" || zero.Count != 0 {
172 t.Fatalf("first zero result = %#v, want complete zero binding", zero)
173 }
174
175 count := m.Feed(Key{Sym: "3"})
176 if count.Kind != ResultPending || count.Pending != "3" || count.Count != 3 {
177 t.Fatalf("count prefix result = %#v, want pending count 3", count)
178 }
179
180 complete := m.Feed(Key{Sym: "j"})
181 if complete.Kind != ResultComplete || complete.Action != "down" || complete.Count != 3 {
182 t.Fatalf("counted action result = %#v, want complete down count 3", complete)
183 }
184}
185
186func TestMatcher_CountPendingString(t *testing.T) {
187 m := NewMatcher(testTrie(t))
188 m.Reset()
189 result := m.Feed(Key{Sym: "3"})
190 if result.Kind != ResultPending {
191 t.Fatalf("Feed kind = %v, want %v", result.Kind, ResultPending)
192 }
193 if result.Pending != "3" {
194 t.Fatalf("Pending = %q, want %q", result.Pending, "3")
195 }
196 if m.Pending() != "3" {
197 t.Fatalf("Matcher.Pending() = %q, want %q", m.Pending(), "3")
198 }
199}
200
201func TestMatcher_CountCap9999(t *testing.T) {
202 m := NewMatcher(testTrie(t))
203 digits := make([]Key, 0, 5)
204 for _, ch := range "10000" {
205 digits = append(digits, Key{Sym: string(ch)})
206 }
207 digits = append(digits, Key{Sym: "j"})
208
209 m.Reset()
210 var result Result
211 for _, key := range digits {
212 result = m.Feed(key)
213 }
214 if result.Kind != ResultComplete {
215 t.Fatalf("Feed kind = %v, want %v", result.Kind, ResultComplete)
216 }
217 if result.Count != 9999 {
218 t.Fatalf("Count = %d, want 9999", result.Count)
219 }
220}
221
222func TestMatcher_AutomaticReset(t *testing.T) {
223 m := NewMatcher(testTrie(t))
224
225 m.Reset()
226 complete := m.Feed(Key{Sym: "j"})
227 if complete.Kind != ResultComplete {
228 t.Fatalf("first Feed kind = %v, want %v", complete.Kind, ResultComplete)
229 }
230
231 next := m.Feed(Key{Sym: "g"})
232 if next.Kind != ResultPending {
233 t.Fatalf("after complete Feed kind = %v, want %v", next.Kind, ResultPending)
234 }
235 if m.Pending() != "g" {
236 t.Fatalf("Pending after reset = %q, want g", m.Pending())
237 }
238
239 m.Reset()
240 invalid := m.Feed(Key{Sym: "x"})
241 if invalid.Kind != ResultInvalid {
242 t.Fatalf("invalid Feed kind = %v, want %v", invalid.Kind, ResultInvalid)
243 }
244
245 afterInvalid := m.Feed(Key{Sym: "j"})
246 if afterInvalid.Kind != ResultComplete {
247 t.Fatalf("after invalid Feed kind = %v, want %v", afterInvalid.Kind, ResultComplete)
248 }
249}
250
251func TestMatcher_AmbiguityClearsOnTransition(t *testing.T) {
252 trie := NewTrie()
253 for _, binding := range []struct {
254 seq Sequence
255 action string
256 }{
257 {seq: Sequence{{Sym: "]"}}, action: "single-bracket"},
258 {seq: Sequence{{Sym: "]"}, {Sym: "]"}, {Sym: "x"}}, action: "triple-bracket"},
259 } {
260 if err := trie.Insert(binding.seq, binding.action); err != nil {
261 t.Fatalf("Insert(%v) error = %v", binding.seq, err)
262 }
263 }
264
265 m := NewMatcher(trie)
266 m.Reset()
267
268 result := m.Feed(Key{Sym: "]"})
269 if result.Kind != ResultPending {
270 t.Fatalf("after ] Feed kind = %v, want %v", result.Kind, ResultPending)
271 }
272 if !m.Ambiguous() {
273 t.Fatal("Ambiguous() after ] = false, want true")
274 }
275
276 result = m.Feed(Key{Sym: "]"})
277 if result.Kind != ResultPending {
278 t.Fatalf("after ]] Feed kind = %v, want %v", result.Kind, ResultPending)
279 }
280 if m.Ambiguous() {
281 t.Fatal("Ambiguous() after ]] = true, want false")
282 }
283}
284
285func TestMatcher_AmbiguousShortLong(t *testing.T) {
286 m := NewMatcher(testTrie(t))
287
288 m.Reset()
289 result := m.Feed(Key{Sym: "]"})
290 if result.Kind != ResultPending {
291 t.Fatalf("Feed ] kind = %v, want %v", result.Kind, ResultPending)
292 }
293 if !m.Ambiguous() {
294 t.Fatal("Ambiguous() = false, want true after ]")
295 }
296
297 short, ok := m.ResolveAmbiguity()
298 if !ok {
299 t.Fatal("ResolveAmbiguity() ok = false, want true")
300 }
301 if short.Kind != ResultComplete {
302 t.Fatalf("ResolveAmbiguity() kind = %v, want %v", short.Kind, ResultComplete)
303 }
304 if short.Action != "single-bracket" {
305 t.Fatalf("short action = %q, want %q", short.Action, "single-bracket")
306 }
307
308 m.Reset()
309 _ = m.Feed(Key{Sym: "]"})
310 finish := m.Feed(Key{Sym: "]"})
311 if finish.Kind != ResultComplete {
312 t.Fatalf("finish ]] kind = %v, want %v", finish.Kind, ResultComplete)
313 }
314 if finish.Action != "double-bracket" {
315 t.Fatalf("long action = %q, want %q", finish.Action, "double-bracket")
316 }
317}
318
319func TestMatcher_Reset(t *testing.T) {
320 m := NewMatcher(testTrie(t))
321 _ = m.Feed(Key{Sym: "y"})
322 _ = m.Feed(Key{Sym: "a"})
323 m.Reset()
324 if m.Pending() != "" {
325 t.Fatalf("Pending after Reset = %q, want empty", m.Pending())
326 }
327 if m.Ambiguous() {
328 t.Fatal("Ambiguous() after Reset = true, want false")
329 }
330}
331
332func TestMatcher_InvalidMidSequence(t *testing.T) {
333 m := NewMatcher(testTrie(t))
334 m.Reset()
335 _ = m.Feed(Key{Sym: "y"})
336 result := m.Feed(Key{Sym: "x"})
337 if result.Kind != ResultInvalid {
338 t.Fatalf("Feed kind = %v, want %v", result.Kind, ResultInvalid)
339 }
340}
341
342func TestMatcher_ResolveAmbiguityInvalid(t *testing.T) {
343 m := NewMatcher(testTrie(t))
344 m.Reset()
345 _, ok := m.ResolveAmbiguity()
346 if ok {
347 t.Fatal("ResolveAmbiguity without prefix ok = true, want false")
348 }
349}
350
351func TestMatcher_NilTriePendingEmpty(t *testing.T) {
352 m := NewMatcher(nil)
353 if got := m.Pending(); got != "" {
354 t.Fatalf("Pending() = %q, want empty", got)
355 }
356}
357
358func TestMatcher_NilTrieAmbiguousFalse(t *testing.T) {
359 m := NewMatcher(nil)
360 if m.Ambiguous() {
361 t.Fatal("Ambiguous() = true, want false")
362 }
363}
364
365func TestMatcher_NilTrieFeedInvalid(t *testing.T) {
366 m := NewMatcher(nil)
367
368 for _, key := range []Key{{Sym: "x"}, {Sym: "2"}} {
369 result := m.Feed(key)
370 if result.Kind != ResultInvalid {
371 t.Fatalf("Feed(%v) kind = %v, want %v", key, result.Kind, ResultInvalid)
372 }
373 if result.Pending != "" {
374 t.Fatalf("Feed(%v) pending = %q, want empty", key, result.Pending)
375 }
376 if got := m.Pending(); got != "" {
377 t.Fatalf("after Feed(%v) Pending() = %q, want empty", key, got)
378 }
379 if m.Ambiguous() {
380 t.Fatalf("after Feed(%v) Ambiguous() = true, want false", key)
381 }
382 }
383}
384
385func TestMatcher_InvalidDeadEnd(t *testing.T) {
386 trie := NewTrie()
387 trie.root.children[Key{Sym: "x"}] = newTrieNode()
388 m := NewMatcher(trie)
389 result := m.Feed(Key{Sym: "x"})
390 if result.Kind != ResultInvalid {
391 t.Fatalf("Feed kind = %v, want %v", result.Kind, ResultInvalid)
392 }
393}
394
395func TestMatcher_CountExactCap(t *testing.T) {
396 m := NewMatcher(testTrie(t))
397 m.Reset()
398 var result Result
399 for _, ch := range "9999" {
400 result = m.Feed(Key{Sym: string(ch)})
401 }
402 result = m.Feed(Key{Sym: "j"})
403 if result.Count != 9999 {
404 t.Fatalf("Count = %d, want 9999", result.Count)
405 }
406}