elvish/pkg/cli/modes/location_test.go
2021-12-08 00:26:17 +00:00

262 lines
5.9 KiB
Go

package modes
import (
"errors"
"fmt"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
"src.elv.sh/pkg/cli"
. "src.elv.sh/pkg/cli/clitest"
"src.elv.sh/pkg/cli/term"
"src.elv.sh/pkg/store/storedefs"
"src.elv.sh/pkg/testutil"
"src.elv.sh/pkg/ui"
)
type locationStore struct {
storedDirs []storedefs.Dir
dirsError error
chdir func(dir string) error
wd string
}
func (ts locationStore) Dirs(blacklist map[string]struct{}) ([]storedefs.Dir, error) {
dirs := []storedefs.Dir{}
for _, dir := range ts.storedDirs {
if _, ok := blacklist[dir.Path]; ok {
continue
}
dirs = append(dirs, dir)
}
return dirs, ts.dirsError
}
func (ts locationStore) Chdir(dir string) error {
if ts.chdir == nil {
return nil
}
return ts.chdir(dir)
}
func (ts locationStore) Getwd() (string, error) {
return ts.wd, nil
}
func TestNewLocation_NoStore(t *testing.T) {
f := Setup()
defer f.Stop()
_, err := NewLocation(f.App, LocationSpec{})
if err != errNoDirectoryHistoryStore {
t.Error("want errNoDirectoryHistoryStore")
}
}
func TestNewLocation_StoreError(t *testing.T) {
f := Setup()
defer f.Stop()
_, err := NewLocation(f.App,
LocationSpec{Store: locationStore{dirsError: errors.New("ERROR")}})
if err.Error() != "db error: ERROR" {
t.Error("want db error")
}
}
func TestLocation_FullWorkflow(t *testing.T) {
home := testutil.InTempHome(t)
f := Setup()
defer f.Stop()
errChdir := errors.New("mock chdir error")
chdirCh := make(chan string, 100)
dirs := []storedefs.Dir{
{Path: filepath.Join(home, "go"), Score: 200},
{Path: home, Score: 100},
{Path: fixPath("/tmp/foo/bar/lorem/ipsum"), Score: 50},
}
startLocation(f.App, LocationSpec{Store: locationStore{
storedDirs: dirs,
chdir: func(dir string) error { chdirCh <- dir; return errChdir },
}})
// Test UI.
wantBuf := locationBuf(
"",
"200 "+filepath.Join("~", "go"),
"100 ~",
" 50 "+fixPath("/tmp/foo/bar/lorem/ipsum"))
f.TTY.TestBuffer(t, wantBuf)
// Test filtering.
f.TTY.Inject(term.K('f'), term.K('o'))
wantBuf = locationBuf(
"fo",
" 50 "+fixPath("/tmp/foo/bar/lorem/ipsum"))
f.TTY.TestBuffer(t, wantBuf)
// Test accepting.
f.TTY.Inject(term.K(ui.Enter))
// There should be no change to codearea after accepting.
f.TestTTY(t /* nothing */)
// Error from Chdir should be sent to notes.
f.TestTTYNotes(t,
"error: mock chdir error", Styles,
"!!!!!!")
// Chdir should be called.
wantChdir := fixPath("/tmp/foo/bar/lorem/ipsum")
select {
case got := <-chdirCh:
if got != wantChdir {
t.Errorf("Chdir called with %s, want %s", got, wantChdir)
}
case <-time.After(testutil.Scaled(time.Second)):
t.Errorf("Chdir not called")
}
}
func TestLocation_Hidden(t *testing.T) {
f := Setup()
defer f.Stop()
dirs := []storedefs.Dir{
{Path: fixPath("/usr/bin"), Score: 200},
{Path: fixPath("/usr"), Score: 100},
{Path: fixPath("/tmp"), Score: 50},
}
startLocation(f.App, LocationSpec{
Store: locationStore{storedDirs: dirs},
IterateHidden: func(f func(string)) { f(fixPath("/usr")) },
})
// Test UI.
wantBuf := locationBuf(
"",
"200 "+fixPath("/usr/bin"),
" 50 "+fixPath("/tmp"))
f.TTY.TestBuffer(t, wantBuf)
}
func TestLocation_Pinned(t *testing.T) {
f := Setup()
defer f.Stop()
dirs := []storedefs.Dir{
{Path: fixPath("/usr/bin"), Score: 200},
{Path: fixPath("/usr"), Score: 100},
{Path: fixPath("/tmp"), Score: 50},
}
startLocation(f.App, LocationSpec{
Store: locationStore{storedDirs: dirs},
IteratePinned: func(f func(string)) { f(fixPath("/home")); f(fixPath("/usr")) },
})
// Test UI.
wantBuf := locationBuf(
"",
" * "+fixPath("/home"),
" * "+fixPath("/usr"),
"200 "+fixPath("/usr/bin"),
" 50 "+fixPath("/tmp"))
f.TTY.TestBuffer(t, wantBuf)
}
func TestLocation_HideWd(t *testing.T) {
f := Setup()
defer f.Stop()
dirs := []storedefs.Dir{
{Path: fixPath("/home"), Score: 200},
{Path: fixPath("/tmp"), Score: 50},
}
startLocation(f.App, LocationSpec{Store: locationStore{storedDirs: dirs, wd: fixPath("/home")}})
// Test UI.
wantBuf := locationBuf(
"",
" 50 "+fixPath("/tmp"))
f.TTY.TestBuffer(t, wantBuf)
}
func TestLocation_Workspace(t *testing.T) {
f := Setup()
defer f.Stop()
chdir := ""
dirs := []storedefs.Dir{
{Path: fixPath("home/src"), Score: 200},
{Path: fixPath("ws1/src"), Score: 150},
{Path: fixPath("ws2/bin"), Score: 100},
{Path: fixPath("/tmp"), Score: 50},
}
startLocation(f.App, LocationSpec{
Store: locationStore{
storedDirs: dirs,
wd: fixPath("/home/elf/bin"),
chdir: func(dir string) error {
chdir = dir
return nil
},
},
IterateWorkspaces: func(f func(kind, pattern string) bool) {
if runtime.GOOS == "windows" {
// Invalid patterns are ignored.
f("ws1", `C:\\usr\\[^\\+`)
f("home", `C:\\home\\[^\\]+`)
f("ws2", `C:\\tmp\[^\]+`)
} else {
// Invalid patterns are ignored.
f("ws1", "/usr/[^/+")
f("home", "/home/[^/]+")
f("ws2", "/tmp/[^/]+")
}
},
})
wantBuf := locationBuf(
"",
"200 "+fixPath("home/src"),
" 50 "+fixPath("/tmp"))
f.TTY.TestBuffer(t, wantBuf)
f.TTY.Inject(term.K(ui.Enter))
f.TestTTY(t /* nothing */)
wantChdir := fixPath("/home/elf/src")
if chdir != wantChdir {
t.Errorf("got chdir %q, want %q", chdir, wantChdir)
}
}
func locationBuf(filter string, lines ...string) *term.Buffer {
b := term.NewBufferBuilder(50).
Newline(). // empty code area
WriteStyled(modeLine(" LOCATION ", true)).
Write(filter).SetDotHere()
for i, line := range lines {
b.Newline()
if i == 0 {
b.WriteStyled(ui.T(fmt.Sprintf("%-50s", line), ui.Inverse))
} else {
b.Write(line)
}
}
return b.Buffer()
}
func fixPath(path string) string {
if runtime.GOOS != "windows" {
return path
}
if path[0] == '/' {
path = "C:" + path
}
return strings.ReplaceAll(path, "/", "\\")
}
func startLocation(app cli.App, spec LocationSpec) {
w, err := NewLocation(app, spec)
startMode(app, w, err)
}