github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/cli/mode/location_test.go (about)

     1  package mode
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"path/filepath"
     7  	"runtime"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  
    12  	"src.elv.sh/pkg/cli"
    13  	. "src.elv.sh/pkg/cli/clitest"
    14  	"src.elv.sh/pkg/cli/term"
    15  	"src.elv.sh/pkg/store"
    16  	"src.elv.sh/pkg/testutil"
    17  	"src.elv.sh/pkg/ui"
    18  )
    19  
    20  type locationStore struct {
    21  	storedDirs []store.Dir
    22  	dirsError  error
    23  	chdir      func(dir string) error
    24  	wd         string
    25  }
    26  
    27  func (ts locationStore) Dirs(blacklist map[string]struct{}) ([]store.Dir, error) {
    28  	dirs := []store.Dir{}
    29  	for _, dir := range ts.storedDirs {
    30  		if _, ok := blacklist[dir.Path]; ok {
    31  			continue
    32  		}
    33  		dirs = append(dirs, dir)
    34  	}
    35  	return dirs, ts.dirsError
    36  }
    37  
    38  func (ts locationStore) Chdir(dir string) error {
    39  	if ts.chdir == nil {
    40  		return nil
    41  	}
    42  	return ts.chdir(dir)
    43  }
    44  
    45  func (ts locationStore) Getwd() (string, error) {
    46  	return ts.wd, nil
    47  }
    48  
    49  func TestNewLocation_NoStore(t *testing.T) {
    50  	f := Setup()
    51  	defer f.Stop()
    52  
    53  	_, err := NewLocation(f.App, LocationSpec{})
    54  	if err != errNoDirectoryHistoryStore {
    55  		t.Error("want errNoDirectoryHistoryStore")
    56  	}
    57  }
    58  
    59  func TestNewLocation_StoreError(t *testing.T) {
    60  	f := Setup()
    61  	defer f.Stop()
    62  
    63  	_, err := NewLocation(f.App,
    64  		LocationSpec{Store: locationStore{dirsError: errors.New("ERROR")}})
    65  	if err.Error() != "db error: ERROR" {
    66  		t.Error("want db error")
    67  	}
    68  }
    69  
    70  func TestLocation_FullWorkflow(t *testing.T) {
    71  	home, cleanupHome := testutil.InTempHome()
    72  	defer cleanupHome()
    73  	f := Setup()
    74  	defer f.Stop()
    75  
    76  	errChdir := errors.New("mock chdir error")
    77  	chdirCh := make(chan string, 100)
    78  	dirs := []store.Dir{
    79  		{Path: filepath.Join(home, "go"), Score: 200},
    80  		{Path: home, Score: 100},
    81  		{Path: fixPath("/tmp/foo/bar/lorem/ipsum"), Score: 50},
    82  	}
    83  	startLocation(f.App, LocationSpec{Store: locationStore{
    84  		storedDirs: dirs,
    85  		chdir:      func(dir string) error { chdirCh <- dir; return errChdir },
    86  	}})
    87  
    88  	// Test UI.
    89  	wantBuf := locationBuf(
    90  		"",
    91  		"200 "+filepath.Join("~", "go"),
    92  		"100 ~",
    93  		" 50 "+fixPath("/tmp/foo/bar/lorem/ipsum"))
    94  	f.TTY.TestBuffer(t, wantBuf)
    95  
    96  	// Test filtering.
    97  	f.TTY.Inject(term.K('f'), term.K('o'))
    98  
    99  	wantBuf = locationBuf(
   100  		"fo",
   101  		" 50 "+fixPath("/tmp/foo/bar/lorem/ipsum"))
   102  	f.TTY.TestBuffer(t, wantBuf)
   103  
   104  	// Test accepting.
   105  	f.TTY.Inject(term.K(ui.Enter))
   106  	// There should be no change to codearea after accepting.
   107  	f.TestTTY(t /* nothing */)
   108  	// Error from Chdir should be sent to notes.
   109  	f.TestTTYNotes(t, "mock chdir error")
   110  	// Chdir should be called.
   111  	wantChdir := fixPath("/tmp/foo/bar/lorem/ipsum")
   112  	select {
   113  	case got := <-chdirCh:
   114  		if got != wantChdir {
   115  			t.Errorf("Chdir called with %s, want %s", got, wantChdir)
   116  		}
   117  	case <-time.After(testutil.ScaledMs(1000)):
   118  		t.Errorf("Chdir not called")
   119  	}
   120  }
   121  
   122  func TestLocation_Hidden(t *testing.T) {
   123  	f := Setup()
   124  	defer f.Stop()
   125  
   126  	dirs := []store.Dir{
   127  		{Path: fixPath("/usr/bin"), Score: 200},
   128  		{Path: fixPath("/usr"), Score: 100},
   129  		{Path: fixPath("/tmp"), Score: 50},
   130  	}
   131  	startLocation(f.App, LocationSpec{
   132  		Store:         locationStore{storedDirs: dirs},
   133  		IterateHidden: func(f func(string)) { f(fixPath("/usr")) },
   134  	})
   135  	// Test UI.
   136  	wantBuf := locationBuf(
   137  		"",
   138  		"200 "+fixPath("/usr/bin"),
   139  		" 50 "+fixPath("/tmp"))
   140  	f.TTY.TestBuffer(t, wantBuf)
   141  }
   142  
   143  func TestLocation_Pinned(t *testing.T) {
   144  	f := Setup()
   145  	defer f.Stop()
   146  
   147  	dirs := []store.Dir{
   148  		{Path: fixPath("/usr/bin"), Score: 200},
   149  		{Path: fixPath("/usr"), Score: 100},
   150  		{Path: fixPath("/tmp"), Score: 50},
   151  	}
   152  	startLocation(f.App, LocationSpec{
   153  		Store:         locationStore{storedDirs: dirs},
   154  		IteratePinned: func(f func(string)) { f(fixPath("/home")); f(fixPath("/usr")) },
   155  	})
   156  	// Test UI.
   157  	wantBuf := locationBuf(
   158  		"",
   159  		"  * "+fixPath("/home"),
   160  		"  * "+fixPath("/usr"),
   161  		"200 "+fixPath("/usr/bin"),
   162  		" 50 "+fixPath("/tmp"))
   163  	f.TTY.TestBuffer(t, wantBuf)
   164  }
   165  
   166  func TestLocation_HideWd(t *testing.T) {
   167  	f := Setup()
   168  	defer f.Stop()
   169  
   170  	dirs := []store.Dir{
   171  		{Path: fixPath("/home"), Score: 200},
   172  		{Path: fixPath("/tmp"), Score: 50},
   173  	}
   174  	startLocation(f.App, LocationSpec{Store: locationStore{storedDirs: dirs, wd: fixPath("/home")}})
   175  	// Test UI.
   176  	wantBuf := locationBuf(
   177  		"",
   178  		" 50 "+fixPath("/tmp"))
   179  	f.TTY.TestBuffer(t, wantBuf)
   180  }
   181  
   182  func TestLocation_Workspace(t *testing.T) {
   183  	f := Setup()
   184  	defer f.Stop()
   185  
   186  	chdir := ""
   187  	dirs := []store.Dir{
   188  		{Path: fixPath("home/src"), Score: 200},
   189  		{Path: fixPath("ws1/src"), Score: 150},
   190  		{Path: fixPath("ws2/bin"), Score: 100},
   191  		{Path: fixPath("/tmp"), Score: 50},
   192  	}
   193  	startLocation(f.App, LocationSpec{
   194  		Store: locationStore{
   195  			storedDirs: dirs,
   196  			wd:         fixPath("/home/elf/bin"),
   197  			chdir: func(dir string) error {
   198  				chdir = dir
   199  				return nil
   200  			},
   201  		},
   202  		IterateWorkspaces: func(f func(kind, pattern string) bool) {
   203  			if runtime.GOOS == "windows" {
   204  				// Invalid patterns are ignored.
   205  				f("ws1", `C:\\usr\\[^\\+`)
   206  				f("home", `C:\\home\\[^\\]+`)
   207  				f("ws2", `C:\\tmp\[^\]+`)
   208  			} else {
   209  				// Invalid patterns are ignored.
   210  				f("ws1", "/usr/[^/+")
   211  				f("home", "/home/[^/]+")
   212  				f("ws2", "/tmp/[^/]+")
   213  			}
   214  		},
   215  	})
   216  
   217  	wantBuf := locationBuf(
   218  		"",
   219  		"200 "+fixPath("home/src"),
   220  		" 50 "+fixPath("/tmp"))
   221  	f.TTY.TestBuffer(t, wantBuf)
   222  
   223  	f.TTY.Inject(term.K(ui.Enter))
   224  	f.TestTTY(t /* nothing */)
   225  	wantChdir := fixPath("/home/elf/src")
   226  	if chdir != wantChdir {
   227  		t.Errorf("got chdir %q, want %q", chdir, wantChdir)
   228  	}
   229  }
   230  
   231  func locationBuf(filter string, lines ...string) *term.Buffer {
   232  	b := term.NewBufferBuilder(50).
   233  		Newline(). // empty code area
   234  		WriteStyled(modeLine(" LOCATION ", true)).
   235  		Write(filter).SetDotHere()
   236  	for i, line := range lines {
   237  		b.Newline()
   238  		if i == 0 {
   239  			b.WriteStyled(ui.T(fmt.Sprintf("%-50s", line), ui.Inverse))
   240  		} else {
   241  			b.Write(line)
   242  		}
   243  	}
   244  	return b.Buffer()
   245  }
   246  
   247  func fixPath(path string) string {
   248  	if runtime.GOOS != "windows" {
   249  		return path
   250  	}
   251  	if path[0] == '/' {
   252  		path = "C:" + path
   253  	}
   254  	return strings.ReplaceAll(path, "/", "\\")
   255  }
   256  
   257  func startLocation(app cli.App, spec LocationSpec) {
   258  	w, err := NewLocation(app, spec)
   259  	startMode(app, w, err)
   260  }