mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-05 03:17:50 +08:00
Use consistent pattern for mutating variables in tests.
- Use testutil.Set. - Only export such variables to tests.
This commit is contained in:
parent
94e43cd2fd
commit
6723b9a226
|
@ -22,9 +22,8 @@ type cmdRegion struct {
|
||||||
cmd string
|
cmd string
|
||||||
}
|
}
|
||||||
|
|
||||||
// MaxBlockForLate specifies the maximum wait time to block for late results.
|
// Maximum wait time to block for late results. Can be changed for test cases.
|
||||||
// It can be changed for test cases.
|
var maxBlockForLate = 10 * time.Millisecond
|
||||||
var MaxBlockForLate = 10 * time.Millisecond
|
|
||||||
|
|
||||||
// Highlights a piece of Elvish code.
|
// Highlights a piece of Elvish code.
|
||||||
func highlight(code string, cfg Config, lateCb func(ui.Text)) (ui.Text, []error) {
|
func highlight(code string, cfg Config, lateCb func(ui.Text)) (ui.Text, []error) {
|
||||||
|
@ -117,7 +116,7 @@ func highlight(code string, cfg Config, lateCb func(ui.Text)) (ui.Text, []error)
|
||||||
select {
|
select {
|
||||||
case late := <-lateCh:
|
case late := <-lateCh:
|
||||||
return late, errors
|
return late, errors
|
||||||
case <-time.After(MaxBlockForLate):
|
case <-time.After(maxBlockForLate):
|
||||||
go func() {
|
go func() {
|
||||||
lateCb(<-lateCh)
|
lateCb(<-lateCh)
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -24,7 +24,7 @@ var styles = ui.RuneStylesheet{
|
||||||
|
|
||||||
func TestHighlighter_HighlightRegions(t *testing.T) {
|
func TestHighlighter_HighlightRegions(t *testing.T) {
|
||||||
// Force commands to be delivered synchronously.
|
// Force commands to be delivered synchronously.
|
||||||
MaxBlockForLate = testutil.Scaled(100 * time.Millisecond)
|
testutil.Set(t, &maxBlockForLate, testutil.Scaled(100*time.Millisecond))
|
||||||
hl := NewHighlighter(Config{
|
hl := NewHighlighter(Config{
|
||||||
HasCommand: func(name string) bool { return name == "ls" },
|
HasCommand: func(name string) bool { return name == "ls" },
|
||||||
})
|
})
|
||||||
|
@ -127,7 +127,7 @@ func testThat(t *testing.T, hl *Highlighter, c c) {
|
||||||
func TestHighlighter_HasCommand_LateResult_Async(t *testing.T) {
|
func TestHighlighter_HasCommand_LateResult_Async(t *testing.T) {
|
||||||
// When the HasCommand callback takes longer than maxBlockForLate, late
|
// When the HasCommand callback takes longer than maxBlockForLate, late
|
||||||
// results are delivered asynchronously.
|
// results are delivered asynchronously.
|
||||||
MaxBlockForLate = testutil.Scaled(time.Millisecond)
|
testutil.Set(t, &maxBlockForLate, testutil.Scaled(time.Millisecond))
|
||||||
hl := NewHighlighter(Config{
|
hl := NewHighlighter(Config{
|
||||||
// HasCommand is slow and only recognizes "ls".
|
// HasCommand is slow and only recognizes "ls".
|
||||||
HasCommand: func(cmd string) bool {
|
HasCommand: func(cmd string) bool {
|
||||||
|
@ -150,7 +150,7 @@ func TestHighlighter_HasCommand_LateResult_Async(t *testing.T) {
|
||||||
func TestHighlighter_HasCommand_LateResult_Sync(t *testing.T) {
|
func TestHighlighter_HasCommand_LateResult_Sync(t *testing.T) {
|
||||||
// When the HasCommand callback takes shorter than maxBlockForLate, late
|
// When the HasCommand callback takes shorter than maxBlockForLate, late
|
||||||
// results are delivered asynchronously.
|
// results are delivered asynchronously.
|
||||||
MaxBlockForLate = testutil.Scaled(100 * time.Millisecond)
|
testutil.Set(t, &maxBlockForLate, testutil.Scaled(100*time.Millisecond))
|
||||||
hl := NewHighlighter(Config{
|
hl := NewHighlighter(Config{
|
||||||
// HasCommand is fast and only recognizes "ls".
|
// HasCommand is fast and only recognizes "ls".
|
||||||
HasCommand: func(cmd string) bool {
|
HasCommand: func(cmd string) bool {
|
||||||
|
@ -175,7 +175,7 @@ func TestHighlighter_HasCommand_LateResultOutOfOrder(t *testing.T) {
|
||||||
// "ls" and is dropped.
|
// "ls" and is dropped.
|
||||||
|
|
||||||
// Make sure that the HasCommand callback takes longer than maxBlockForLate.
|
// Make sure that the HasCommand callback takes longer than maxBlockForLate.
|
||||||
MaxBlockForLate = testutil.Scaled(time.Millisecond)
|
testutil.Set(t, &maxBlockForLate, testutil.Scaled(time.Millisecond))
|
||||||
|
|
||||||
hlSecond := make(chan struct{})
|
hlSecond := make(chan struct{})
|
||||||
hl := NewHighlighter(Config{
|
hl := NewHighlighter(Config{
|
||||||
|
|
|
@ -409,15 +409,9 @@ func deprecate(fm *Frame, msg string) {
|
||||||
fm.Deprecate(msg, ctx, 0)
|
fm.Deprecate(msg, ctx, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TimeAfter is used by the sleep command to obtain a channel that is delivered
|
// Reference to time.After, can be mutated for testing. Takes an additional
|
||||||
// a value after the specified time.
|
// Frame argument to allow inspection of the value of d in tests.
|
||||||
//
|
var timeAfter = func(fm *Frame, d time.Duration) <-chan time.Time { return time.After(d) }
|
||||||
// It is a variable to allow for unit tests to efficiently test the behavior of
|
|
||||||
// the `sleep` command, both by eliminating an actual sleep and verifying the
|
|
||||||
// duration was properly parsed.
|
|
||||||
var TimeAfter = func(fm *Frame, d time.Duration) <-chan time.Time {
|
|
||||||
return time.After(d)
|
|
||||||
}
|
|
||||||
|
|
||||||
//elvdoc:fn sleep
|
//elvdoc:fn sleep
|
||||||
//
|
//
|
||||||
|
@ -486,7 +480,7 @@ func sleep(fm *Frame, duration any) error {
|
||||||
select {
|
select {
|
||||||
case <-fm.Interrupts():
|
case <-fm.Interrupts():
|
||||||
return ErrInterrupted
|
return ErrInterrupted
|
||||||
case <-TimeAfter(fm, d):
|
case <-timeAfter(fm, d):
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,13 +108,13 @@ func TestUseMod(t *testing.T) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func timeAfterMock(fm *Frame, d time.Duration) <-chan time.Time {
|
|
||||||
fm.ValueOutput().Put(d) // report to the test framework the duration we received
|
|
||||||
return time.After(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSleep(t *testing.T) {
|
func TestSleep(t *testing.T) {
|
||||||
TimeAfter = timeAfterMock
|
testutil.Set(t, TimeAfter,
|
||||||
|
func(fm *Frame, d time.Duration) <-chan time.Time {
|
||||||
|
fm.ValueOutput().Put(d)
|
||||||
|
return time.After(0)
|
||||||
|
})
|
||||||
|
|
||||||
Test(t,
|
Test(t,
|
||||||
That(`sleep 0`).Puts(0*time.Second),
|
That(`sleep 0`).Puts(0*time.Second),
|
||||||
That(`sleep 1`).Puts(1*time.Second),
|
That(`sleep 1`).Puts(1*time.Second),
|
||||||
|
|
|
@ -7,32 +7,21 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
. "src.elv.sh/pkg/eval"
|
. "src.elv.sh/pkg/eval"
|
||||||
"src.elv.sh/pkg/testutil"
|
"src.elv.sh/pkg/testutil"
|
||||||
|
|
||||||
. "src.elv.sh/pkg/eval/evaltest"
|
. "src.elv.sh/pkg/eval/evaltest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func interruptedTimeAfterMock(fm *Frame, d time.Duration) <-chan time.Time {
|
func TestSleep_Interrupt(t *testing.T) {
|
||||||
if d == time.Second {
|
testutil.Set(t, TimeAfter,
|
||||||
// Special-case intended to verity that a sleep can be interrupted.
|
func(fm *Frame, d time.Duration) <-chan time.Time {
|
||||||
go func() {
|
go unix.Kill(os.Getpid(), unix.SIGINT)
|
||||||
// Wait a little bit to ensure that the control flow in the "sleep"
|
return time.After(d)
|
||||||
// function is in the select block when the interrupt is sent.
|
})
|
||||||
time.Sleep(testutil.Scaled(time.Millisecond))
|
|
||||||
p, _ := os.FindProcess(os.Getpid())
|
|
||||||
p.Signal(os.Interrupt)
|
|
||||||
}()
|
|
||||||
return time.After(1 * time.Second)
|
|
||||||
}
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInterruptedSleep(t *testing.T) {
|
|
||||||
TimeAfter = interruptedTimeAfterMock
|
|
||||||
Test(t,
|
Test(t,
|
||||||
// Special-case that should result in the sleep being interrupted. See
|
|
||||||
// timeAfterMock above.
|
|
||||||
That(`sleep 1s`).Throws(ErrInterrupted, "sleep 1s"),
|
That(`sleep 1s`).Throws(ErrInterrupted, "sleep 1s"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,13 @@ type pwdVar struct {
|
||||||
|
|
||||||
var _ vars.Var = pwdVar{}
|
var _ vars.Var = pwdVar{}
|
||||||
|
|
||||||
// Getwd allows for unit test error injection.
|
// Can be mutated in tests.
|
||||||
var Getwd func() (string, error) = os.Getwd
|
var getwd func() (string, error) = os.Getwd
|
||||||
|
|
||||||
// Get returns the current working directory. It returns "/unknown/pwd" when
|
// Get returns the current working directory. It returns "/unknown/pwd" when
|
||||||
// it cannot be determined.
|
// it cannot be determined.
|
||||||
func (pwdVar) Get() any {
|
func (pwdVar) Get() any {
|
||||||
pwd, err := Getwd()
|
pwd, err := getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This should really use the path separator appropriate for the
|
// This should really use the path separator appropriate for the
|
||||||
// platform but in practice this hardcoded string works fine. Both
|
// platform but in practice this hardcoded string works fine. Both
|
||||||
|
|
|
@ -52,15 +52,7 @@ func TestBuiltinPwd(t *testing.T) {
|
||||||
|
|
||||||
// Verify the behavior when the CWD cannot be determined.
|
// Verify the behavior when the CWD cannot be determined.
|
||||||
func TestBuiltinPwd_GetwdError(t *testing.T) {
|
func TestBuiltinPwd_GetwdError(t *testing.T) {
|
||||||
origGetwd := Getwd
|
testutil.Set(t, Getwd, func() (string, error) { return "", errors.New("cwd unknown") })
|
||||||
Getwd = mockGetwdWithError
|
|
||||||
defer func() { Getwd = origGetwd }()
|
|
||||||
|
|
||||||
Test(t,
|
Test(t, That(`put $pwd`).Puts("/unknown/pwd"))
|
||||||
That(`put $pwd`).Puts("/unknown/pwd"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func mockGetwdWithError() (string, error) {
|
|
||||||
return "", errors.New("cwd unknown")
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
package eval
|
package eval
|
||||||
|
|
||||||
var GetHome = &getHome
|
// Pointers to functions that can be mutated for testing.
|
||||||
|
var (
|
||||||
|
GetHome = &getHome
|
||||||
|
Getwd = &getwd
|
||||||
|
TimeAfter = &timeAfter
|
||||||
|
)
|
||||||
|
|
|
@ -9,9 +9,6 @@ import (
|
||||||
"src.elv.sh/pkg/env"
|
"src.elv.sh/pkg/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CurrentUser allows for unit test error injection.
|
|
||||||
var CurrentUser func() (*user.User, error) = user.Current
|
|
||||||
|
|
||||||
// GetHome finds the home directory of a specified user. When given an empty
|
// GetHome finds the home directory of a specified user. When given an empty
|
||||||
// string, it finds the home directory of the current user.
|
// string, it finds the home directory of the current user.
|
||||||
func GetHome(uname string) (string, error) {
|
func GetHome(uname string) (string, error) {
|
||||||
|
@ -28,7 +25,7 @@ func GetHome(uname string) (string, error) {
|
||||||
var u *user.User
|
var u *user.User
|
||||||
var err error
|
var err error
|
||||||
if uname == "" {
|
if uname == "" {
|
||||||
u, err = CurrentUser()
|
u, err = user.Current()
|
||||||
} else {
|
} else {
|
||||||
u, err = user.Lookup(uname)
|
u, err = user.Lookup(uname)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user