pkg/eval/vals: Add methods to ValueTester; use it to test StructMap.

This commit is contained in:
Qi Xiao 2020-01-08 10:20:43 +00:00
parent d19b9c5ab7
commit 2d609bd5b4
12 changed files with 165 additions and 53 deletions

View File

@ -37,13 +37,6 @@ func TestAssoc(t *testing.T) {
Args(MakeMap("k", "v"), "k2", "v2").Rets(
Eq(MakeMap("k", "v", "k2", "v2")), nil),
Args(testStructMap{"foo", 1.0}, "name", "bar").
Rets(testStructMap{"bar", 1.0}, nil),
Args(testStructMap{"foo", 1.0}, "score-number", "2.0").
Rets(testStructMap{"foo", 2.0}, nil),
Args(testStructMap{"foo", 1.0}, "score-number", "bad number").
Rets(nil, cannotParseAs{"number", `'bad number'`}),
Args(customAssocer{}, "x", "y").Rets("custom result", errCustomAssoc),
Args(struct{}{}, "x", "y").Rets(nil, errAssocUnsupported),

View File

@ -37,11 +37,6 @@ func TestEqual(t *testing.T) {
Args(MakeMap("k", "v"), MakeMap("k2", "v")).Rets(false),
Args(MakeMap("k", "v", "k2", "v2"), MakeMap("k", "v")).Rets(false),
Args(testStructMap{}, testStructMap{}).Rets(true),
Args(testStructMap{"a", 1.0}, testStructMap{"a", 1.0}).Rets(true),
Args(testStructMap{"a", 1.0}, testStructMap{"a", 2.0}).Rets(false),
Args(testStructMap{"a", 1.0}, "a").Rets(false),
Args(customEqualer{true}, 2).Rets(true),
Args(customEqualer{false}, 2).Rets(false),

View File

@ -15,9 +15,6 @@ func TestHasKey(t *testing.T) {
// Map
Args(MakeMap("k", "v"), "k").Rets(true),
Args(MakeMap("k", "v"), "bad").Rets(false),
// StructMap
Args(testStructMap{}, "name").Rets(true),
Args(testStructMap{}, "bad").Rets(false),
// HasKeyer
Args(hasKeyer{"valid"}, "valid").Rets(true),
Args(hasKeyer{"valid"}, "invalid").Rets(false),

View File

@ -25,8 +25,6 @@ func TestHash(t *testing.T) {
Args(MakeList("foo", "bar")).Rets(hash.DJB(Hash("foo"), Hash("bar"))),
Args(MakeMap("foo", "bar")).
Rets(hash.DJB(Hash("foo"), Hash("bar"))),
Args(testStructMap{"name", 1.0}).
Rets(hash.DJB(Hash("name"), Hash(1.0))),
Args(hasher{}).Rets(uint32(42)),
Args(nonHasher{}).Rets(uint32(0)),
})

View File

@ -80,9 +80,5 @@ func TestIndex(t *testing.T) {
Args(m, "foo").Rets("bar", nil),
Args(m, "bad").Rets(Any, NoSuchKey("bad")),
// StructMap indicies
Args(testStructMap{"foo", 1.0}, "name").Rets("foo", nil),
Args(testStructMap{"foo", 1.0}, "bad").Rets(nil, NoSuchKey("bad")),
})
}

View File

@ -7,16 +7,6 @@ import (
"github.com/elves/elvish/pkg/util"
)
// A variant of IterateKeys that is easier to test.
func iterateKeys(v interface{}) ([]interface{}, error) {
var keys []interface{}
err := IterateKeys(v, func(k interface{}) bool {
keys = append(keys, k)
return true
})
return keys, err
}
func vs(xs ...interface{}) []interface{} { return xs }
type keysIterator struct{ keys []interface{} }
@ -28,9 +18,8 @@ func (k keysIterator) IterateKeys(f func(interface{}) bool) {
type nonKeysIterator struct{}
func TestIterateKeys(t *testing.T) {
Test(t, Fn("iterateKeys", iterateKeys), Table{
Test(t, Fn("collectKeys", collectKeys), Table{
Args(MakeMap("k1", "v1", "k2", "v2")).Rets(vs("k1", "k2"), nil),
Args(testStructMap{}).Rets(vs("name", "score-number")),
Args(keysIterator{vs("lorem", "ipsum")}).Rets(vs("lorem", "ipsum")),
Args(nonKeysIterator{}).Rets(
Any, cannotIterateKeysOf{"!!vals.nonKeysIterator"}),

View File

@ -17,7 +17,6 @@ func TestKind(t *testing.T) {
Args(os.Stdin).Rets("file"),
Args(EmptyList).Rets("list"),
Args(EmptyMap).Rets("map"),
Args(testStructMap{}).Rets("structmap"),
Args(xtype(0)).Rets("!!vals.xtype"),

View File

@ -9,7 +9,6 @@ import (
func TestLen(t *testing.T) {
Test(t, Fn("Len", Len), Table{
Args("foobar").Rets(6),
Args(testStructMap{}).Rets(2),
Args(10).Rets(-1),
})
}

View File

@ -17,9 +17,9 @@ func TestPipe(t *testing.T) {
defer w.Close()
TestValue(t, NewPipe(r, w)).
HasKind("pipe").
HasHash(hash.DJB(hash.UIntPtr(r.Fd()), hash.UIntPtr(w.Fd()))).
HasRepr(fmt.Sprintf("<pipe{%v %v}>", r.Fd(), w.Fd())).
IsEqualTo(NewPipe(r, w)).
IsNotEqualTo(123, "a string", NewPipe(w, r))
Kind("pipe").
Hash(hash.DJB(hash.UIntPtr(r.Fd()), hash.UIntPtr(w.Fd()))).
Repr(fmt.Sprintf("<pipe{%v %v}>", r.Fd(), w.Fd())).
Equal(NewPipe(r, w)).
NotEqual(123, "a string", NewPipe(w, r))
}

View File

@ -29,8 +29,6 @@ func TestRepr(t *testing.T) {
Args(MakeList("foo", "bar")).Rets("[foo bar]"),
Args(EmptyMap).Rets("[&]"),
Args(MakeMap("foo", "bar")).Rets("[&foo=bar]"),
Args(testStructMap{"name", 1.0}).Rets(
"[&name=name &score-number=(float64 1)]"),
Args(reprer{}).Rets("<reprer>"),
Args(nonReprer{}).Rets("<unknown {}>"),
})

View File

@ -1,5 +1,11 @@
package vals
import (
"testing"
"github.com/xiaq/persistent/hash"
)
// A test structmap type used in other tests.
type testStructMap struct {
Name string `json:"name"`
@ -7,3 +13,33 @@ type testStructMap struct {
}
func (testStructMap) IsStructMap(StructMapMarker) {}
func TestStructMap(t *testing.T) {
TestValue(t, testStructMap{}).
Kind("structmap").
Hash(hash.DJB(Hash(""), Hash(0.0))).
Repr(`[&name='' &score-number=(float64 0)]`).
Len(2).
Equal(testStructMap{}).
NotEqual("a", MakeMap(), testStructMap{"a", 1.0}).
HasKey("name", "score-number").
HasNoKey("bad").
AllKeys("name", "score-number").
Index("name", "").
Index("score-number", 0.0)
TestValue(t, testStructMap{"a", 1.0}).
Kind("structmap").
Hash(hash.DJB(Hash("a"), Hash(1.0))).
Repr(`[&name=a &score-number=(float64 1)]`).
Len(2).
Equal(testStructMap{"a", 1.0}).
NotEqual(
"a", MakeMap("name", "", "score-number", 1.0),
testStructMap{}, testStructMap{"a", 2.0}, testStructMap{"b", 1.0}).
Index("name", "a").
Index("score-number", 1.0).
Assoc("name", "b", testStructMap{"b", 1.0}).
Assoc("score-number", 2.0, testStructMap{"a", 2.0}).
AssocError("score-number", "not-num", cannotParseAs{"number", "not-num"})
}

View File

@ -1,6 +1,7 @@
package vals
import (
"reflect"
"testing"
"github.com/elves/elvish/pkg/tt"
@ -17,8 +18,8 @@ func TestValue(t *testing.T, v interface{}) ValueTester {
return ValueTester{t, v}
}
// HasKind tests the Kind of the value.
func (vt ValueTester) HasKind(wantKind string) ValueTester {
// Kind tests the Kind of the value.
func (vt ValueTester) Kind(wantKind string) ValueTester {
vt.t.Helper()
kind := Kind(vt.v)
if kind != wantKind {
@ -27,8 +28,8 @@ func (vt ValueTester) HasKind(wantKind string) ValueTester {
return vt
}
// HasHash tests the Hash of the value.
func (vt ValueTester) HasHash(wantHash uint32) ValueTester {
// Hash tests the Hash of the value.
func (vt ValueTester) Hash(wantHash uint32) ValueTester {
vt.t.Helper()
kind := Hash(vt.v)
if kind != wantHash {
@ -37,18 +38,28 @@ func (vt ValueTester) HasHash(wantHash uint32) ValueTester {
return vt
}
// HasRepr tests the Repr of the value.
func (vt ValueTester) HasRepr(wantRepr string) ValueTester {
// Len tests the Len of the value.
func (vt ValueTester) Len(wantLen int) ValueTester {
vt.t.Helper()
kind := Repr(vt.v, -1)
kind := Len(vt.v)
if kind != wantLen {
vt.t.Errorf("Len(v) = %v, want %v", kind, wantLen)
}
return vt
}
// Repr tests the Repr of the value.
func (vt ValueTester) Repr(wantRepr string) ValueTester {
vt.t.Helper()
kind := Repr(vt.v, NoPretty)
if kind != wantRepr {
vt.t.Errorf("Repr(v) = %s, want %s", kind, wantRepr)
}
return vt
}
// IsEqualTo tests that the value is Equal to another.
func (vt ValueTester) IsEqualTo(others ...interface{}) ValueTester {
// Equal tests that the value is Equal to every of the given values.
func (vt ValueTester) Equal(others ...interface{}) ValueTester {
vt.t.Helper()
for _, other := range others {
eq := Equal(vt.v, other)
@ -59,8 +70,8 @@ func (vt ValueTester) IsEqualTo(others ...interface{}) ValueTester {
return vt
}
// IsNotEqualTo tests that the value is not Equal to another.
func (vt ValueTester) IsNotEqualTo(others ...interface{}) ValueTester {
// NotEqual tests that the value is not Equal to any of the given values.
func (vt ValueTester) NotEqual(others ...interface{}) ValueTester {
vt.t.Helper()
for _, other := range others {
eq := Equal(vt.v, other)
@ -71,6 +82,107 @@ func (vt ValueTester) IsNotEqualTo(others ...interface{}) ValueTester {
return vt
}
// HasKey tests that the value has each of the given keys.
func (vt ValueTester) HasKey(keys ...interface{}) ValueTester {
vt.t.Helper()
for _, key := range keys {
has := HasKey(vt.v, key)
if !has {
vt.t.Errorf("HasKey(v, %v) = false, want true", key)
}
}
return vt
}
// HasNoKey tests that the value does not have any of the given keys.
func (vt ValueTester) HasNoKey(keys ...interface{}) ValueTester {
vt.t.Helper()
for _, key := range keys {
has := HasKey(vt.v, key)
if has {
vt.t.Errorf("HasKey(v, %v) = true, want false", key)
}
}
return vt
}
// AllKeys tests that the given keys match what the result of IterateKeys on the
// value.
//
// NOTE: This now checks equality using reflect.DeepEqual, since all the builtin
// types have string keys. This can be changed in future to use Equal is the
// need arises.
func (vt ValueTester) AllKeys(wantKeys ...interface{}) ValueTester {
vt.t.Helper()
keys, err := collectKeys(vt.v)
if err != nil {
vt.t.Errorf("IterateKeys(v, f) -> err %v, want nil", err)
}
if !reflect.DeepEqual(keys, wantKeys) {
vt.t.Errorf("IterateKeys(v, f) calls f with %v, want %v", keys, wantKeys)
}
return vt
}
func collectKeys(v interface{}) ([]interface{}, error) {
var keys []interface{}
err := IterateKeys(v, func(k interface{}) bool {
keys = append(keys, k)
return true
})
return keys, err
}
// Index tests that Index'ing the value with the given key returns the wanted value
// and no error.
func (vt ValueTester) Index(key, wantVal interface{}) ValueTester {
vt.t.Helper()
got, err := Index(vt.v, key)
if err != nil {
vt.t.Errorf("Index(v, %v) -> err %v, want nil", key, err)
}
if !Equal(got, wantVal) {
vt.t.Errorf("Index(v, %v) -> %v, want %v", key, got, wantVal)
}
return vt
}
// IndexError tests that Index'ing the value with the given key returns the given
// error.
func (vt ValueTester) IndexError(key interface{}, wantErr error) ValueTester {
vt.t.Helper()
_, err := Index(vt.v, key)
if !reflect.DeepEqual(err, wantErr) {
vt.t.Errorf("Index(v, %v) -> err %v, want %v", key, err, wantErr)
}
return vt
}
// Assoc tests that Assoc'ing the value with the given key-value pair returns
// the wanted new value and no error.
func (vt ValueTester) Assoc(key, val, wantNew interface{}) ValueTester {
vt.t.Helper()
got, err := Assoc(vt.v, key, val)
if err != nil {
vt.t.Errorf("Assoc(v, %v) -> err %v, want nil", key, err)
}
if !Equal(got, wantNew) {
vt.t.Errorf("Assoc(v, %v) -> %v, want %v", key, got, wantNew)
}
return vt
}
// AssocError tests that Assoc'ing the value with the given key-value pair
// returns the given error.
func (vt ValueTester) AssocError(key, val interface{}, wantErr error) ValueTester {
vt.t.Helper()
_, err := Assoc(vt.v, key, val)
if !reflect.DeepEqual(err, wantErr) {
vt.t.Errorf("Assoc(v, %v) -> err %v, want %v", key, err, wantErr)
}
return vt
}
// Eq returns a tt.Matcher that matches using the Equal function.
func Eq(r interface{}) tt.Matcher { return equalMatcher{r} }