github.com/mem/u-root@v2.0.1-0.20181004165302-9b18b4636a33+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  			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  }