mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-01 08:42:55 +08:00
Merge pull request #1310 from krader1961/peach-unit-test
Add test coverage of `peach`
This commit is contained in:
commit
45fe3ac2c8
|
@ -2,6 +2,7 @@ package eval
|
|||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"src.elv.sh/pkg/diag"
|
||||
"src.elv.sh/pkg/eval/vals"
|
||||
|
@ -136,10 +137,15 @@ func each(fm *Frame, f Callable, inputs Inputs) error {
|
|||
//elvdoc:fn peach
|
||||
//
|
||||
// ```elvish
|
||||
// peach $f $input-list?
|
||||
// peach $f~ $input-list?
|
||||
// ```
|
||||
//
|
||||
// Call `$f` on all inputs, possibly in parallel.
|
||||
// Call `$f~` on all inputs, possibly in parallel. The exception from a
|
||||
// [`break`](./builtin.html#break) command it will cause `peach` to stop starting new instances of
|
||||
// `$f` with any remaining inputs. Because each instance of `$f~` is being run in parallel it is not
|
||||
// predictable when the early termination will occur or even that it will happen before all the
|
||||
// input has been consumed. The exception from a [`continue`](./builtin.html#continue) command is
|
||||
// ignored.
|
||||
//
|
||||
// Example (your output will differ):
|
||||
//
|
||||
|
@ -151,6 +157,12 @@ func each(fm *Frame, f Callable, inputs Inputs) error {
|
|||
// ▶ 16
|
||||
// ▶ 15
|
||||
// ▶ 14
|
||||
// ~> var tot = 0
|
||||
// ~> range 1 101 |
|
||||
// peach [x]{ if (== 50 $x) { break } else { put $x } } |
|
||||
// each [x]{ set tot = (+ $tot $x) }
|
||||
// ~> put $tot # without the break the total should be (num 5050)
|
||||
// ▶ (num 1603)
|
||||
// ```
|
||||
//
|
||||
// This command is intended for homogeneous processing of possibly unbound data. If
|
||||
|
@ -161,10 +173,11 @@ func each(fm *Frame, f Callable, inputs Inputs) error {
|
|||
|
||||
func peach(fm *Frame, f Callable, inputs Inputs) error {
|
||||
var wg sync.WaitGroup
|
||||
broken := false
|
||||
var broken atomic.Value
|
||||
broken.Store(false)
|
||||
var err error
|
||||
inputs(func(v interface{}) {
|
||||
if broken || err != nil {
|
||||
if broken.Load().(bool) || err != nil {
|
||||
return
|
||||
}
|
||||
wg.Add(1)
|
||||
|
@ -179,9 +192,9 @@ func peach(fm *Frame, f Callable, inputs Inputs) error {
|
|||
case nil, Continue:
|
||||
// nop
|
||||
case Break:
|
||||
broken = true
|
||||
broken.Store(true)
|
||||
default:
|
||||
broken = true
|
||||
broken.Store(true)
|
||||
err = diag.Errors(err, ex)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,42 @@ func TestEach(t *testing.T) {
|
|||
)
|
||||
}
|
||||
|
||||
// TODO: test peach
|
||||
func TestPeach(t *testing.T) {
|
||||
// Testing the `peach` builtin is a challenge since, by definition, the order of execution is
|
||||
// undefined.
|
||||
Test(t,
|
||||
// Verify the output has the expected values when sorted.
|
||||
That(`range 5 | peach [x]{ + 1 $x } | to-lines | order`).
|
||||
Puts("1", "2", "3", "4", "5"),
|
||||
// Verify that successive runs produce the output in different order. This test can
|
||||
// theoretically suffer false positives but the vast majority of the time this will produce
|
||||
// the expected output in the first iteration. The probability it will produce the same
|
||||
// order of output in 100 iterations is effectively zero.
|
||||
That(`
|
||||
var cpu-count = 99
|
||||
var x = [(range $cpu-count | peach [x]{ + 1 $x })]
|
||||
for r [(range 100)] {
|
||||
var y = [(range $cpu-count | peach [x]{ sleep 1us; + 1 $x })]
|
||||
if (not-eq $x $y) {
|
||||
put $true
|
||||
break
|
||||
}
|
||||
}
|
||||
`).Puts(true),
|
||||
// Verify that exceptions are propagated.
|
||||
That(`peach [x]{ fail $x } [a]`).
|
||||
Throws(FailError{"a"}, "fail $x ", "peach [x]{ fail $x } [a]"),
|
||||
// Verify that `break` works by terminating the `peach` before the entire sequence is
|
||||
// consumed.
|
||||
That(`
|
||||
var tot = 0
|
||||
range 1 101 |
|
||||
peach [x]{ if (== 50 $x) { break } else { put $x } } |
|
||||
each [x]{ set tot = (+ $tot $x) }
|
||||
if (< $tot (/ (* 100 101) 2)) { put okay }
|
||||
`).Puts("okay"),
|
||||
)
|
||||
}
|
||||
|
||||
func TestFail(t *testing.T) {
|
||||
Test(t,
|
||||
|
|
|
@ -2090,9 +2090,9 @@ If an external command exits with a non-zero status, Elvish treats that as an
|
|||
exception.
|
||||
|
||||
Flow commands -- `break`, `continue` and `return` -- are ordinary builtin
|
||||
commands that raise special "flow control" exceptions. The `for` and `while`
|
||||
commands capture `break` and `continue`, while `fn` modifies its closure to
|
||||
capture `return`.
|
||||
commands that raise special "flow control" exceptions. The `for`, `while`, and
|
||||
`peach` commands capture `break` and `continue`, while `fn` modifies its closure
|
||||
to capture `return`.
|
||||
|
||||
One interesting implication is that since flow commands are just ordinary
|
||||
commands you can build functions on top of them. For instance, this function
|
||||
|
|
Loading…
Reference in New Issue
Block a user