2018-09-29 07:27:54 +08:00
|
|
|
package shell
|
|
|
|
|
|
|
|
import (
|
2022-06-20 04:45:36 +08:00
|
|
|
"errors"
|
2022-06-20 02:08:11 +08:00
|
|
|
"fmt"
|
2022-06-20 03:09:55 +08:00
|
|
|
"io"
|
2022-06-20 02:08:11 +08:00
|
|
|
"os"
|
2021-08-07 04:31:39 +08:00
|
|
|
"path/filepath"
|
2018-09-29 07:27:54 +08:00
|
|
|
"testing"
|
2022-06-20 02:08:11 +08:00
|
|
|
"time"
|
2018-09-29 07:27:54 +08:00
|
|
|
|
2022-06-20 02:08:11 +08:00
|
|
|
"src.elv.sh/pkg/daemon"
|
2022-06-20 03:09:55 +08:00
|
|
|
"src.elv.sh/pkg/daemon/daemondefs"
|
2022-06-12 21:21:57 +08:00
|
|
|
"src.elv.sh/pkg/env"
|
2022-06-21 03:29:14 +08:00
|
|
|
"src.elv.sh/pkg/must"
|
2021-01-27 09:28:38 +08:00
|
|
|
. "src.elv.sh/pkg/prog/progtest"
|
2022-03-26 10:21:06 +08:00
|
|
|
"src.elv.sh/pkg/testutil"
|
2018-09-29 07:27:54 +08:00
|
|
|
)
|
|
|
|
|
2022-06-20 02:08:11 +08:00
|
|
|
func TestInteract_Eval(t *testing.T) {
|
2022-06-08 06:26:14 +08:00
|
|
|
setupCleanHomePaths(t)
|
2022-03-26 10:21:06 +08:00
|
|
|
testutil.InTempDir(t)
|
2022-06-21 03:29:14 +08:00
|
|
|
must.WriteFile("rc.elv", "echo hello from rc.elv")
|
|
|
|
must.WriteFile("rc-dnc.elv", "echo $a")
|
|
|
|
must.WriteFile("rc-fail.elv", "fail bad")
|
2021-09-12 21:04:30 +08:00
|
|
|
|
2022-02-05 22:48:55 +08:00
|
|
|
Test(t, &Program{},
|
2021-09-12 21:04:30 +08:00
|
|
|
thatElvishInteract().WithStdin("echo hello\n").WritesStdout("hello\n"),
|
|
|
|
thatElvishInteract().WithStdin("fail mock\n").WritesStderrContaining("fail mock"),
|
|
|
|
|
|
|
|
thatElvishInteract("-rc", "rc.elv").WritesStdout("hello from rc.elv\n"),
|
|
|
|
// rc file does not compile
|
|
|
|
thatElvishInteract("-rc", "rc-dnc.elv").
|
|
|
|
WritesStderrContaining("variable $a not found"),
|
|
|
|
// rc file throws exception
|
|
|
|
thatElvishInteract("-rc", "rc-fail.elv").WritesStderrContaining("fail bad"),
|
|
|
|
// rc file not existing is OK
|
|
|
|
thatElvishInteract("-rc", "rc-nonexistent.elv").DoesNothing(),
|
|
|
|
)
|
2020-04-17 05:02:05 +08:00
|
|
|
}
|
|
|
|
|
2022-06-12 21:21:57 +08:00
|
|
|
func TestInteract_RCPath_Legacy(t *testing.T) {
|
2022-06-08 06:26:14 +08:00
|
|
|
home := setupCleanHomePaths(t)
|
2022-06-21 03:29:14 +08:00
|
|
|
must.WriteFile(
|
2021-09-12 21:04:30 +08:00
|
|
|
filepath.Join(home, ".elvish", "rc.elv"), "echo hello legacy rc.elv")
|
2020-04-16 18:41:34 +08:00
|
|
|
|
2022-02-05 22:48:55 +08:00
|
|
|
Test(t, &Program{},
|
2022-06-12 21:21:57 +08:00
|
|
|
thatElvishInteract().
|
|
|
|
WritesStdout("hello legacy rc.elv\n").
|
|
|
|
WritesStderrContaining(legacyRcPathWarning),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInteract_RCPath_XDG_CONFIG_HOME(t *testing.T) {
|
|
|
|
setupCleanHomePaths(t)
|
|
|
|
xdgConfigHome := testutil.Setenv(t, env.XDG_CONFIG_HOME, testutil.TempDir(t))
|
2022-06-21 03:29:14 +08:00
|
|
|
must.WriteFile(
|
2022-06-12 21:21:57 +08:00
|
|
|
filepath.Join(xdgConfigHome, "elvish", "rc.elv"),
|
|
|
|
"echo hello XDG_CONFIG_HOME rc.elv")
|
|
|
|
|
|
|
|
Test(t, &Program{},
|
|
|
|
thatElvishInteract().WritesStdout("hello XDG_CONFIG_HOME rc.elv\n"),
|
2021-09-12 21:04:30 +08:00
|
|
|
)
|
2020-04-16 18:41:34 +08:00
|
|
|
}
|
|
|
|
|
2022-06-20 02:08:11 +08:00
|
|
|
func TestInteract_ConnectsToDaemon(t *testing.T) {
|
2022-06-20 03:09:55 +08:00
|
|
|
sockPath := startDaemon(t)
|
2022-06-20 02:08:11 +08:00
|
|
|
|
2022-06-20 03:09:55 +08:00
|
|
|
Test(t, &Program{ActivateDaemon: fakeActivate(sockPath)},
|
|
|
|
thatElvishInteract().
|
|
|
|
WithStdin("use daemon; echo $daemon:pid\n").
|
|
|
|
WritesStdout(fmt.Sprintln(os.Getpid())),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-06-20 04:45:36 +08:00
|
|
|
func TestInteract_DoesNotStoreEmptyCommandInHistory(t *testing.T) {
|
|
|
|
sockPath := startDaemon(t)
|
|
|
|
Test(t, &Program{ActivateDaemon: fakeActivate(sockPath)},
|
|
|
|
thatElvishInteract().
|
|
|
|
WithStdin("\n"+"use store; print (store:next-cmd-seq)\n").
|
|
|
|
WritesStdout("1"),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInteract_ErrorInActivateDaemon(t *testing.T) {
|
|
|
|
activate := func(io.Writer, *daemondefs.SpawnConfig) (daemondefs.Client, error) {
|
|
|
|
return nil, errors.New("fake error")
|
|
|
|
}
|
|
|
|
Test(t, &Program{ActivateDaemon: activate},
|
|
|
|
thatElvishInteract().
|
|
|
|
WritesStderrContaining("Cannot connect to daemon: fake error"),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-06-20 03:09:55 +08:00
|
|
|
func TestInteract_DBPath_Legacy(t *testing.T) {
|
|
|
|
sockPath := startDaemon(t)
|
|
|
|
home := setupCleanHomePaths(t)
|
|
|
|
legacyDBPath := filepath.Join(home, ".elvish", "db")
|
2022-06-21 03:29:14 +08:00
|
|
|
must.WriteFile(legacyDBPath, "")
|
2022-06-20 03:09:55 +08:00
|
|
|
|
|
|
|
Test(t, &Program{ActivateDaemon: fakeActivate(sockPath)},
|
|
|
|
thatElvishInteract().
|
|
|
|
WritesStderrContaining("db requested: "+legacyDBPath),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestInteract_DBPath_XDG_STATE_HOME(t *testing.T) {
|
|
|
|
sockPath := startDaemon(t)
|
|
|
|
setupCleanHomePaths(t)
|
|
|
|
xdgStateHome := testutil.Setenv(t, env.XDG_STATE_HOME, t.TempDir())
|
|
|
|
|
|
|
|
Test(t, &Program{ActivateDaemon: fakeActivate(sockPath)},
|
|
|
|
thatElvishInteract().
|
|
|
|
WritesStderrContaining("db requested: "+
|
|
|
|
filepath.Join(xdgStateHome, "elvish", "db.bolt")),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
func thatElvishInteract(args ...string) Case {
|
|
|
|
return ThatElvish(args...).WritesStderrContaining("")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Starts a daemon, and returns the socket path to connect it with.
|
|
|
|
func startDaemon(t *testing.T) string {
|
|
|
|
t.Helper()
|
2022-06-20 02:08:11 +08:00
|
|
|
// Run the daemon in the same process for simplicity.
|
2022-06-20 04:45:36 +08:00
|
|
|
dir := testutil.TempDir(t)
|
2022-06-20 03:09:55 +08:00
|
|
|
sockPath := filepath.Join(dir, "sock")
|
|
|
|
sigCh := make(chan os.Signal)
|
|
|
|
readyCh := make(chan struct{})
|
2022-06-20 02:08:11 +08:00
|
|
|
daemonDone := make(chan struct{})
|
2022-06-20 03:09:55 +08:00
|
|
|
go func() {
|
|
|
|
daemon.Serve(sockPath, filepath.Join(dir, "db.bolt"),
|
|
|
|
daemon.ServeOpts{Ready: readyCh, Signals: sigCh})
|
|
|
|
close(daemonDone)
|
|
|
|
}()
|
|
|
|
t.Cleanup(func() {
|
|
|
|
t.Helper()
|
|
|
|
close(sigCh)
|
2022-06-20 02:08:11 +08:00
|
|
|
select {
|
|
|
|
case <-daemonDone:
|
|
|
|
case <-time.After(testutil.Scaled(2 * time.Second)):
|
|
|
|
t.Errorf("timed out waiting for daemon to quit")
|
|
|
|
}
|
2022-06-20 03:09:55 +08:00
|
|
|
})
|
2022-06-20 02:08:11 +08:00
|
|
|
select {
|
|
|
|
case <-readyCh:
|
|
|
|
// Do nothing
|
|
|
|
case <-time.After(testutil.Scaled(2 * time.Second)):
|
|
|
|
t.Fatalf("timed out waiting for daemon to start")
|
|
|
|
}
|
2022-06-20 03:09:55 +08:00
|
|
|
return sockPath
|
2022-06-20 02:08:11 +08:00
|
|
|
}
|
|
|
|
|
2022-06-20 03:09:55 +08:00
|
|
|
func fakeActivate(sockPath string) daemondefs.ActivateFunc {
|
|
|
|
return func(stderr io.Writer, cfg *daemondefs.SpawnConfig) (daemondefs.Client, error) {
|
|
|
|
fmt.Fprintln(stderr, "db requested:", cfg.DbPath)
|
|
|
|
// Always connect to the in-process daemon just started.
|
|
|
|
return daemon.NewClient(sockPath), nil
|
|
|
|
}
|
2020-04-16 18:41:34 +08:00
|
|
|
}
|