ginger/graph/graph_test.go

269 lines
5.1 KiB
Go
Raw Permalink Normal View History

package graph
import (
2021-12-30 18:16:08 +00:00
"errors"
2021-12-30 22:29:38 +00:00
"fmt"
"strconv"
"testing"
"github.com/stretchr/testify/assert"
)
type S string
func (s S) Equal(s2 Value) bool { return s == s2.(S) }
func (s S) String() string { return string(s) }
2021-12-30 18:16:08 +00:00
type I int
func (i I) Equal(i2 Value) bool { return i == i2.(I) }
func (i I) String() string { return strconv.Itoa(int(i)) }
func TestEqual(t *testing.T) {
var (
zeroValue S
zeroGraph = new(Graph[S, S])
)
tests := []struct {
a, b *Graph[S, S]
exp bool
}{
{
a: zeroGraph,
b: zeroGraph,
exp: true,
},
{
a: zeroGraph,
b: zeroGraph.AddValueIn("out", ValueOut[S, S]("incr", "in")),
exp: false,
},
{
a: zeroGraph.AddValueIn("out", ValueOut[S, S]("incr", "in")),
b: zeroGraph.AddValueIn("out", ValueOut[S, S]("incr", "in")),
exp: true,
},
{
a: zeroGraph.AddValueIn("out", ValueOut[S, S]("incr", "in")),
b: zeroGraph.AddValueIn("out", TupleOut[S, S](
"add",
ValueOut[S, S]("ident", "in"),
ValueOut[S, S]("ident", "1"),
)),
exp: false,
},
{
// tuples are different order
a: zeroGraph.AddValueIn("out", TupleOut[S, S](
"add",
ValueOut[S, S]("ident", "1"),
ValueOut[S, S]("ident", "in"),
)),
b: zeroGraph.AddValueIn("out", TupleOut[S, S](
"add",
ValueOut[S, S]("ident", "in"),
ValueOut[S, S]("ident", "1"),
)),
exp: false,
},
{
// tuple with no edge value and just a single input edge should be
// equivalent to just that edge.
a: zeroGraph.AddValueIn("out", TupleOut[S, S](
zeroValue,
ValueOut[S, S]("ident", "1"),
)),
b: zeroGraph.AddValueIn("out", ValueOut[S, S]("ident", "1")),
exp: true,
},
{
// tuple with an edge value and just a single input edge that has no
// edgeVal should be equivalent to just that edge with the tuple's
// edge value.
a: zeroGraph.AddValueIn("out", TupleOut[S, S](
"ident",
ValueOut[S, S](zeroValue, "1"),
)),
b: zeroGraph.AddValueIn("out", ValueOut[S, S]("ident", "1")),
exp: true,
},
{
a: zeroGraph.
AddValueIn("out", ValueOut[S, S]("incr", "in")).
AddValueIn("out2", ValueOut[S, S]("incr2", "in2")),
b: zeroGraph.
AddValueIn("out", ValueOut[S, S]("incr", "in")),
exp: false,
},
{
a: zeroGraph.
AddValueIn("out", ValueOut[S, S]("incr", "in")).
AddValueIn("out2", ValueOut[S, S]("incr2", "in2")),
b: zeroGraph.
AddValueIn("out", ValueOut[S, S]("incr", "in")).
AddValueIn("out2", ValueOut[S, S]("incr2", "in2")),
exp: true,
},
{
// order of value ins shouldn't matter
a: zeroGraph.
AddValueIn("out", ValueOut[S, S]("incr", "in")).
AddValueIn("out2", ValueOut[S, S]("incr2", "in2")),
b: zeroGraph.
AddValueIn("out2", ValueOut[S, S]("incr2", "in2")).
AddValueIn("out", ValueOut[S, S]("incr", "in")),
exp: true,
},
}
for i, test := range tests {
t.Run(strconv.Itoa(i), func(t *testing.T) {
assert.Equal(t, test.exp, test.a.Equal(test.b))
})
}
}
2021-12-30 18:16:08 +00:00
type mapReduceTestEdge struct {
name string
2021-12-30 22:29:38 +00:00
fn func([]int) int
2021-12-30 18:16:08 +00:00
done bool
}
func (e *mapReduceTestEdge) Equal(e2i Value) bool {
e2, _ := e2i.(*mapReduceTestEdge)
if e == nil || e2 == nil {
return e == e2
}
return e.name == e2.name
}
func (e *mapReduceTestEdge) String() string {
return e.name
}
func (e *mapReduceTestEdge) do(ii []int) int {
if e.done {
panic(fmt.Sprintf("%q already done", e.name))
}
e.done = true
return e.fn(ii)
}
func TestMapReduce(t *testing.T) {
type (
2021-12-30 22:29:38 +00:00
Va = I
Vb = int
Ea = *mapReduceTestEdge
2021-12-30 18:16:08 +00:00
edge = OpenEdge[Ea, Va]
)
var (
zeroVb Vb
)
vOut := func(edge Ea, val Va) *edge {
return ValueOut[Ea, Va](edge, val)
}
tOut := func(edge Ea, ins ...*edge) *edge {
return TupleOut[Ea, Va](edge, ins...)
}
2021-12-30 22:29:38 +00:00
add := func() *mapReduceTestEdge {
2021-12-30 18:16:08 +00:00
return &mapReduceTestEdge{
name: "add",
fn: func(ii []int) int {
var n int
for _, i := range ii {
n += i
}
return n
},
}
}
2021-12-30 22:29:38 +00:00
mul := func() *mapReduceTestEdge {
2021-12-30 18:16:08 +00:00
return &mapReduceTestEdge{
name: "mul",
fn: func(ii []int) int {
n := 1
for _, i := range ii {
n *= i
}
return n
},
}
}
mapVal := func(valA Va) (Vb, error) {
return Vb(valA * 10), nil
}
reduceEdge := func(edgeA Ea, valBs []Vb) (Vb, error) {
if edgeA == nil {
if len(valBs) == 1 {
return valBs[0], nil
}
return zeroVb, errors.New("tuple edge must have edge value")
}
return edgeA.do(valBs), nil
}
tests := []struct {
2021-12-30 22:29:38 +00:00
in *edge
2021-12-30 18:16:08 +00:00
exp int
}{
{
2021-12-30 22:29:38 +00:00
in: vOut(nil, 1),
2021-12-30 18:16:08 +00:00
exp: 10,
},
{
2021-12-30 22:29:38 +00:00
in: vOut(add(), 1),
2021-12-30 18:16:08 +00:00
exp: 10,
},
{
in: tOut(
add(),
vOut(nil, 1),
vOut(add(), 2),
vOut(mul(), 3),
),
exp: 60,
},
{
// duplicate edges and values getting used twice, each should only
// get eval'd once
in: tOut(
add(),
tOut(add(), vOut(nil, 1), vOut(nil, 2)),
tOut(add(), vOut(nil, 1), vOut(nil, 2)),
tOut(add(), vOut(nil, 3), vOut(nil, 3)),
),
exp: 120,
},
}
for i, test := range tests {
t.Run(strconv.Itoa(i), func(t *testing.T) {
got, err := MapReduce(test.in, mapVal, reduceEdge)
assert.NoError(t, err)
assert.Equal(t, test.exp, got)
})
}
}