1package vimkeys
2
3import (
4 "errors"
5 "testing"
6)
7
8func TestTrie_InsertWalkPrefixes(t *testing.T) {
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 }
19 for _, binding := range bindings {
20 if err := trie.Insert(binding.seq, binding.action); err != nil {
21 t.Fatalf("Insert(%v) error = %v", binding.seq, err)
22 }
23 }
24
25 prefixCases := []struct {
26 name string
27 seq Sequence
28 wantExact bool
29 wantChildren bool
30 wantAction string
31 }{
32 {name: "]] prefix ]", seq: Sequence{{Sym: "]"}}, wantChildren: true},
33 {name: "]] full", seq: Sequence{{Sym: "]"}, {Sym: "]"}}, wantExact: true, wantAction: "double-bracket"},
34 {name: "]c prefix ]", seq: Sequence{{Sym: "]"}}, wantChildren: true},
35 {name: "]c full", seq: Sequence{{Sym: "]"}, {Sym: "c"}}, wantExact: true, wantAction: "bracket-c"},
36 {name: "gO prefix g", seq: Sequence{{Sym: "g"}}, wantChildren: true},
37 {name: "gO full", seq: Sequence{{Sym: "g"}, {Sym: "o", Mods: ModShift}}, wantExact: true, wantAction: "gO"},
38 {name: "yah prefix y", seq: Sequence{{Sym: "y"}}, wantChildren: true},
39 {name: "yah prefix ya", seq: Sequence{{Sym: "y"}, {Sym: "a"}}, wantChildren: true},
40 {name: "yah full", seq: Sequence{{Sym: "y"}, {Sym: "a"}, {Sym: "h"}}, wantExact: true, wantAction: "yah"},
41 }
42 for _, tt := range prefixCases {
43 t.Run(tt.name, func(t *testing.T) {
44 node, ok := trie.Walk(tt.seq)
45 if !ok {
46 t.Fatalf("Walk(%v) ok = false, want true", tt.seq)
47 }
48 if node.Exact != tt.wantExact {
49 t.Errorf("Walk(%v) Exact = %v, want %v", tt.seq, node.Exact, tt.wantExact)
50 }
51 if node.HasChildren != tt.wantChildren {
52 t.Errorf("Walk(%v) HasChildren = %v, want %v", tt.seq, node.HasChildren, tt.wantChildren)
53 }
54 if tt.wantExact && node.Action != tt.wantAction {
55 t.Errorf("Walk(%v) Action = %q, want %q", tt.seq, node.Action, tt.wantAction)
56 }
57 })
58 }
59}
60
61func TestTrie_InsertDuplicateReturnsConflict(t *testing.T) {
62 trie := NewTrie()
63 seq := Sequence{{Sym: "j"}}
64 if err := trie.Insert(seq, "first"); err != nil {
65 t.Fatalf("first Insert error = %v", err)
66 }
67 err := trie.Insert(seq, "second")
68 if !errors.Is(err, ErrConflict) {
69 t.Fatalf("duplicate Insert error = %v, want %v", err, ErrConflict)
70 }
71}
72
73func TestTrie_InsertEmptySequenceReturnsErrEmptyBinding(t *testing.T) {
74 trie := NewTrie()
75 for _, seq := range []Sequence{nil, {}} {
76 err := trie.Insert(seq, "root-action")
77 if !errors.Is(err, ErrEmptyBinding) {
78 t.Fatalf("Insert(%v) error = %v, want %v", seq, err, ErrEmptyBinding)
79 }
80 }
81 node, ok := trie.Walk(nil)
82 if !ok {
83 t.Fatal("Walk(nil) ok = false, want true for empty path")
84 }
85 if node.Exact || node.Action != "" {
86 t.Fatalf("Walk(nil) after rejected empty Insert = %+v, want no root action", node)
87 }
88 if trie.MaxDepth() != 0 {
89 t.Fatalf("MaxDepth() = %d after rejected empty Insert, want 0", trie.MaxDepth())
90 }
91}
92
93func TestTrie_MaxDepth(t *testing.T) {
94 trie := NewTrie()
95 if got := trie.MaxDepth(); got != 0 {
96 t.Fatalf("empty MaxDepth() = %d, want 0", got)
97 }
98 if err := trie.Insert(Sequence{{Sym: "y"}, {Sym: "a"}, {Sym: "h"}}, "yah"); err != nil {
99 t.Fatalf("Insert(yah) error = %v", err)
100 }
101 if err := trie.Insert(Sequence{{Sym: "]"}, {Sym: "]"}}, "brackets"); err != nil {
102 t.Fatalf("Insert(]]) error = %v", err)
103 }
104 if got := trie.MaxDepth(); got != 3 {
105 t.Fatalf("MaxDepth() = %d, want 3", got)
106 }
107}
108
109func TestTrie_WalkMissingPath(t *testing.T) {
110 trie := NewTrie()
111 if err := trie.Insert(Sequence{{Sym: "j"}}, "down"); err != nil {
112 t.Fatalf("Insert(j) error = %v", err)
113 }
114 _, ok := trie.Walk(Sequence{{Sym: "k"}})
115 if ok {
116 t.Fatal("Walk missing path ok = true, want false")
117 }
118}