2014-01-28 15:02:48 +08:00
|
|
|
package eval
|
|
|
|
|
|
|
|
import (
|
2014-09-21 05:39:16 +08:00
|
|
|
"reflect"
|
2014-01-28 15:02:48 +08:00
|
|
|
"strconv"
|
2014-01-29 21:17:04 +08:00
|
|
|
"syscall"
|
|
|
|
"testing"
|
2014-09-21 05:39:16 +08:00
|
|
|
|
2014-10-30 03:50:10 +08:00
|
|
|
"github.com/elves/elvish/parse"
|
2014-01-28 15:02:48 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestNewEvaluator(t *testing.T) {
|
2015-02-24 01:36:46 +08:00
|
|
|
ev := NewEvaluator(nil, "")
|
2014-01-28 15:02:48 +08:00
|
|
|
pid := strconv.Itoa(syscall.Getpid())
|
2015-01-26 23:21:13 +08:00
|
|
|
if toString(ev.builtin["pid"].Get()) != pid {
|
2015-01-23 08:27:51 +08:00
|
|
|
t.Errorf(`ev.builtin["pid"] = %v, want %v`, ev.builtin["pid"], pid)
|
2014-01-28 15:02:48 +08:00
|
|
|
}
|
|
|
|
}
|
2014-09-21 05:39:16 +08:00
|
|
|
|
2015-02-25 04:59:14 +08:00
|
|
|
func mustParse(name, text string) *parse.Chunk {
|
2014-09-21 05:39:16 +08:00
|
|
|
n, e := parse.Parse(name, text)
|
|
|
|
if e != nil {
|
|
|
|
panic("parser error")
|
|
|
|
}
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
|
|
|
func stringValues(ss ...string) []Value {
|
|
|
|
vs := make([]Value, len(ss))
|
|
|
|
for i, s := range ss {
|
2015-01-27 02:05:07 +08:00
|
|
|
vs[i] = str(s)
|
2014-09-21 05:39:16 +08:00
|
|
|
}
|
|
|
|
return vs
|
|
|
|
}
|
|
|
|
|
|
|
|
var evalTests = []struct {
|
2014-09-26 05:43:52 +08:00
|
|
|
text string
|
|
|
|
wanted []Value
|
|
|
|
wantError bool
|
2014-09-21 05:39:16 +08:00
|
|
|
}{
|
2014-09-25 06:08:37 +08:00
|
|
|
// Empty chunk
|
2014-09-26 05:43:52 +08:00
|
|
|
{"", []Value{}, false},
|
2014-09-25 06:08:37 +08:00
|
|
|
|
|
|
|
// Trivial command
|
2014-09-26 05:43:52 +08:00
|
|
|
{"put 233 lorem ipsum", stringValues("233", "lorem", "ipsum"), false},
|
2014-09-25 06:08:37 +08:00
|
|
|
|
|
|
|
// Byte Pipeline
|
|
|
|
{`echo "Albert\nAllan\nAlbraham\nBerlin" | sed s/l/1/g | grep e | feedchan`,
|
2014-09-26 05:43:52 +08:00
|
|
|
stringValues("A1bert", "Ber1in"), false},
|
2014-09-25 06:08:37 +08:00
|
|
|
|
|
|
|
// Arithmetics
|
|
|
|
// TODO test more edge cases
|
2014-09-26 05:43:52 +08:00
|
|
|
{"* 353 661", stringValues("233333"), false},
|
|
|
|
{"+ 233100 233", stringValues("233333"), false},
|
|
|
|
{"- 233333 233100", stringValues("233"), false},
|
|
|
|
{"/ 233333 353", stringValues("661"), false},
|
|
|
|
{"/ 1 0", stringValues("+Inf"), false},
|
2014-09-25 06:08:37 +08:00
|
|
|
|
|
|
|
// String literal
|
2014-09-26 05:43:52 +08:00
|
|
|
{"put `such \\\"``literal`", stringValues("such \\\"`literal"), false},
|
2014-09-25 06:08:37 +08:00
|
|
|
{`put "much \n\033[31;1m$cool\033[m"`,
|
2014-09-26 05:43:52 +08:00
|
|
|
stringValues("much \n\033[31;1m$cool\033[m"), false},
|
2014-09-25 06:08:37 +08:00
|
|
|
|
|
|
|
// Compounding list primaries
|
2014-09-26 05:47:27 +08:00
|
|
|
{"put {fi elvi}sh{1 2}",
|
|
|
|
stringValues("fish1", "fish2", "elvish1", "elvish2"), false},
|
2014-09-25 06:08:37 +08:00
|
|
|
|
|
|
|
// Table and subscript
|
|
|
|
{"println [a b c &key value] | feedchan",
|
2014-09-26 05:43:52 +08:00
|
|
|
stringValues("[a b c &key value]"), false},
|
|
|
|
{"put [a b c &key value][2]", stringValues("c"), false},
|
|
|
|
{"put [a b c &key value][key]", stringValues("value"), false},
|
2014-09-25 06:08:37 +08:00
|
|
|
|
|
|
|
// Variable and compounding
|
2015-01-19 23:46:40 +08:00
|
|
|
{"var $x string = `SHELL`\nput `WOW, SUCH `$x`, MUCH COOL`\n",
|
2014-09-26 05:43:52 +08:00
|
|
|
stringValues("WOW, SUCH SHELL, MUCH COOL"), false},
|
2014-09-25 06:18:37 +08:00
|
|
|
|
2014-09-26 05:26:56 +08:00
|
|
|
// var and set
|
2015-01-19 23:46:40 +08:00
|
|
|
{"var $x $y string = SUCH VAR; put $x $y",
|
2014-09-26 05:47:27 +08:00
|
|
|
stringValues("SUCH", "VAR"), false},
|
2015-01-19 23:46:40 +08:00
|
|
|
{"var $x $y string; set $x $y = SUCH SET; put $x $y",
|
2014-09-26 05:43:52 +08:00
|
|
|
stringValues("SUCH", "SET"), false},
|
2015-01-19 23:46:40 +08:00
|
|
|
{"var $x", stringValues(), false},
|
|
|
|
{"var $x string $y", stringValues(), false},
|
2015-01-20 05:19:13 +08:00
|
|
|
{"var $x table; set $x = [lorem ipsum]; put $x[1]",
|
|
|
|
stringValues("ipsum"), false},
|
2014-09-26 05:26:56 +08:00
|
|
|
|
2015-01-21 04:33:01 +08:00
|
|
|
// Channel capture
|
|
|
|
{"put (put lorem ipsum)", stringValues("lorem", "ipsum"), false},
|
|
|
|
|
2014-09-25 06:18:37 +08:00
|
|
|
// Status capture
|
2014-09-26 05:47:27 +08:00
|
|
|
{"put ?(true|false|false)",
|
2015-01-22 06:43:30 +08:00
|
|
|
[]Value{success, newFailure("1"), newFailure("1")}, false},
|
2015-01-21 04:48:31 +08:00
|
|
|
|
|
|
|
// Closure evaluation
|
|
|
|
{"{ }", stringValues(), false},
|
|
|
|
{"{|$x| put $x} foo", stringValues("foo"), false},
|
2015-01-21 04:52:37 +08:00
|
|
|
|
|
|
|
// Variable enclosure
|
|
|
|
{"var $x = lorem; { put $x; set $x = ipsum }; put $x",
|
|
|
|
stringValues("lorem", "ipsum"), false},
|
2015-01-21 05:20:56 +08:00
|
|
|
// Shadowing
|
|
|
|
{"var $x = ipsum; { var $x = lorem; put $x }; put $x",
|
|
|
|
stringValues("lorem", "ipsum"), false},
|
|
|
|
// Shadowing by argument
|
|
|
|
{"var $x = ipsum; { |$x| put $x; set $x = BAD } lorem; put $x",
|
|
|
|
stringValues("lorem", "ipsum"), false},
|
2015-01-22 01:06:14 +08:00
|
|
|
|
|
|
|
// fn
|
|
|
|
{"fn f $x { put $x ipsum }; f lorem",
|
|
|
|
stringValues("lorem", "ipsum"), false},
|
2015-02-10 18:43:01 +08:00
|
|
|
// if
|
|
|
|
{"if $true { put x }", stringValues("x"), false},
|
|
|
|
{"if $true $false { put x } else if $true { put y }",
|
|
|
|
stringValues("y"), false},
|
|
|
|
{"if $true $false { put x } else if $false { put y } else { put z }",
|
|
|
|
stringValues("z"), false},
|
2015-01-23 08:27:51 +08:00
|
|
|
|
2015-01-25 06:40:34 +08:00
|
|
|
// Namespaces
|
2015-02-10 19:48:56 +08:00
|
|
|
// Pseudo-namespaces local: and up:
|
|
|
|
{"var $true = lorem; { var $true = ipsum; put $up:true $local:true $builtin:true }",
|
2015-01-27 02:05:07 +08:00
|
|
|
[]Value{str("lorem"), str("ipsum"), boolean(true)}, false},
|
2015-02-10 19:48:56 +08:00
|
|
|
{"var $x = lorem; { set $up:x = ipsum }; put $x",
|
2015-01-23 08:27:51 +08:00
|
|
|
stringValues("ipsum"), false},
|
2015-01-25 07:05:47 +08:00
|
|
|
// Pseudo-namespace env:
|
|
|
|
{"set $env:foo = lorem; put $env:foo", stringValues("lorem"), false},
|
|
|
|
{"del $env:foo; put $env:foo", stringValues(""), false},
|
2014-09-21 05:39:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestEval(t *testing.T) {
|
|
|
|
name := "<eval test>"
|
|
|
|
for _, tt := range evalTests {
|
|
|
|
n := mustParse(name, tt.text)
|
|
|
|
|
2015-02-24 01:36:46 +08:00
|
|
|
ev := NewEvaluator(nil, ".")
|
2014-09-21 05:39:16 +08:00
|
|
|
out := make(chan Value, len(tt.wanted))
|
|
|
|
outs := []Value{}
|
|
|
|
exhausted := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
for v := range out {
|
|
|
|
outs = append(outs, v)
|
|
|
|
}
|
|
|
|
exhausted <- struct{}{}
|
|
|
|
}()
|
|
|
|
|
2014-09-25 06:18:37 +08:00
|
|
|
ev.ports[1].ch = out
|
2014-09-21 05:39:16 +08:00
|
|
|
|
2015-02-24 01:36:46 +08:00
|
|
|
e := ev.Eval(name, tt.text, ".", n)
|
2014-09-21 05:39:16 +08:00
|
|
|
close(out)
|
|
|
|
<-exhausted
|
2014-09-26 05:43:52 +08:00
|
|
|
if tt.wantError {
|
|
|
|
// Test for error, ignore output
|
|
|
|
if e == nil {
|
|
|
|
t.Errorf("ev.Eval(*, %q, *) => <nil>, want non-nil", tt.text)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Test for output
|
|
|
|
if e != nil {
|
|
|
|
t.Errorf("ev.Eval(*, %q, *) => %v, want <nil>", tt.text, e)
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(outs, tt.wanted) {
|
|
|
|
t.Errorf("Evalling %q outputs %v, want %v", tt.text, outs, tt.wanted)
|
|
|
|
}
|
2014-09-21 05:39:16 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|