mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-13 01:40:31 +08:00
87656c99fa
The `AnyError` placeholder error can cause tests to succeed for errors other than what was expected. That is, the use of `AnyError` can mask bugs in a unit test. So replace it with the specific error, or error type, the test expects to be raised. This does not remove the anyError structure because it is used in the TestCase.DoesNotCompile() method. To keep the size of this change as small as possible I want to defer updating that use to a separate change. However, remove the public AnyError var so future test writers don't attempt to use it.
147 lines
4.6 KiB
Go
147 lines
4.6 KiB
Go
package eval_test
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"src.elv.sh/pkg/diag"
|
|
. "src.elv.sh/pkg/eval"
|
|
"src.elv.sh/pkg/parse"
|
|
|
|
. "src.elv.sh/pkg/eval/evaltest"
|
|
"src.elv.sh/pkg/eval/vals"
|
|
"src.elv.sh/pkg/testutil"
|
|
)
|
|
|
|
func TestKindOf(t *testing.T) {
|
|
Test(t,
|
|
That("kind-of a []").Puts("string", "list"),
|
|
thatOutputErrorIsBubbled("kind-of a"),
|
|
)
|
|
}
|
|
|
|
func TestConstantly(t *testing.T) {
|
|
Test(t,
|
|
That(`f = (constantly foo); $f; $f`).Puts("foo", "foo"),
|
|
thatOutputErrorIsBubbled("(constantly foo)"),
|
|
)
|
|
}
|
|
|
|
func TestEval(t *testing.T) {
|
|
Test(t,
|
|
That("eval 'put x'").Puts("x"),
|
|
// Using variable from the local scope.
|
|
That("x = foo; eval 'put $x'").Puts("foo"),
|
|
// Setting a variable in the local scope.
|
|
That("x = foo; eval 'x = bar'; put $x").Puts("bar"),
|
|
// Using variable from the upvalue scope.
|
|
That("x = foo; { nop $x; eval 'put $x' }").Puts("foo"),
|
|
// Specifying a namespace.
|
|
That("n = (ns [&x=foo]); eval 'put $x' &ns=$n").Puts("foo"),
|
|
// Altering variables in the specified namespace.
|
|
That("n = (ns [&x=foo]); eval 'x = bar' &ns=$n; put $n[x]").Puts("bar"),
|
|
// Newly created variables do not appear in the local namespace.
|
|
That("eval 'x = foo'; put $x").DoesNotCompile(),
|
|
// Newly created variables do not alter the specified namespace, either.
|
|
That("n = (ns [&]); eval &ns=$n 'x = foo'; put $n[x]").
|
|
Throws(vals.NoSuchKey("x"), "$n[x]"),
|
|
// However, newly created variable can be accessed in the final
|
|
// namespace using &on-end.
|
|
That("eval &on-end={|n| put $n[x] } 'x = foo'").Puts("foo"),
|
|
// Parse error.
|
|
That("eval '['").Throws(ErrorWithType(&parse.Error{})),
|
|
// Compilation error.
|
|
That("eval 'put $x'").Throws(ErrorWithType(&diag.Error{})),
|
|
// Exception.
|
|
That("eval 'fail x'").Throws(FailError{"x"}),
|
|
)
|
|
}
|
|
|
|
func TestDeprecate(t *testing.T) {
|
|
Test(t,
|
|
That("deprecate msg").PrintsStderrWith("msg"),
|
|
// Different call sites trigger multiple deprecation messages
|
|
That("fn f { deprecate msg }", "f 2>"+os.DevNull, "f").
|
|
PrintsStderrWith("msg"),
|
|
// The same call site only triggers the message once
|
|
That("fn f { deprecate msg}", "fn g { f }", "g 2>"+os.DevNull, "g 2>&1").
|
|
DoesNothing(),
|
|
)
|
|
}
|
|
|
|
func TestTime(t *testing.T) {
|
|
Test(t,
|
|
// Since runtime duration is non-deterministic, we only have some sanity
|
|
// checks here.
|
|
That("time { echo foo } | a _ = (all)", "put $a").Puts("foo"),
|
|
That("duration = ''",
|
|
"time &on-end={|x| duration = $x } { echo foo } | out = (all)",
|
|
"put $out", "kind-of $duration").Puts("foo", "number"),
|
|
That("time { fail body } | nop (all)").Throws(FailError{"body"}),
|
|
That("time &on-end={|_| fail on-end } { }").Throws(
|
|
FailError{"on-end"}),
|
|
|
|
That("time &on-end={|_| fail on-end } { fail body }").Throws(
|
|
FailError{"body"}),
|
|
|
|
thatOutputErrorIsBubbled("time { }"),
|
|
)
|
|
}
|
|
|
|
func TestUseMod(t *testing.T) {
|
|
testutil.InTempDir(t)
|
|
testutil.MustWriteFile("mod.elv", "x = value")
|
|
|
|
Test(t,
|
|
That("put (use-mod ./mod)[x]").Puts("value"),
|
|
)
|
|
}
|
|
|
|
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) {
|
|
TimeAfter = timeAfterMock
|
|
Test(t,
|
|
That(`sleep 0`).Puts(0*time.Second),
|
|
That(`sleep 1`).Puts(1*time.Second),
|
|
That(`sleep 1.3s`).Puts(1300*time.Millisecond),
|
|
That(`sleep 0.1`).Puts(100*time.Millisecond),
|
|
That(`sleep 0.1ms`).Puts(100*time.Microsecond),
|
|
That(`sleep 3h5m7s`).Puts((3*3600+5*60+7)*time.Second),
|
|
|
|
That(`sleep 1x`).Throws(ErrInvalidSleepDuration, "sleep 1x"),
|
|
That(`sleep -7`).Throws(ErrNegativeSleepDuration, "sleep -7"),
|
|
That(`sleep -3h`).Throws(ErrNegativeSleepDuration, "sleep -3h"),
|
|
|
|
That(`sleep 1/2`).Puts(time.Second/2), // rational number string
|
|
|
|
// Verify the correct behavior if a numeric type, rather than a string, is passed to the
|
|
// command.
|
|
That(`sleep (num 42)`).Puts(42*time.Second),
|
|
That(`sleep (float64 0)`).Puts(0*time.Second),
|
|
That(`sleep (float64 1.7)`).Puts(1700*time.Millisecond),
|
|
That(`sleep (float64 -7)`).Throws(ErrNegativeSleepDuration, "sleep (float64 -7)"),
|
|
|
|
// An invalid argument type should raise an exception.
|
|
That(`sleep [1]`).Throws(ErrInvalidSleepDuration, "sleep [1]"),
|
|
)
|
|
}
|
|
|
|
func TestResolve(t *testing.T) {
|
|
libdir := testutil.InTempDir(t)
|
|
testutil.MustWriteFile("mod.elv", "fn func { }")
|
|
|
|
TestWithSetup(t, func(ev *Evaler) { ev.LibDirs = []string{libdir} },
|
|
That("resolve for").Puts("special"),
|
|
That("resolve put").Puts("$put~"),
|
|
That("fn f { }; resolve f").Puts("$f~"),
|
|
That("use mod; resolve mod:func").Puts("$mod:func~"),
|
|
That("resolve cat").Puts("(external cat)"),
|
|
That(`resolve external`).Puts("$external~"),
|
|
)
|
|
}
|