builtin_fn_misc_test.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. package eval_test
  2. import (
  3. "os"
  4. "testing"
  5. "time"
  6. "src.elv.sh/pkg/diag"
  7. . "src.elv.sh/pkg/eval"
  8. "src.elv.sh/pkg/must"
  9. "src.elv.sh/pkg/parse"
  10. "src.elv.sh/pkg/eval/errs"
  11. . "src.elv.sh/pkg/eval/evaltest"
  12. "src.elv.sh/pkg/eval/vals"
  13. "src.elv.sh/pkg/testutil"
  14. )
  15. func TestKindOf(t *testing.T) {
  16. Test(t,
  17. That("kind-of a []").Puts("string", "list"),
  18. thatOutputErrorIsBubbled("kind-of a"),
  19. )
  20. }
  21. func TestConstantly(t *testing.T) {
  22. Test(t,
  23. That(`var f = (constantly foo); $f; $f`).Puts("foo", "foo"),
  24. thatOutputErrorIsBubbled("(constantly foo)"),
  25. )
  26. }
  27. func TestCallCommand(t *testing.T) {
  28. Test(t,
  29. That(`call {|arg &opt=v| put $arg $opt } [foo] [&opt=bar]`).
  30. Puts("foo", "bar"),
  31. That(`call { } [foo] [&[]=bar]`).
  32. Throws(errs.BadValue{What: "option key", Valid: "string", Actual: "list"}),
  33. )
  34. }
  35. func TestEval(t *testing.T) {
  36. Test(t,
  37. That("eval 'put x'").Puts("x"),
  38. // Using variable from the local scope.
  39. That("var x = foo; eval 'put $x'").Puts("foo"),
  40. // Setting a variable in the local scope.
  41. That("var x = foo; eval 'set x = bar'; put $x").Puts("bar"),
  42. // Using variable from the upvalue scope.
  43. That("var x = foo; { nop $x; eval 'put $x' }").Puts("foo"),
  44. // Specifying a namespace.
  45. That("var n = (ns [&x=foo]); eval 'put $x' &ns=$n").Puts("foo"),
  46. // Altering variables in the specified namespace.
  47. That("var n = (ns [&x=foo]); eval 'set x = bar' &ns=$n; put $n[x]").Puts("bar"),
  48. // Newly created variables do not appear in the local namespace.
  49. That("eval 'x = foo'; put $x").DoesNotCompile(),
  50. // Newly created variables do not alter the specified namespace, either.
  51. That("var n = (ns [&]); eval &ns=$n 'var x = foo'; put $n[x]").
  52. Throws(vals.NoSuchKey("x"), "$n[x]"),
  53. // However, newly created variable can be accessed in the final
  54. // namespace using &on-end.
  55. That("eval &on-end={|n| put $n[x] } 'var x = foo'").Puts("foo"),
  56. // Parse error.
  57. That("eval '['").Throws(ErrorWithType(&parse.Error{})),
  58. // Compilation error.
  59. That("eval 'put $x'").Throws(ErrorWithType(&diag.Error{})),
  60. // Exception.
  61. That("eval 'fail x'").Throws(FailError{"x"}),
  62. )
  63. }
  64. func TestDeprecate(t *testing.T) {
  65. Test(t,
  66. That("deprecate msg").PrintsStderrWith("msg"),
  67. // Different call sites trigger multiple deprecation messages
  68. That("fn f { deprecate msg }", "f 2>"+os.DevNull, "f").
  69. PrintsStderrWith("msg"),
  70. // The same call site only triggers the message once
  71. That("fn f { deprecate msg}", "fn g { f }", "g 2>"+os.DevNull, "g 2>&1").
  72. DoesNothing(),
  73. )
  74. }
  75. func TestTime(t *testing.T) {
  76. Test(t,
  77. // Since runtime duration is non-deterministic, we only have some sanity
  78. // checks here.
  79. That("time { echo foo } | var a _ = (all)", "put $a").Puts("foo"),
  80. That("var duration = ''",
  81. "time &on-end={|x| set duration = $x } { echo foo } | var out = (all)",
  82. "put $out", "kind-of $duration").Puts("foo", "number"),
  83. That("time { fail body } | nop (all)").Throws(FailError{"body"}),
  84. That("time &on-end={|_| fail on-end } { }").Throws(
  85. FailError{"on-end"}),
  86. That("time &on-end={|_| fail on-end } { fail body }").Throws(
  87. FailError{"body"}),
  88. thatOutputErrorIsBubbled("time { }"),
  89. )
  90. }
  91. func TestBenchmark(t *testing.T) {
  92. var ticks []int64
  93. testutil.Set(t, &TimeNow, func() time.Time {
  94. if len(ticks) == 0 {
  95. panic("mock TimeNow called more than len(ticks)")
  96. }
  97. v := ticks[0]
  98. ticks = ticks[1:]
  99. return time.Unix(v, 0)
  100. })
  101. setupTicks := func(ts ...int64) func(*Evaler) {
  102. return func(_ *Evaler) { ticks = ts }
  103. }
  104. Test(t,
  105. // Default output
  106. That("benchmark &min-runs=2 &min-time=2s { }").
  107. WithSetup(setupTicks(0, 1, 1, 3)).
  108. Prints("1.5s ± 500ms (min 1s, max 2s, 2 runs)\n"),
  109. // &on-end callback
  110. That(
  111. "var f = {|m| put $m[avg] $m[stddev] $m[min] $m[max] $m[runs]}",
  112. "benchmark &min-runs=2 &min-time=2s &on-end=$f { }").
  113. WithSetup(setupTicks(0, 1, 1, 3)).
  114. Puts(1.5, 0.5, 1.0, 2.0, 2),
  115. // &min-runs determining number of runs
  116. That("benchmark &min-runs=4 &min-time=0s &on-end={|m| put $m[runs]} { }").
  117. WithSetup(setupTicks(0, 1, 1, 3, 3, 4, 4, 6)).
  118. Puts(4),
  119. // &min-time determining number of runs
  120. That("benchmark &min-runs=0 &min-time=10s &on-end={|m| put $m[runs]} { }").
  121. WithSetup(setupTicks(0, 1, 1, 6, 6, 11)).
  122. Puts(3),
  123. // &on-run-end
  124. That("benchmark &min-runs=3 &on-run-end=$put~ &on-end={|m| } { }").
  125. WithSetup(setupTicks(0, 1, 1, 3, 3, 4)).
  126. Puts(1.0, 2.0, 1.0),
  127. // $callable throws exception
  128. That(
  129. "var i = 0",
  130. "benchmark { set i = (+ $i 1); if (== $i 3) { fail failure } }").
  131. WithSetup(setupTicks(0, 1, 1, 3, 3)).
  132. Throws(FailError{"failure"}).
  133. Prints("1.5s ± 500ms (min 1s, max 2s, 2 runs)\n"),
  134. // $callable throws exception on first run
  135. That("benchmark { fail failure }").
  136. WithSetup(setupTicks(0)).
  137. Throws(FailError{"failure"}).
  138. Prints( /* nothing */ ""),
  139. That("benchmark &on-end=$put~ { fail failure }").
  140. WithSetup(setupTicks(0)).
  141. Throws(FailError{"failure"}).
  142. Puts( /* nothing */ ),
  143. // &on-run-end throws exception
  144. That("benchmark &on-run-end={|_| fail failure } { }").
  145. WithSetup(setupTicks(0, 1)).
  146. Throws(FailError{"failure"}).
  147. Prints("1s ± 0s (min 1s, max 1s, 1 runs)\n"),
  148. // &on-run throws exception
  149. That("benchmark &min-runs=2 &min-time=0s &on-end={|_| fail failure } { }").
  150. WithSetup(setupTicks(0, 1, 1, 3)).
  151. Throws(FailError{"failure"}),
  152. // Option errors
  153. That("benchmark &min-runs=-1 { }").
  154. Throws(errs.BadValue{What: "min-runs option",
  155. Valid: "non-negative integer", Actual: "-1"}),
  156. That("benchmark &min-time=abc { }").
  157. Throws(errs.BadValue{What: "min-time option",
  158. Valid: "duration string", Actual: "abc"}),
  159. That("benchmark &min-time=-1s { }").
  160. Throws(errs.BadValue{What: "min-time option",
  161. Valid: "non-negative duration", Actual: "-1s"}),
  162. // Test that output error is bubbled. We can't use
  163. // testOutputErrorIsBubbled here, since the mock TimeNow requires setup.
  164. That("benchmark &min-runs=0 &min-time=0s { } >&-").
  165. WithSetup(setupTicks(0, 1)).
  166. Throws(os.ErrInvalid),
  167. That("benchmark &min-runs=0 &min-time=0s &on-end=$put~ { } >&-").
  168. WithSetup(setupTicks(0, 1)).
  169. Throws(ErrPortDoesNotSupportValueOutput),
  170. )
  171. }
  172. func TestUseMod(t *testing.T) {
  173. testutil.InTempDir(t)
  174. must.WriteFile("mod.elv", "var x = value")
  175. Test(t,
  176. That("put (use-mod ./mod)[x]").Puts("value"),
  177. )
  178. }
  179. func TestSleep(t *testing.T) {
  180. testutil.Set(t, TimeAfter,
  181. func(fm *Frame, d time.Duration) <-chan time.Time {
  182. fm.ValueOutput().Put(d)
  183. return time.After(0)
  184. })
  185. Test(t,
  186. That(`sleep 0`).Puts(0*time.Second),
  187. That(`sleep 1`).Puts(1*time.Second),
  188. That(`sleep 1.3s`).Puts(1300*time.Millisecond),
  189. That(`sleep 0.1`).Puts(100*time.Millisecond),
  190. That(`sleep 0.1ms`).Puts(100*time.Microsecond),
  191. That(`sleep 3h5m7s`).Puts((3*3600+5*60+7)*time.Second),
  192. That(`sleep 1x`).Throws(ErrInvalidSleepDuration, "sleep 1x"),
  193. That(`sleep -7`).Throws(ErrNegativeSleepDuration, "sleep -7"),
  194. That(`sleep -3h`).Throws(ErrNegativeSleepDuration, "sleep -3h"),
  195. That(`sleep 1/2`).Puts(time.Second/2), // rational number string
  196. // Verify the correct behavior if a numeric type, rather than a string, is passed to the
  197. // command.
  198. That(`sleep (num 42)`).Puts(42*time.Second),
  199. That(`sleep (num 0)`).Puts(0*time.Second),
  200. That(`sleep (num 1.7)`).Puts(1700*time.Millisecond),
  201. That(`sleep (num -7)`).Throws(ErrNegativeSleepDuration, "sleep (num -7)"),
  202. // An invalid argument type should raise an exception.
  203. That(`sleep [1]`).Throws(ErrInvalidSleepDuration, "sleep [1]"),
  204. )
  205. }
  206. func TestResolve(t *testing.T) {
  207. libdir := testutil.InTempDir(t)
  208. must.WriteFile("mod.elv", "fn func { }")
  209. TestWithSetup(t, func(ev *Evaler) { ev.LibDirs = []string{libdir} },
  210. That("resolve for").Puts("special"),
  211. That("resolve put").Puts("$put~"),
  212. That("fn f { }; resolve f").Puts("$f~"),
  213. That("use mod; resolve mod:func").Puts("$mod:func~"),
  214. That("resolve cat").Puts("(external cat)"),
  215. That(`resolve external`).Puts("$external~"),
  216. )
  217. }