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 }