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