graph-building seems to be working

selection
Jordan Orelli 4 years ago
parent 4d5a33dbdb
commit 500b02db34

@ -26,38 +26,41 @@ type lnode struct {
// If there are no parents, the lnode is a root node, and the provided test is // If there are no parents, the lnode is a root node, and the provided test is
// run once. Otherwise, the provided test is used to create xnodes that are // run once. Otherwise, the provided test is used to create xnodes that are
// children of all of the provided parent nodes' xnodes. // children of all of the provided parent nodes' xnodes.
func newLNode(test Test, parents ...*lnode) *lnode { func newLNode(test Test, sel Selection) *lnode {
if len(parents) == 0 { if len(sel.nodes) == 0 {
return rootLNode(test) return rootLNode(test)
} }
id := nextNodeID()
node := lnode{ node := lnode{
id: id, id: nextNodeID(),
name: parseName(test), name: parseName(test),
test: test, test: test,
parents: parents, parents: make([]*lnode, len(sel.nodes)),
} }
copy(node.parents, sel.nodes)
xID := 0 xID := 0
for _, parent := range parents { for _, parent := range node.parents {
parent.children = append(parent.children, &node)
for i, _ := range parent.xnodes { for i, _ := range parent.xnodes {
node.xnodes = append(node.xnodes, xnode{ x := xnode{
id: xID, id: xID,
lnode: &node, lnode: &node,
parent: &parent.xnodes[i], parent: &parent.xnodes[i],
}) }
node.xnodes = append(node.xnodes, x)
xID++ xID++
} }
} }
for i, _ := range node.xnodes { for i, x := range node.xnodes {
xparent := node.xnodes[i] x.parent.children = append(x.parent.children, &node.xnodes[i])
xparent.children = append(xparent.children, &node.xnodes[i])
} }
return &node return &node
} }
// rootLNode creates a root lnode. This case is a lot simpler so I split it out
// to keep newLNode a little more readable.
func rootLNode(test Test) *lnode { func rootLNode(test Test) *lnode {
id := nextNodeID() id := nextNodeID()
node := lnode{ node := lnode{
@ -69,21 +72,6 @@ func rootLNode(test Test) *lnode {
return &node return &node
} }
// child adds an lnode as a child of the receiver. For every xnode in this
// receiver, the child lnode has a component xnode whose parent is the
// corresponding xnode in this lnode.
func (l *lnode) child(c *lnode, t Test) {
panic("nuh")
//c.parents = append(c.parents, l)
//l.children = append(l.children, c)
//for i, x := range l.xnodes {
// xchild := x.child(t)
// xchild.lnode = c
// xchild.id = i
// c.xnodes = append(c.xnodes, xchild)
//}
}
// xnode is a node in the execution graph, representing one instance of a test // xnode is a node in the execution graph, representing one instance of a test
// to be executed. xnode is the unit test in tea. every xnode is either // to be executed. xnode is the unit test in tea. every xnode is either
// unparented or has one parent. // unparented or has one parent.
@ -94,32 +82,33 @@ type xnode struct {
children []*xnode children []*xnode
} }
// func newXNode(L *lnode
func (x *xnode) child(t Test) *xnode {
panic("no")
// child := &xnode{test: t, parent: x}
// x.children = append(x.children, child)
// return child
}
func (x *xnode) isOnlyTestInLNode() bool { func (x *xnode) isOnlyTestInLNode() bool {
return len(x.lnode.children) == 1 return len(x.lnode.xnodes) == 1
} }
func (x *xnode) label() string { func (x *xnode) label() string {
if x.isOnlyTestInLNode() { if x.parent == nil {
return x.lnode.name switch {
case len(x.lnode.children) < 10:
return fmt.Sprintf("%s.%d", x.lnode.name, x.id)
case len(x.lnode.children) < 100:
return fmt.Sprintf("%s.%02d", x.lnode.name, x.id)
case len(x.lnode.children) < 1000:
return fmt.Sprintf("%s.%03d", x.lnode.name, x.id)
default:
return fmt.Sprintf("%s.%04d", x.lnode.name, x.id)
} }
} else {
switch { switch {
case len(x.lnode.children) < 10: case len(x.lnode.children) < 10:
return fmt.Sprintf("%s:%d", x.lnode.name, x.id) return fmt.Sprintf("%s.%d.%s", x.lnode.name, x.id, x.parent.lnode.name)
case len(x.lnode.children) < 100: case len(x.lnode.children) < 100:
return fmt.Sprintf("%s:%02d", x.lnode.name, x.id) return fmt.Sprintf("%s.%02d.%s", x.lnode.name, x.id, x.parent.lnode.name)
case len(x.lnode.children) < 1000: case len(x.lnode.children) < 1000:
return fmt.Sprintf("%s:%03d", x.lnode.name, x.id) return fmt.Sprintf("%s.%03d.%s", x.lnode.name, x.id, x.parent.lnode.name)
default: default:
return fmt.Sprintf("%s:%04d", x.lnode.name, x.id) return fmt.Sprintf("%s.%04d.%s", x.lnode.name, x.id, x.parent.lnode.name)
}
} }
} }

@ -1,7 +1,8 @@
package tea package tea
func NewSelection(test Test) Selection { func NewSelection(test Test) Selection {
return Selection{nodes: []*lnode{newLNode(test)}} node := newLNode(test, Selection{})
return Selection{nodes: []*lnode{node}}
} }
// Selection represents a set of nodes in our graph. // Selection represents a set of nodes in our graph.
@ -10,7 +11,7 @@ type Selection struct {
} }
func (s Selection) Child(test Test) Selection { func (s Selection) Child(test Test) Selection {
node := newLNode(test, s.nodes...) node := newLNode(test, s)
return Selection{nodes: []*lnode{node}} return Selection{nodes: []*lnode{node}}
} }
@ -32,8 +33,8 @@ func (s Selection) And(other Selection) Selection {
func (s Selection) xnodes() []*xnode { func (s Selection) xnodes() []*xnode {
xnodes := make([]*xnode, 0, s.countXNodes()) xnodes := make([]*xnode, 0, s.countXNodes())
for _, L := range s.nodes { for _, L := range s.nodes {
for _, x := range L.xnodes { for i, _ := range L.xnodes {
xnodes = append(xnodes, &x) xnodes = append(xnodes, &L.xnodes[i])
} }
} }
return xnodes return xnodes
@ -46,18 +47,3 @@ func (s Selection) countXNodes() int {
} }
return total return total
} }
// func (s Selection) writeXDOT(w io.Writer) {
// xnodes := s.xnodes()
//
// type xedge [2]string
// included := make(map[xedge]bool)
// edges := make([]xedge, 0, len(xnodes))
//
// for _, X := range xnodes {
//
// for p := X; p != nil; p = p.parent {
//
// }
// }
// }

@ -7,19 +7,57 @@ import (
type selectionTest struct { type selectionTest struct {
label string label string
selection Selection selection Selection
selLNodes int // number of lnodes in the selection lnodes []string
selXNodes int // number of xnodes in the selection xnodes []string
reachLNodes int // number of lnodes reachable by the selection
reachXnodes int // number of xnodes reachable by the selection
} }
func (test *selectionTest) Run(t *testing.T) { func (test *selectionTest) Run(t *testing.T) {
if count := len(test.selection.nodes); count != test.selLNodes { lfound := make(map[string]bool)
t.Errorf("expected %d node in selection, saw %d", test.selLNodes, count) for _, L := range test.selection.nodes {
if len(L.parents) > 0 {
pnames := make([]string, 0, len(L.parents))
for _, p := range L.parents {
pnames = append(pnames, p.name)
}
t.Logf("found lnode with label %s having %d parents: %s", L.name, len(L.parents), pnames)
} else {
t.Logf("found root lnode with label %s", L.name)
}
lfound[L.name] = true
}
for _, expected := range test.lnodes {
if lfound[expected] {
delete(lfound, expected)
} else {
t.Errorf("missing expected lnode with label %s", expected)
}
}
for label, _ := range lfound {
t.Errorf("found unexpected lnode with label %s", label)
}
xfound := make(map[string]bool)
for _, x := range test.selection.xnodes() {
if x.parent != nil {
t.Logf("found xnode with label %s having parent %s", x.label(), x.parent.label())
} else {
t.Logf("found root xnode with label %s", x.label())
}
xfound[x.label()] = true
}
for _, expected := range test.xnodes {
if xfound[expected] {
delete(xfound, expected)
} else {
t.Errorf("missing expected xnode with label %s", expected)
}
} }
if count := test.selection.countXNodes(); count != test.selXNodes { for label, _ := range xfound {
t.Errorf("expected %d xnode in lnode, saw %d", test.selXNodes, count) t.Errorf("found unexpected xnode with label %s", label)
} }
} }
@ -28,20 +66,20 @@ func TestSelections(t *testing.T) {
{ {
label: "new selection", label: "new selection",
selection: NewSelection(A), selection: NewSelection(A),
selLNodes: 1, lnodes: []string{"A"},
selXNodes: 1, xnodes: []string{"A.0"},
}, },
{ {
label: "root with one child", label: "root with one child",
selection: NewSelection(A).Child(B), selection: NewSelection(A).Child(B),
selLNodes: 1, lnodes: []string{"B"},
selXNodes: 1, xnodes: []string{"B.0.A"},
}, },
{ {
label: "two selected roots", label: "two selected roots",
selection: NewSelection(A).And(NewSelection(B)), selection: NewSelection(A).And(NewSelection(B)),
selLNodes: 2, lnodes: []string{"A", "B"},
selXNodes: 2, xnodes: []string{"A.0", "B.0"},
}, },
} }
@ -53,8 +91,8 @@ func TestSelections(t *testing.T) {
return selectionTest{ return selectionTest{
label: "root and child selected", label: "root and child selected",
selection: root.And(b), selection: root.And(b),
selLNodes: 2, lnodes: []string{"A", "B"},
selXNodes: 2, xnodes: []string{"A.0", "B.0.A"},
} }
}) })
@ -64,8 +102,8 @@ func TestSelections(t *testing.T) {
return selectionTest{ return selectionTest{
label: "an optional test", label: "an optional test",
selection: root.And(b).Child(C), selection: root.And(b).Child(C),
selLNodes: 1, lnodes: []string{"C"},
selXNodes: 2, xnodes: []string{"C.0.A", "C.1.B"},
} }
}) })
@ -77,8 +115,8 @@ func TestSelections(t *testing.T) {
return selectionTest{ return selectionTest{
label: "two children selected", label: "two children selected",
selection: b.And(c), selection: b.And(c),
selLNodes: 2, lnodes: []string{"B", "C"},
selXNodes: 2, xnodes: []string{"B.0.A", "C.0.A"},
} }
}) })
@ -89,8 +127,8 @@ func TestSelections(t *testing.T) {
return selectionTest{ return selectionTest{
label: "a diamond test", label: "a diamond test",
selection: b.And(c).Child(D), selection: b.And(c).Child(D),
selLNodes: 1, lnodes: []string{"D"},
selXNodes: 2, xnodes: []string{"D.0.B", "D.1.C"},
} }
}) })
@ -102,8 +140,8 @@ func TestSelections(t *testing.T) {
return selectionTest{ return selectionTest{
label: "child of a node having multiple parents", label: "child of a node having multiple parents",
selection: d.Child(E), selection: d.Child(E),
selLNodes: 1, lnodes: []string{"E"},
selXNodes: 2, xnodes: []string{"E.0.D", "E.1.D"},
} }
}) })

@ -57,6 +57,7 @@ const Pass = Passing("test passed")
type Passing string type Passing string
func (p Passing) Run(t *testing.T) {} func (p Passing) Run(t *testing.T) {}
func (p Passing) String() string { return string(p) }
// parseName parses the name for a given test // parseName parses the name for a given test
func parseName(test Test) string { func parseName(test Test) string {

Loading…
Cancel
Save