github.com/serversong/goreporter@v0.0.0-20200325104552-3cfaf44fd178/linters/copycheck/suffixtree/suffixtree_test.go (about) 1 package suffixtree 2 3 import "testing" 4 5 type char byte 6 7 func (c char) Val() int { 8 return int(c) 9 } 10 11 func str2tok(str string) []Token { 12 toks := make([]Token, len(str)) 13 for i, c := range str { 14 toks[i] = char(c) 15 } 16 return toks 17 } 18 19 func TestConstruction(t *testing.T) { 20 str := "cacao" 21 _, s := genStates(8, str) 22 // s[0] is root 23 s[0].addTran(0, 1, s[1]) // ca 24 s[0].addTran(1, 1, s[2]) // a 25 s[0].addTran(4, 4, s[3]) // o 26 27 s[1].addTran(2, 4, s[4]) // cao 28 s[1].addTran(4, 4, s[5]) // o 29 30 s[2].addTran(2, 4, s[4]) // cao 31 s[2].addTran(4, 4, s[5]) // o 32 33 cacao := New() 34 cacao.Update(str2tok(str)...) 35 compareTrees(t, s[0], cacao.root) 36 37 str2 := "banana" 38 _, r := genStates(4, str2) 39 r[0].addTran(0, 5, r[1]) // banana 40 r[0].addTran(1, 5, r[2]) // anana 41 r[0].addTran(2, 5, r[3]) // nana 42 43 banana := New() 44 banana.Update(str2tok(str2)...) 45 compareTrees(t, r[0], banana.root) 46 47 _, q := genStates(11, str2+"$") 48 // r[0] is root 49 q[0].addTran(0, 6, q[1]) // banana$ 50 q[0].addTran(1, 1, q[2]) // a 51 q[0].addTran(2, 3, q[3]) // na 52 q[0].addTran(6, 6, q[4]) // $ 53 54 q[2].addTran(2, 3, q[5]) // na 55 q[2].addTran(6, 6, q[6]) // $ 56 57 q[3].addTran(4, 6, q[7]) // na$ 58 q[3].addTran(6, 6, q[8]) // $ 59 60 q[5].addTran(4, 6, q[9]) // na$ 61 q[5].addTran(6, 6, q[10]) // $ 62 63 banana.Update(char('$')) 64 compareTrees(t, q[0], banana.root) 65 66 foo := New() 67 foo.Update(str2tok("a b ac c ")...) 68 } 69 70 func compareTrees(t *testing.T, expected, actual *state) { 71 ch1, ch2 := walker(expected), walker(actual) 72 for { 73 etran, ok1 := <-ch1 74 atran, ok2 := <-ch2 75 if !ok1 || !ok2 { 76 if ok1 { 77 t.Error("expected tree is longer") 78 } else if ok2 { 79 t.Error("actual tree is longer") 80 } 81 break 82 } 83 if etran.start != atran.start || etran.ActEnd() != atran.ActEnd() { 84 t.Errorf("got transition (%d, %d) '%s', want (%d, %d) '%s'", 85 atran.start, atran.ActEnd(), actual.tree.data[atran.start:atran.ActEnd()+1], 86 etran.start, etran.ActEnd(), expected.tree.data[etran.start:etran.ActEnd()+1], 87 ) 88 } 89 } 90 } 91 92 func walker(s *state) <-chan *tran { 93 ch := make(chan *tran) 94 go func() { 95 walk(s, ch) 96 close(ch) 97 }() 98 return ch 99 } 100 101 func walk(s *state, ch chan<- *tran) { 102 for _, tr := range s.trans { 103 ch <- tr 104 walk(tr.state, ch) 105 } 106 } 107 108 func genStates(count int, data string) (*STree, []*state) { 109 t := new(STree) 110 t.data = str2tok(data) 111 states := make([]*state, count) 112 for i := range states { 113 states[i] = newState(t) 114 } 115 return t, states 116 } 117 118 type refPair struct { 119 s *state 120 start, end Pos 121 } 122 123 func TestCanonize(t *testing.T) { 124 tree, s := genStates(5, "somebanana") 125 tree.auxState, tree.root = s[4], s[0] 126 s[0].addTran(0, 3, s[1]) 127 s[1].addTran(4, 6, s[2]) 128 s[2].addTran(7, infinity, s[3]) 129 130 find := func(needle *state) int { 131 for i, state := range s { 132 if state == needle { 133 return i 134 } 135 } 136 return -1 137 } 138 139 var testCases = []struct { 140 origin, expected refPair 141 }{ 142 {refPair{s[0], 0, 0}, refPair{s[0], 0, 0}}, 143 {refPair{s[0], 0, 2}, refPair{s[0], 0, 0}}, 144 {refPair{s[0], 0, 3}, refPair{s[1], 4, 0}}, 145 {refPair{s[0], 0, 8}, refPair{s[2], 7, 0}}, 146 {refPair{s[0], 0, 6}, refPair{s[2], 7, 0}}, 147 {refPair{s[0], 0, 100}, refPair{s[2], 7, 0}}, 148 {refPair{s[4], -1, 100}, refPair{s[2], 7, 0}}, 149 } 150 151 for _, tc := range testCases { 152 s, start := tree.canonize(tc.origin.s, tc.origin.start, tc.origin.end) 153 if s != tc.expected.s || start != tc.expected.start { 154 t.Errorf("for origin ref. pair (%d, (%d, %d)) got (%d, %d), want (%d, %d)", 155 find(tc.origin.s), tc.origin.start, tc.origin.end, 156 find(s), start, 157 find(tc.expected.s), tc.expected.start, 158 ) 159 } 160 } 161 } 162 163 func TestSplitting(t *testing.T) { 164 tree := new(STree) 165 tree.data = str2tok("banana|cbao") 166 s1 := newState(tree) 167 s2 := newState(tree) 168 s1.addTran(0, 3, s2) 169 170 // active point is (s1, 0, -1), an explicit state 171 tree.end = 7 // c 172 rets, end := tree.testAndSplit(s1, 0, -1) 173 if rets != s1 { 174 t.Errorf("got state %p, want %p", rets, s1) 175 } 176 if end { 177 t.Error("should not be an end-point") 178 } 179 tree.end = 8 // b 180 _, end = tree.testAndSplit(s1, 0, -1) 181 if !end { 182 t.Error("should be an end-point") 183 } 184 185 // active point is (s1, 0, 2), an implicit state 186 tree.end = 9 // a 187 rets, end = tree.testAndSplit(s1, 0, 2) 188 if rets != s1 { 189 t.Error("returned state should be unchanged") 190 } 191 if !end { 192 t.Error("should be an end-point") 193 } 194 195 // [s1]-banana->[s2] => [s1]-ban->[rets]-ana->[s2] 196 tree.end = 10 // o 197 rets, end = tree.testAndSplit(s1, 0, 2) 198 tr := s1.findTran(char('b')) 199 if tr == nil { 200 t.Error("should have a b-transition") 201 } else if tr.state != rets { 202 t.Errorf("got state %p, want %p", tr.state, rets) 203 } 204 tr2 := rets.findTran(char('a')) 205 if tr2 == nil { 206 t.Error("should have an a-transition") 207 } else if tr2.state != s2 { 208 t.Errorf("got state %p, want %p", tr2.state, s2) 209 } 210 if end { 211 t.Error("should not be an end-point") 212 } 213 } 214 215 func TestPosMaxValue(t *testing.T) { 216 var p Pos = infinity 217 if p+1 > 0 { 218 t.Error("const infinity is not max value") 219 } 220 } 221 222 func BenchmarkConstruction(b *testing.B) { 223 stream := str2tok(`all work and no play makes jack a dull boy 224 all work and no play makes jack a dull boy 225 all work and no play makes jack a dull boy`) 226 227 for i := 0; i < b.N; i++ { 228 t := New() 229 t.Update(stream...) 230 } 231 }