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