github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/complete/complete_test.go (about) 1 // Copyright 2012-2018 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package complete 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 "reflect" 15 "strings" 16 "testing" 17 ) 18 19 // TestSimple tests a basic completer for completion with arrays of strings, 20 // as might be used for builtin commands. 21 func TestSimple(t *testing.T) { 22 var ( 23 hinames = []string{"hi", "hil", "hit"} 24 hnames = append(hinames, "how") 25 allnames = append(hnames, "there") 26 tests = []struct { 27 in string 28 x string 29 outs []string 30 }{ 31 {"hi", "hi", []string{}}, 32 {"h", "", hnames}, 33 {"t", "", []string{"there"}}, 34 } 35 ) 36 37 f := NewStringCompleter(allnames) 38 for _, tst := range tests { 39 x, o, err := f.Complete(tst.in) 40 if err != nil { 41 t.Errorf("Complete %v: got %v, want nil", tst.in, err) 42 continue 43 } 44 if x != tst.x { 45 t.Errorf("Complete %v: got %v, want %v", tst.in, x, tst.x) 46 } 47 if !reflect.DeepEqual(o, tst.outs) { 48 t.Errorf("Complete %v: got %v, want %v", tst.in, o, tst.outs) 49 } 50 } 51 } 52 53 // TestFile tests the file completer 54 func TestFile(t *testing.T) { 55 tempDir, err := ioutil.TempDir("", "TestComplete") 56 if err != nil { 57 t.Fatal(err) 58 } 59 defer os.RemoveAll(tempDir) 60 61 var ( 62 hinames = []string{"hi", "hil", "hit"} 63 hnames = append(hinames, "how") 64 allnames = append(hnames, "there") 65 tests = []struct { 66 in string 67 x string 68 g []string 69 }{ 70 {"hi", "hi", hinames[1:]}, 71 {"h", "", hnames}, 72 {"t", "", []string{"there"}}, 73 } 74 ) 75 76 for _, n := range allnames { 77 if err := ioutil.WriteFile(filepath.Join(tempDir, n), []byte{}, 0600); err != nil { 78 t.Fatal(err) 79 } 80 t.Logf("Wrote %v", filepath.Join(tempDir, n)) 81 } 82 f := NewFileCompleter(tempDir) 83 errCount := 0 84 for _, tst := range tests { 85 x, o, err := f.Complete(tst.in) 86 if err != nil { 87 t.Errorf("%v: got %v, want nil", tst.in, err) 88 errCount++ 89 continue 90 } 91 t.Logf("tst %v gets %v %v", tst, x, o) 92 // potential issue here: we assume FileCompleter, which uses glob, returns 93 // sorted order. We'll see if that's an issue later. 94 // adjust outs for the path and then check it. 95 if len(o) != len(tst.g) { 96 t.Errorf("%v: %v results, want %v", tst, o, tst.g) 97 errCount++ 98 continue 99 } 100 if tst.x != x && x != filepath.Join(tempDir, tst.x) { 101 t.Errorf("%v: got %v, want %v", tst.in, x, filepath.Join(tempDir, tst.x)) 102 errCount++ 103 } 104 for i := range o { 105 p := filepath.Join(tempDir, tst.g[i]) 106 if o[i] != p { 107 t.Errorf("%v: got %v, want %v", tst.in, o, p) 108 errCount++ 109 continue 110 } 111 } 112 t.Logf("tst %v ok", tst) 113 } 114 t.Logf("%d errors", errCount) 115 } 116 117 // TestMulti tests a multi completer. It creates a multi completer consisting 118 // of a simple completer and another multicompleter, which in turn has two 119 // file completers. It also tests the Path completer. 120 func TestMulti(t *testing.T) { 121 tempDir, err := ioutil.TempDir("", "TestComplete") 122 if err != nil { 123 t.Fatal(err) 124 } 125 defer os.RemoveAll(tempDir) 126 127 var ( 128 hinames = []string{"hi", "hil", "hit"} 129 hnames = append(hinames, "how") 130 allnames = append(hnames, "there") 131 tests = []struct { 132 in string 133 x string 134 outs []string 135 }{ 136 {"hi", "hi", []string{}}, 137 {"h", "", hnames}, 138 {"t", "", []string{"there"}}, 139 {"ahi", "bin/ahi", []string{"bin/ahil", "bin/ahit"}}, 140 {"bh", "", []string{"sbin/bhi", "sbin/bhil", "sbin/bhit", "sbin/bhow"}}, 141 } 142 ) 143 for _, p := range []string{"bin", "sbin"} { 144 if err := os.MkdirAll(filepath.Join(tempDir, p), 0700); err != nil { 145 t.Fatal(err) 146 } 147 } 148 for _, n := range allnames { 149 if err := ioutil.WriteFile(filepath.Join(tempDir, "bin", "a"+n), []byte{}, 0600); err != nil { 150 t.Fatal(err) 151 } 152 if err := ioutil.WriteFile(filepath.Join(tempDir, "sbin", "b"+n), []byte{}, 0600); err != nil { 153 t.Fatal(err) 154 } 155 } 156 if err := os.Setenv("PATH", fmt.Sprintf("%s:%s", filepath.Join(tempDir, "bin"), filepath.Join(tempDir, "sbin"))); err != nil { 157 t.Fatal(err) 158 } 159 p, err := NewPathCompleter() 160 if err != nil { 161 t.Fatal(err) 162 } 163 // note that since p is a Multi, this also checks nested Multis 164 f := NewMultiCompleter(NewStringCompleter(allnames), p) 165 166 for _, tst := range tests { 167 x, o, err := f.Complete(tst.in) 168 if err != nil { 169 t.Errorf("Error Complete %v: got %v, want nil", tst.in, err) 170 continue 171 } 172 t.Logf("Complete: tst %v gets %v", tst, o) 173 if tst.x != x && x != filepath.Join(tempDir, tst.x) { 174 t.Errorf("ERROR %v: got %v, want %v", tst.in, x, tst.x) 175 } 176 177 // potential issue here: we assume FileCompleter, which uses glob, returns 178 // sorted order. We'll see if that's an issue later. 179 // adjust outs for the path and then check it. 180 if len(o) != len(tst.outs) { 181 t.Errorf("Error Complete %v, wrong len for return: %v results, want %v", tst, o, tst.outs) 182 continue 183 } 184 for i := range o { 185 p := tst.outs[i] 186 if tst.in[0] == 'a' || tst.in[0] == 'b' { 187 p = filepath.Join(tempDir, tst.outs[i]) 188 } 189 t.Logf("\tcheck %v", p) 190 if o[i] != p { 191 t.Errorf("Error Complete %v, %d'th result mismatches: got %v, want %v", tst.in, i, o[i], p) 192 } 193 } 194 t.Logf("Done check %v: found %v", tst, o) 195 } 196 } 197 198 func TestInOut(t *testing.T) { 199 var tests = []struct { 200 ins []string 201 stack string 202 }{ 203 {[]string{"a", "b", "c", "d"}, "d"}, 204 {[]string{""}, ""}, 205 {[]string{}, ""}, 206 } 207 for _, tst := range tests { 208 l := NewLine() 209 if len(tst.ins) > 0 { 210 l.Push(tst.ins...) 211 } 212 213 stack := l.Pop() 214 if stack != tst.stack { 215 t.Errorf("tst %v: got %v, want %v", tst, stack, tst.stack) 216 } 217 } 218 } 219 220 // TestInOut tests the InOut structures, which we don't know we want. 221 func TestInOutRW(t *testing.T) { 222 var els = []string{"ab", "bc", "de", "fgh"} 223 var outs = []string{"ab", "abbc", "abbcde", "abbcdefgh"} 224 225 l := NewLine() 226 t.Logf("%v %v %v", els, outs, l) 227 for i := range els { 228 s := strings.Join(els[:i+1], "") 229 l.Write([]byte(s)) 230 b, err := l.ReadAll() 231 if err != nil { 232 t.Errorf("ReadAll of %s: got %v, want nil", s, err) 233 } 234 if string(b) != outs[i] { 235 t.Errorf("Read back %s: got %s, want %s", s, string(b), s) 236 } 237 } 238 } 239 240 // TestLineReader tests Line Readers, and looks for proper read and output behavior. 241 func TestLineReader(t *testing.T) { 242 var ( 243 hinames = []string{"hi", "hil", "hit"} 244 hnames = append(hinames, "how") 245 allnames = append(hnames, "there") 246 tests = []struct { 247 in string 248 names []string 249 x string 250 choices []string 251 out string 252 }{ 253 {"there\t", []string{"there"}, "there", []string{}, "there"}, 254 {"there", []string{"there"}, "", []string{}, "there"}, 255 {"\n", []string{}, "", []string{}, ""}, 256 {"", []string{}, "", []string{}, ""}, 257 {" ", []string{}, "", []string{}, ""}, 258 } 259 ) 260 Debug = t.Logf 261 for i, tst := range tests[:1] { 262 r := bytes.NewBufferString(tst.in) 263 t.Logf("%d: Test %v", i, tst) 264 cr, cw := io.Pipe() 265 f := NewStringCompleter(allnames) 266 267 l := NewLineReader(f, r, cw) 268 var out []byte 269 go func(o string, r io.Reader) { 270 var err error 271 out, err = ioutil.ReadAll(r) 272 if err != nil { 273 t.Errorf("reading console io.Pipe: got %v, want nil", err) 274 } 275 if string(out) != o { 276 t.Errorf("console out: got %v, want %v", o, string(out)) 277 } 278 }(tst.out, cr) 279 280 err := l.ReadLine() 281 282 x := l.Exact 283 s := l.Candidates 284 t.Logf("ReadLine returns %v %v %v", x, s, err) 285 if err != nil && err != io.EOF && err != ErrEOL { 286 t.Errorf("Test %d: got %v, want nil", i, err) 287 continue 288 } 289 if len(s) != len(tst.choices) { 290 t.Errorf("Test %d: Got %d choices, want %d", i, len(s), len(tst.choices)) 291 continue 292 } 293 if len(s) == 0 { 294 continue 295 } 296 if x != tst.x { 297 t.Errorf("Test %d: Got %v, want %v", i, x, tst.x) 298 continue 299 } 300 t.Logf("%d passes", i) 301 } 302 } 303 304 // TestEnv tests the the environment completer. 305 func TestEnv(t *testing.T) { 306 var tests = []struct { 307 pathVal string 308 nels int 309 err error 310 }{ 311 {"", 0, ErrEmptyEnv}, 312 {"a", 1, nil}, 313 {"A:B", 2, nil}, 314 } 315 for _, tst := range tests { 316 t.Logf("tst %v", tst) 317 if err := os.Setenv("PATH", tst.pathVal); err != nil { 318 t.Fatal(err) 319 } 320 e, err := NewPathCompleter() 321 if tst.err != err { 322 t.Errorf("tst %v: got %v, want %v", tst, err, tst.err) 323 continue 324 } 325 t.Logf("NewPathCompleter returns %v, %v", e, err) 326 if tst.nels == 0 && e != nil { 327 t.Errorf("tst %v: got %v, want nil", tst, e) 328 continue 329 } 330 if tst.nels == 0 { 331 continue 332 } 333 if e == nil { 334 t.Errorf("tst %v: got nil, want MultiCompleter", tst) 335 continue 336 } 337 nels := len(e.(*MultiCompleter).Completers) 338 if nels != tst.nels { 339 t.Errorf("tst %v: got %d els, want %d", tst, nels, tst.nels) 340 } 341 } 342 }