elvish/pkg/shell/interact_test.go
2024-02-10 16:07:04 +00:00

138 lines
3.8 KiB
Go

package shell
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"testing"
"time"
"src.elv.sh/pkg/daemon"
"src.elv.sh/pkg/daemon/daemondefs"
"src.elv.sh/pkg/env"
"src.elv.sh/pkg/must"
. "src.elv.sh/pkg/prog/progtest"
"src.elv.sh/pkg/testutil"
)
func TestInteract_Eval(t *testing.T) {
setupCleanHomePaths(t)
testutil.InTempDir(t)
must.WriteFile("rc.elv", "echo hello from rc.elv")
must.WriteFile("rc-dnc.elv", "echo $a")
must.WriteFile("rc-fail.elv", "fail bad")
Test(t, &Program{},
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(),
)
}
func TestInteract_RCPath_XDG_CONFIG_HOME(t *testing.T) {
setupCleanHomePaths(t)
xdgConfigHome := testutil.Setenv(t, env.XDG_CONFIG_HOME, testutil.TempDir(t))
must.WriteFile(
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"),
)
}
func TestInteract_ConnectsToDaemon(t *testing.T) {
sockPath := startDaemon(t)
Test(t, &Program{ActivateDaemon: fakeActivate(sockPath)},
thatElvishInteract().
WithStdin("use daemon; echo $daemon:pid\n").
WritesStdout(fmt.Sprintln(os.Getpid())),
)
}
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"),
)
}
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()
// Run the daemon in the same process for simplicity.
dir := testutil.TempDir(t)
sockPath := filepath.Join(dir, "sock")
sigCh := make(chan os.Signal)
readyCh := make(chan struct{})
daemonDone := make(chan struct{})
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)
select {
case <-daemonDone:
case <-time.After(testutil.Scaled(2 * time.Second)):
t.Errorf("timed out waiting for daemon to quit")
}
})
select {
case <-readyCh:
// Do nothing
case <-time.After(testutil.Scaled(2 * time.Second)):
t.Fatalf("timed out waiting for daemon to start")
}
return sockPath
}
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
}
}