src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/shell/interact_test.go (about) 1 package shell 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "os" 8 "path/filepath" 9 "testing" 10 "time" 11 12 "src.elv.sh/pkg/daemon" 13 "src.elv.sh/pkg/daemon/daemondefs" 14 "src.elv.sh/pkg/env" 15 "src.elv.sh/pkg/must" 16 . "src.elv.sh/pkg/prog/progtest" 17 "src.elv.sh/pkg/testutil" 18 ) 19 20 func TestInteract_Eval(t *testing.T) { 21 setupCleanHomePaths(t) 22 testutil.InTempDir(t) 23 must.WriteFile("rc.elv", "echo hello from rc.elv") 24 must.WriteFile("rc-dnc.elv", "echo $a") 25 must.WriteFile("rc-fail.elv", "fail bad") 26 27 Test(t, &Program{}, 28 thatElvishInteract().WithStdin("echo hello\n").WritesStdout("hello\n"), 29 thatElvishInteract().WithStdin("fail mock\n").WritesStderrContaining("fail mock"), 30 31 thatElvishInteract("-rc", "rc.elv").WritesStdout("hello from rc.elv\n"), 32 // rc file does not compile 33 thatElvishInteract("-rc", "rc-dnc.elv"). 34 WritesStderrContaining("variable $a not found"), 35 // rc file throws exception 36 thatElvishInteract("-rc", "rc-fail.elv").WritesStderrContaining("fail bad"), 37 // rc file not existing is OK 38 thatElvishInteract("-rc", "rc-nonexistent.elv").DoesNothing(), 39 ) 40 } 41 42 func TestInteract_RCPath_XDG_CONFIG_HOME(t *testing.T) { 43 setupCleanHomePaths(t) 44 xdgConfigHome := testutil.Setenv(t, env.XDG_CONFIG_HOME, testutil.TempDir(t)) 45 must.WriteFile( 46 filepath.Join(xdgConfigHome, "elvish", "rc.elv"), 47 "echo hello XDG_CONFIG_HOME rc.elv") 48 49 Test(t, &Program{}, 50 thatElvishInteract().WritesStdout("hello XDG_CONFIG_HOME rc.elv\n"), 51 ) 52 } 53 54 func TestInteract_ConnectsToDaemon(t *testing.T) { 55 sockPath := startDaemon(t) 56 57 Test(t, &Program{ActivateDaemon: fakeActivate(sockPath)}, 58 thatElvishInteract(). 59 WithStdin("use daemon; echo $daemon:pid\n"). 60 WritesStdout(fmt.Sprintln(os.Getpid())), 61 ) 62 } 63 64 func TestInteract_DoesNotStoreEmptyCommandInHistory(t *testing.T) { 65 sockPath := startDaemon(t) 66 Test(t, &Program{ActivateDaemon: fakeActivate(sockPath)}, 67 thatElvishInteract(). 68 WithStdin("\n"+"use store; print (store:next-cmd-seq)\n"). 69 WritesStdout("1"), 70 ) 71 } 72 73 func TestInteract_ErrorInActivateDaemon(t *testing.T) { 74 activate := func(io.Writer, *daemondefs.SpawnConfig) (daemondefs.Client, error) { 75 return nil, errors.New("fake error") 76 } 77 Test(t, &Program{ActivateDaemon: activate}, 78 thatElvishInteract(). 79 WritesStderrContaining("Cannot connect to daemon: fake error"), 80 ) 81 } 82 83 func TestInteract_DBPath_XDG_STATE_HOME(t *testing.T) { 84 sockPath := startDaemon(t) 85 setupCleanHomePaths(t) 86 xdgStateHome := testutil.Setenv(t, env.XDG_STATE_HOME, t.TempDir()) 87 88 Test(t, &Program{ActivateDaemon: fakeActivate(sockPath)}, 89 thatElvishInteract(). 90 WritesStderrContaining("db requested: "+ 91 filepath.Join(xdgStateHome, "elvish", "db.bolt")), 92 ) 93 } 94 95 func thatElvishInteract(args ...string) Case { 96 return ThatElvish(args...).WritesStderrContaining("") 97 } 98 99 // Starts a daemon, and returns the socket path to connect it with. 100 func startDaemon(t *testing.T) string { 101 t.Helper() 102 // Run the daemon in the same process for simplicity. 103 dir := testutil.TempDir(t) 104 sockPath := filepath.Join(dir, "sock") 105 sigCh := make(chan os.Signal) 106 readyCh := make(chan struct{}) 107 daemonDone := make(chan struct{}) 108 go func() { 109 daemon.Serve(sockPath, filepath.Join(dir, "db.bolt"), 110 daemon.ServeOpts{Ready: readyCh, Signals: sigCh}) 111 close(daemonDone) 112 }() 113 t.Cleanup(func() { 114 t.Helper() 115 close(sigCh) 116 select { 117 case <-daemonDone: 118 case <-time.After(testutil.Scaled(2 * time.Second)): 119 t.Errorf("timed out waiting for daemon to quit") 120 } 121 }) 122 select { 123 case <-readyCh: 124 // Do nothing 125 case <-time.After(testutil.Scaled(2 * time.Second)): 126 t.Fatalf("timed out waiting for daemon to start") 127 } 128 return sockPath 129 } 130 131 func fakeActivate(sockPath string) daemondefs.ActivateFunc { 132 return func(stderr io.Writer, cfg *daemondefs.SpawnConfig) (daemondefs.Client, error) { 133 fmt.Fprintln(stderr, "db requested:", cfg.DbPath) 134 // Always connect to the in-process daemon just started. 135 return daemon.NewClient(sockPath), nil 136 } 137 }