diff --git a/assertions_test.go b/assertions_test.go new file mode 100644 index 0000000..6d44e00 --- /dev/null +++ b/assertions_test.go @@ -0,0 +1,48 @@ +package tea + +import "testing" + +type labelChecker struct { + name string + wanted map[string]bool // all the strings we want + found map[string]bool // all the strings we've found +} + +func wantStrings(name string, want ...string) labelChecker { + l := newLabelChecker(name) + l.want(want...) + return l +} + +func newLabelChecker(name string) labelChecker { + return labelChecker{ + name: name, + wanted: make(map[string]bool), + found: make(map[string]bool), + } +} + +func (l *labelChecker) want(names ...string) { + for _, name := range names { + l.wanted[name] = true + } +} + +func (l *labelChecker) add(name string) { + l.found[name] = true +} + +func (l *labelChecker) report(t *testing.T) { + for name, _ := range l.found { + if l.wanted[name] { + t.Logf("%s saw expected value %s", l.name, name) + } else { + t.Errorf("%s saw unexpected value %s", l.name, name) + } + } + for name, _ := range l.wanted { + if !l.found[name] { + t.Errorf("%s missing expected value %s", l.name, name) + } + } +} diff --git a/node.go b/node.go index eca61ab..1d6c35a 100644 --- a/node.go +++ b/node.go @@ -37,6 +37,7 @@ func newLNode(test Test, sel Selection) *lnode { test: test, parents: make([]*lnode, len(sel.nodes)), } + // not sure if this copy is necessary copy(node.parents, sel.nodes) xID := 0 @@ -86,6 +87,9 @@ func (x *xnode) isOnlyTestInLNode() bool { return len(x.lnode.xnodes) == 1 } +// label must be unique or some other shit will break, I'm using this as a way +// to globally identify xnodes, which may be very flawed and maybe I should +// have an actual global ID system. func (x *xnode) label() string { if x.parent == nil { switch { @@ -113,7 +117,8 @@ func (x *xnode) label() string { } // ancestry gives a slice of xnodes beginning at the root of the x graph and -// terminating at the receiver xnode. +// terminating at the receiver xnode. The ancestry list of a leaf node in the x +// graph is a single chain of tests. func (x *xnode) ancestry() []*xnode { if x.parent == nil { return []*xnode{x} @@ -135,3 +140,19 @@ func (x *xnode) descendents() []*xnode { } return descendents } + +// leaves descends the x graph from the receiver xnode, returning a slice +// containing all of the leaves of the x graph having the receiver x as an +// ancestor. +func (x *xnode) leaves() []*xnode { + if len(x.children) == 0 { + return []*xnode{x} + } + + var leaves []*xnode + for _, child := range x.children { + leaves = append(leaves, child.leaves()...) + } + + return leaves +} diff --git a/selection.go b/selection.go index a738910..49b3efd 100644 --- a/selection.go +++ b/selection.go @@ -47,3 +47,27 @@ func (s Selection) countXNodes() int { } return total } + +// xleaves looks at all of the selected xnodes, and for every selected xnode, +// traverses the x graph until we arrive at the set of all leaf nodes that have +// a selected ancestor. If the selection consists of the root node, the xleaves +// are all of the leaves of the x graph. +func (s *Selection) xleaves() []*xnode { + // honestly think that by definition every xnode in the selection has a + // non-overlapping set of leaves but thinking about this shit is extremely + // starting to hurt my brain so I'm going to write this in a way that's + // maybe very redundant. + + seen := make(map[string]bool) + var leaves []*xnode + for _, x := range s.xnodes() { + for _, leaf := range x.leaves() { + if seen[leaf.label()] { + panic("double-counting leaves somehow") + } + seen[leaf.label()] = true + leaves = append(leaves, leaf) + } + } + return leaves +} diff --git a/selection_test.go b/selection_test.go index f911619..48c5654 100644 --- a/selection_test.go +++ b/selection_test.go @@ -9,56 +9,31 @@ type selectionTest struct { selection Selection lnodes []string xnodes []string + xleaves []string } func (test *selectionTest) Run(t *testing.T) { - lfound := make(map[string]bool) + LWanted := wantStrings("selected lnode names", test.lnodes...) 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 + LWanted.add(L.name) } + LWanted.report(t) - for _, expected := range test.lnodes { - if lfound[expected] { - delete(lfound, expected) - } else { - t.Errorf("missing expected lnode with label %s", expected) - } + XWanted := wantStrings("selected xnode labels", test.xnodes...) + for _, X := range test.selection.xnodes() { + XWanted.add(X.label()) } + XWanted.report(t) - 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 - } + // XLeavesWanted := wantStrings("leaf xnode labels", test.xleaves...) + // for _, X := range test.selection.xnodes() { + // leaves := X.leaves() + // for _, leaf := range leaves { + // XLeavesWanted. - for _, expected := range test.xnodes { - if xfound[expected] { - delete(xfound, expected) - } else { - t.Errorf("missing expected xnode with label %s", expected) - } - } + // } - for label, _ := range xfound { - t.Errorf("found unexpected xnode with label %s", label) - } + // } } func TestSelections(t *testing.T) { @@ -145,6 +120,20 @@ func TestSelections(t *testing.T) { } }) + add(func() selectionTest { + root := NewSelection(A) + b := root.Child(B) + c := root.Child(C) + d := b.And(c).Child(D) + d.Child(E) + return selectionTest{ + label: "the root of a complex graph", + selection: root, + lnodes: []string{"A"}, + xnodes: []string{"A.0"}, + } + }) + for _, test := range tests { t.Run(test.label, test.Run) }