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  }