github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/edit/builtins_test.go (about) 1 package edit 2 3 import ( 4 "io" 5 "testing" 6 7 "src.elv.sh/pkg/cli/term" 8 "src.elv.sh/pkg/cli/tk" 9 "src.elv.sh/pkg/eval/vals" 10 "src.elv.sh/pkg/tt" 11 "src.elv.sh/pkg/ui" 12 ) 13 14 func TestBindingTable(t *testing.T) { 15 f := setup() 16 defer f.Cleanup() 17 18 evals(f.Evaler, `called = $false`) 19 evals(f.Evaler, `m = (edit:binding-table [&a={ called = $true }])`) 20 _, ok := getGlobal(f.Evaler, "m").(bindingsMap) 21 if !ok { 22 t.Errorf("edit:binding-table did not create BindingMap variable") 23 } 24 } 25 26 func TestCloseMode(t *testing.T) { 27 f := setup() 28 defer f.Cleanup() 29 30 f.Editor.app.SetAddon(tk.Empty{}, false) 31 evals(f.Evaler, `edit:close-mode`) 32 33 if listing := f.Editor.app.CopyState().Addon; listing != nil { 34 t.Errorf("got listing %v, want nil", listing) 35 } 36 } 37 38 func TestDumpBuf(t *testing.T) { 39 f := setup() 40 defer f.Cleanup() 41 42 feedInput(f.TTYCtrl, "echo") 43 // Wait until the buffer we want has shown up. 44 f.TestTTY(t, 45 "~> echo", Styles, 46 " vvvv", term.DotHere, 47 ) 48 49 evals(f.Evaler, `html = (edit:-dump-buf)`) 50 testGlobal(t, f.Evaler, 51 "html", 52 `~> <span class="sgr-32">echo</span>`+"\n") 53 } 54 55 func TestInsertRaw(t *testing.T) { 56 f := setup() 57 defer f.Cleanup() 58 59 f.TTYCtrl.Inject(term.K('V', ui.Ctrl)) 60 wantBuf := f.MakeBuffer( 61 "~> ", term.DotHere, "\n", 62 " RAW ", Styles, 63 "*****", 64 ) 65 f.TTYCtrl.TestBuffer(t, wantBuf) 66 // Since we do not use real terminals in the test, we cannot have a 67 // realistic test case against actual raw inputs. However, we can still 68 // check that the builtin command does call the SetRawInput method with 1. 69 if raw := f.TTYCtrl.RawInput(); raw != 1 { 70 t.Errorf("RawInput() -> %d, want 1", raw) 71 } 72 73 // Raw mode does not respond to non-key events. 74 f.TTYCtrl.Inject(term.MouseEvent{}) 75 f.TTYCtrl.TestBuffer(t, wantBuf) 76 77 // Raw mode is dismissed after a single key event. 78 f.TTYCtrl.Inject(term.K('+')) 79 f.TestTTY(t, 80 "~> +", Styles, 81 " v", term.DotHere, 82 ) 83 } 84 85 func TestEndOfHistory(t *testing.T) { 86 f := setup() 87 defer f.Cleanup() 88 89 evals(f.Evaler, `edit:end-of-history`) 90 f.TestTTYNotes(t, "End of history") 91 } 92 93 func TestKey(t *testing.T) { 94 f := setup() 95 defer f.Cleanup() 96 97 evals(f.Evaler, `k = (edit:key a)`) 98 wantK := ui.K('a') 99 if k, _ := f.Evaler.Global().Index("k"); k != wantK { 100 t.Errorf("$k is %v, want %v", k, wantK) 101 } 102 } 103 104 func TestRedraw(t *testing.T) { 105 f := setup() 106 defer f.Cleanup() 107 108 evals(f.Evaler, 109 `edit:current-command = echo`, 110 `edit:redraw`) 111 f.TestTTY(t, 112 "~> echo", Styles, 113 " vvvv", term.DotHere) 114 evals(f.Evaler, `edit:redraw &full=$true`) 115 // TODO(xiaq): Test that this is actually a full redraw. 116 f.TestTTY(t, 117 "~> echo", Styles, 118 " vvvv", term.DotHere) 119 } 120 121 func TestClear(t *testing.T) { 122 f := setup() 123 defer f.Cleanup() 124 125 evals(f.Evaler, `edit:current-command = echo`, `edit:clear`) 126 f.TestTTY(t, 127 "~> echo", Styles, 128 " vvvv", term.DotHere) 129 if cleared := f.TTYCtrl.ScreenCleared(); cleared != 1 { 130 t.Errorf("screen cleared %v times, want 1", cleared) 131 } 132 } 133 134 func TestReturnCode(t *testing.T) { 135 f := setup() 136 defer f.Cleanup() 137 138 f.Editor.app.CodeArea().MutateState(func(s *tk.CodeAreaState) { 139 s.Buffer.Content = "test code" 140 }) 141 evals(f.Evaler, `edit:return-line`) 142 code, err := f.Wait() 143 if code != "test code" { 144 t.Errorf("got code %q, want %q", code, "test code") 145 } 146 if err != nil { 147 t.Errorf("got err %v, want nil", err) 148 } 149 } 150 151 func TestReturnEOF(t *testing.T) { 152 f := setup() 153 defer f.Cleanup() 154 155 evals(f.Evaler, `edit:return-eof`) 156 if _, err := f.Wait(); err != io.EOF { 157 t.Errorf("got err %v, want %v", err, io.EOF) 158 } 159 } 160 161 func TestSmartEnter_InsertsNewlineWhenIncomplete(t *testing.T) { 162 f := setup() 163 defer f.Cleanup() 164 165 f.SetCodeBuffer(tk.CodeBuffer{Content: "put [", Dot: 5}) 166 evals(f.Evaler, `edit:smart-enter`) 167 wantBuf := tk.CodeBuffer{Content: "put [\n", Dot: 6} 168 if buf := f.Editor.app.CodeArea().CopyState().Buffer; buf != wantBuf { 169 t.Errorf("got code buffer %v, want %v", buf, wantBuf) 170 } 171 } 172 173 func TestSmartEnter_AcceptsCodeWhenWholeBufferIsComplete(t *testing.T) { 174 f := setup() 175 defer f.Cleanup() 176 177 f.SetCodeBuffer(tk.CodeBuffer{Content: "put []", Dot: 5}) 178 evals(f.Evaler, `edit:smart-enter`) 179 wantCode := "put []" 180 if code, _ := f.Wait(); code != wantCode { 181 t.Errorf("got return code %q, want %q", code, wantCode) 182 } 183 } 184 185 func TestWordify(t *testing.T) { 186 f := setup() 187 defer f.Cleanup() 188 189 evals(f.Evaler, `@words = (edit:wordify 'ls str [list]')`) 190 wantWords := vals.MakeList("ls", "str", "[list]") 191 if words, _ := f.Evaler.Global().Index("words"); !vals.Equal(words, wantWords) { 192 t.Errorf("$words is %v, want %v", words, wantWords) 193 } 194 } 195 196 var bufferBuiltinsTests = []struct { 197 name string 198 bufBefore tk.CodeBuffer 199 bufAfter tk.CodeBuffer 200 }{ 201 { 202 "move-dot-left", 203 tk.CodeBuffer{Content: "ab", Dot: 1}, 204 tk.CodeBuffer{Content: "ab", Dot: 0}, 205 }, 206 { 207 "move-dot-right", 208 tk.CodeBuffer{Content: "ab", Dot: 1}, 209 tk.CodeBuffer{Content: "ab", Dot: 2}, 210 }, 211 { 212 "kill-rune-left", 213 tk.CodeBuffer{Content: "ab", Dot: 1}, 214 tk.CodeBuffer{Content: "b", Dot: 0}, 215 }, 216 { 217 "kill-rune-right", 218 tk.CodeBuffer{Content: "ab", Dot: 1}, 219 tk.CodeBuffer{Content: "a", Dot: 1}, 220 }, 221 } 222 223 func TestBufferBuiltins(t *testing.T) { 224 f := setup() 225 app := f.Editor.app 226 defer f.Cleanup() 227 228 for _, test := range bufferBuiltinsTests { 229 t.Run(test.name, func(t *testing.T) { 230 app.CodeArea().MutateState(func(s *tk.CodeAreaState) { 231 s.Buffer = test.bufBefore 232 }) 233 evals(f.Evaler, "edit:"+test.name) 234 if buf := app.CodeArea().CopyState().Buffer; buf != test.bufAfter { 235 t.Errorf("got buf %v, want %v", buf, test.bufAfter) 236 } 237 }) 238 } 239 } 240 241 // Tests for pure movers. 242 243 func TestMoveDotLeftRight(t *testing.T) { 244 tt.Test(t, tt.Fn("moveDotLeft", moveDotLeft), tt.Table{ 245 tt.Args("foo", 0).Rets(0), 246 tt.Args("bar", 3).Rets(2), 247 tt.Args("精灵", 0).Rets(0), 248 tt.Args("精灵", 3).Rets(0), 249 tt.Args("精灵", 6).Rets(3), 250 }) 251 tt.Test(t, tt.Fn("moveDotRight", moveDotRight), tt.Table{ 252 tt.Args("foo", 0).Rets(1), 253 tt.Args("bar", 3).Rets(3), 254 tt.Args("精灵", 0).Rets(3), 255 tt.Args("精灵", 3).Rets(6), 256 tt.Args("精灵", 6).Rets(6), 257 }) 258 } 259 260 func TestMoveDotSOLEOL(t *testing.T) { 261 buffer := "abc\ndef" 262 // Index: 263 // 012 34567 264 tt.Test(t, tt.Fn("moveDotSOL", moveDotSOL), tt.Table{ 265 tt.Args(buffer, 0).Rets(0), 266 tt.Args(buffer, 1).Rets(0), 267 tt.Args(buffer, 2).Rets(0), 268 tt.Args(buffer, 3).Rets(0), 269 tt.Args(buffer, 4).Rets(4), 270 tt.Args(buffer, 5).Rets(4), 271 tt.Args(buffer, 6).Rets(4), 272 tt.Args(buffer, 7).Rets(4), 273 }) 274 tt.Test(t, tt.Fn("moveDotEOL", moveDotEOL), tt.Table{ 275 tt.Args(buffer, 0).Rets(3), 276 tt.Args(buffer, 1).Rets(3), 277 tt.Args(buffer, 2).Rets(3), 278 tt.Args(buffer, 3).Rets(3), 279 tt.Args(buffer, 4).Rets(7), 280 tt.Args(buffer, 5).Rets(7), 281 tt.Args(buffer, 6).Rets(7), 282 tt.Args(buffer, 7).Rets(7), 283 }) 284 } 285 286 func TestMoveDotUpDown(t *testing.T) { 287 buffer := "abc\n精灵语\ndef" 288 // Index: 289 // 012 34 7 0 34567 290 // + 10 * 0 1 291 292 tt.Test(t, tt.Fn("moveDotUp", moveDotUp), tt.Table{ 293 tt.Args(buffer, 0).Rets(0), // a -> a 294 tt.Args(buffer, 1).Rets(1), // b -> b 295 tt.Args(buffer, 2).Rets(2), // c -> c 296 tt.Args(buffer, 3).Rets(3), // EOL1 -> EOL1 297 tt.Args(buffer, 4).Rets(0), // 精 -> a 298 tt.Args(buffer, 7).Rets(2), // 灵 -> c 299 tt.Args(buffer, 10).Rets(3), // 语 -> EOL1 300 tt.Args(buffer, 13).Rets(3), // EOL2 -> EOL1 301 tt.Args(buffer, 14).Rets(4), // d -> 精 302 tt.Args(buffer, 15).Rets(4), // e -> 精 (jump left half width) 303 tt.Args(buffer, 16).Rets(7), // f -> 灵 304 tt.Args(buffer, 17).Rets(7), // EOL3 -> 灵 (jump left half width) 305 }) 306 307 tt.Test(t, tt.Fn("moveDotDown", moveDotDown), tt.Table{ 308 tt.Args(buffer, 0).Rets(4), // a -> 精 309 tt.Args(buffer, 1).Rets(4), // b -> 精 (jump left half width) 310 tt.Args(buffer, 2).Rets(7), // c -> 灵 311 tt.Args(buffer, 3).Rets(7), // EOL1 -> 灵 (jump left half width) 312 tt.Args(buffer, 4).Rets(14), // 精 -> d 313 tt.Args(buffer, 7).Rets(16), // 灵 -> f 314 tt.Args(buffer, 10).Rets(17), // 语 -> EOL3 315 tt.Args(buffer, 13).Rets(17), // EOL2 -> EOL3 316 tt.Args(buffer, 14).Rets(14), // d -> d 317 tt.Args(buffer, 15).Rets(15), // e -> e 318 tt.Args(buffer, 16).Rets(16), // f -> f 319 tt.Args(buffer, 17).Rets(17), // EOL3 -> EOL3 320 }) 321 } 322 323 // Word movement tests. 324 325 // The string below is carefully chosen to test all word, small-word, and 326 // alnum-word move/kill functions, because it contains features to set the 327 // different movement behaviors apart. 328 // 329 // The string is annotated with carets (^) to indicate the beginning of words, 330 // and periods (.) to indicate trailing runes of words. Indices are also 331 // annotated. 332 // 333 // cd ~/downloads; rm -rf 2018aug07-pics/*; 334 // ^. ^........... ^. ^.. ^................ (word) 335 // ^. ^.^........^ ^. ^^. ^........^^...^.. (small-word) 336 // ^. ^........ ^. ^. ^........ ^... (alnum-word) 337 // 01234567890123456789012345678901234567890 338 // 0 1 2 3 4 339 // 340 // word boundaries: 0 3 16 19 23 341 // small-word boundaries: 0 3 5 14 16 19 20 23 32 33 37 342 // alnum-word boundaries: 0 5 16 20 23 33 343 // 344 var wordMoveTestBuffer = "cd ~/downloads; rm -rf 2018aug07-pics/*;" 345 346 var ( 347 // word boundaries: 0 3 16 19 23 348 moveDotLeftWordTests = tt.Table{ 349 tt.Args(wordMoveTestBuffer, 0).Rets(0), 350 tt.Args(wordMoveTestBuffer, 1).Rets(0), 351 tt.Args(wordMoveTestBuffer, 2).Rets(0), 352 tt.Args(wordMoveTestBuffer, 3).Rets(0), 353 tt.Args(wordMoveTestBuffer, 4).Rets(3), 354 tt.Args(wordMoveTestBuffer, 16).Rets(3), 355 tt.Args(wordMoveTestBuffer, 19).Rets(16), 356 tt.Args(wordMoveTestBuffer, 23).Rets(19), 357 tt.Args(wordMoveTestBuffer, 40).Rets(23), 358 } 359 moveDotRightWordTests = tt.Table{ 360 tt.Args(wordMoveTestBuffer, 0).Rets(3), 361 tt.Args(wordMoveTestBuffer, 1).Rets(3), 362 tt.Args(wordMoveTestBuffer, 2).Rets(3), 363 tt.Args(wordMoveTestBuffer, 3).Rets(16), 364 tt.Args(wordMoveTestBuffer, 16).Rets(19), 365 tt.Args(wordMoveTestBuffer, 19).Rets(23), 366 tt.Args(wordMoveTestBuffer, 23).Rets(40), 367 } 368 369 // small-word boundaries: 0 3 5 14 16 19 20 23 32 33 37 370 moveDotLeftSmallWordTests = tt.Table{ 371 tt.Args(wordMoveTestBuffer, 0).Rets(0), 372 tt.Args(wordMoveTestBuffer, 1).Rets(0), 373 tt.Args(wordMoveTestBuffer, 2).Rets(0), 374 tt.Args(wordMoveTestBuffer, 3).Rets(0), 375 tt.Args(wordMoveTestBuffer, 4).Rets(3), 376 tt.Args(wordMoveTestBuffer, 5).Rets(3), 377 tt.Args(wordMoveTestBuffer, 14).Rets(5), 378 tt.Args(wordMoveTestBuffer, 16).Rets(14), 379 tt.Args(wordMoveTestBuffer, 19).Rets(16), 380 tt.Args(wordMoveTestBuffer, 20).Rets(19), 381 tt.Args(wordMoveTestBuffer, 23).Rets(20), 382 tt.Args(wordMoveTestBuffer, 32).Rets(23), 383 tt.Args(wordMoveTestBuffer, 33).Rets(32), 384 tt.Args(wordMoveTestBuffer, 37).Rets(33), 385 tt.Args(wordMoveTestBuffer, 40).Rets(37), 386 } 387 moveDotRightSmallWordTests = tt.Table{ 388 tt.Args(wordMoveTestBuffer, 0).Rets(3), 389 tt.Args(wordMoveTestBuffer, 1).Rets(3), 390 tt.Args(wordMoveTestBuffer, 2).Rets(3), 391 tt.Args(wordMoveTestBuffer, 3).Rets(5), 392 tt.Args(wordMoveTestBuffer, 5).Rets(14), 393 tt.Args(wordMoveTestBuffer, 14).Rets(16), 394 tt.Args(wordMoveTestBuffer, 16).Rets(19), 395 tt.Args(wordMoveTestBuffer, 19).Rets(20), 396 tt.Args(wordMoveTestBuffer, 20).Rets(23), 397 tt.Args(wordMoveTestBuffer, 23).Rets(32), 398 tt.Args(wordMoveTestBuffer, 32).Rets(33), 399 tt.Args(wordMoveTestBuffer, 33).Rets(37), 400 tt.Args(wordMoveTestBuffer, 37).Rets(40), 401 } 402 403 // alnum-word boundaries: 0 5 16 20 23 33 404 moveDotLeftAlnumWordTests = tt.Table{ 405 tt.Args(wordMoveTestBuffer, 0).Rets(0), 406 tt.Args(wordMoveTestBuffer, 1).Rets(0), 407 tt.Args(wordMoveTestBuffer, 2).Rets(0), 408 tt.Args(wordMoveTestBuffer, 3).Rets(0), 409 tt.Args(wordMoveTestBuffer, 4).Rets(0), 410 tt.Args(wordMoveTestBuffer, 5).Rets(0), 411 tt.Args(wordMoveTestBuffer, 6).Rets(5), 412 tt.Args(wordMoveTestBuffer, 16).Rets(5), 413 tt.Args(wordMoveTestBuffer, 20).Rets(16), 414 tt.Args(wordMoveTestBuffer, 23).Rets(20), 415 tt.Args(wordMoveTestBuffer, 33).Rets(23), 416 tt.Args(wordMoveTestBuffer, 40).Rets(33), 417 } 418 moveDotRightAlnumWordTests = tt.Table{ 419 tt.Args(wordMoveTestBuffer, 0).Rets(5), 420 tt.Args(wordMoveTestBuffer, 1).Rets(5), 421 tt.Args(wordMoveTestBuffer, 2).Rets(5), 422 tt.Args(wordMoveTestBuffer, 3).Rets(5), 423 tt.Args(wordMoveTestBuffer, 4).Rets(5), 424 tt.Args(wordMoveTestBuffer, 5).Rets(16), 425 tt.Args(wordMoveTestBuffer, 16).Rets(20), 426 tt.Args(wordMoveTestBuffer, 20).Rets(23), 427 tt.Args(wordMoveTestBuffer, 23).Rets(33), 428 tt.Args(wordMoveTestBuffer, 33).Rets(40), 429 } 430 ) 431 432 func TestMoveDotWord(t *testing.T) { 433 tt.Test(t, tt.Fn("moveDotLeftWord", moveDotLeftWord), moveDotLeftWordTests) 434 tt.Test(t, tt.Fn("moveDotRightWord", moveDotRightWord), moveDotRightWordTests) 435 } 436 437 func TestMoveDotSmallWord(t *testing.T) { 438 tt.Test(t, 439 tt.Fn("moveDotLeftSmallWord", moveDotLeftSmallWord), 440 moveDotLeftSmallWordTests, 441 ) 442 tt.Test(t, 443 tt.Fn("moveDotRightSmallWord", moveDotRightSmallWord), 444 moveDotRightSmallWordTests, 445 ) 446 } 447 448 func TestMoveDotAlnumWord(t *testing.T) { 449 tt.Test(t, 450 tt.Fn("moveDotLeftAlnumWord", moveDotLeftAlnumWord), 451 moveDotLeftAlnumWordTests, 452 ) 453 tt.Test(t, 454 tt.Fn("moveDotRightAlnumWord", moveDotRightAlnumWord), 455 moveDotRightAlnumWordTests, 456 ) 457 }