more notes on how multiparent tests might work

g-counter
Jordan Orelli 4 years ago
parent 30bdf631c4
commit 6a1e87f67e

@ -276,29 +276,78 @@ func TestMatch(t *testing.T) {
// Constructing a test node that has multiple parents: // Constructing a test node that has multiple parents:
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// //
// In this example, B is an optional test. // So far the hardest thing conceptually has been figuring out how we might
// construct a test that has two parents. This is entirely unhandled by
// existing test frameworks to my knowledge, but is extremely useful in
// determining that a part of our system has no effect on another part of our
// system.
//
// Here is an example of a small test graph in which B is an optional test.
// //
// Logical Execution // Logical Execution
// //
// A A // A A
// /| / \ // /| / \
// / | / \ // / | / \
// B | ----> B C' // B | ----> B C
// \ | | // \ | |
// \| | // \| |
// C C // C C
// //
// This logical graph of test dependencies would yield an execution plan // On the left, we have a graph representing the logical relationship between
// consisting of two test chains: // the tests. On the right, a graph representing the relationship between how
// the tests would be executed. The test C has two parents, which means it is
// represented twice in the execution plan, as it will be run separately for
// each parent: once following A, and once following B. The shape of this test
// can be used to confirm that test C will pass both in the event that test B
// has been run and test B has not been run. This is used to confirm that the
// portion of our system tested by B does not violate the invariants of the
// portion of our system tested by C.
//
// The execution plan for this set of tests would consiste of the following
// test chains:
// //
// A -> B -> C // A -> B -> C
// A -> C // A ------> C
//
// Which go test -v would output as:
//
// === RUN A
// === RUN A/B
// === RUN A/B/C
// === RUN A/C
// --- PASS: A
// --- PASS: A/B
// --- PASS: A/B/C
// --- PASS: A/C
//
// My idea for how this is solved is to rename tea.Tree to tea.Selection, since
// it would represent something different entirely. Everywhere that tea.Tree
// appears, we would use tea.Selection instead. A selection is defined as a set
// of nodes in the test graph. A selection would have a Child method as
// tea.Tree does now, and what it would do is add to every node in the
// selection the provided test as a child. We would define an additional method
// Add on the selection, which takes another selection and returns a selection
// whose selected nodes is the union of the two input selections. More simply:
// you can add selections together.
// //
// We could write this as follows: // We could write this as follows:
// //
// root := New(A) // 1 root := New(A)
// b := root.Child(B) // 2 b := root.Child(B)
// root.And(b).Child(C) // 3 both := root.And(b)
// 4 leaves := both.Child(C)
//
// line 1: root is a selection consisting of one node. That node contains
// test A.
// line 2: b is a selection consisting of one node. That node contains
// test B. The node is a child of the root node.
// line 3: both is a selection consisting of both of the nodes that
// currently exist in the graph.
// line 4: we add a new node to the graph for every node in the input
// selection. leaves is a new selection, consisting of the two added
// nodes, both of which contain the value of C, but having different
// parents.
// //
// Alternatively: // Alternatively:
// //
@ -314,7 +363,10 @@ func TestMatch(t *testing.T) {
// This last form is not strictly the same, since it includes an additional // This last form is not strictly the same, since it includes an additional
// node in the graph which is a passing test. However since Pass is a // node in the graph which is a passing test. However since Pass is a
// specific example, we can trivially remove nodes having a test value of // specific example, we can trivially remove nodes having a test value of
// Pass in the planning phase. // Pass in the planning phase. I'm not sure if I like this. I've tripped
// myself up thinking about it because I keep forgetting that Child does not
// make a sequence. Perhaps "Child" is no longer the right name for this
// method.
// //
// Another simple example: a diamond-shaped test graph // Another simple example: a diamond-shaped test graph
// //
@ -329,8 +381,22 @@ func TestMatch(t *testing.T) {
// D D D' // D D D'
// //
// Test Plan: // Test Plan:
// - A -> B -> D //
// - A -> C -> D // A -> B -> D
// A -> C -> D
//
// go test -v output:
//
// === RUN A
// === RUN A/B
// === RUN A/B/D
// === RUN A/C
// === RUN A/C/D
// --- PASS: A
// --- PASS: A/B
// --- PASS: A/B/D
// --- PASS: A/C
// --- PASS: A/C/D
// //
// Expressed in test code as follows: // Expressed in test code as follows:
// //
@ -356,15 +422,25 @@ func TestMatch(t *testing.T) {
// E D // E D
// //
// Test Plan: // Test Plan:
// - A -> B -> E
// - A -> B -> D
// - A -> C -> D
// //
// Essentially what we're saying is: // A -> B -> E
// Run test A. // A -> B -> D
// If test A passes: // A -> C -> D
// Run test B. //
// Run test C. // go test -v output:
//
// === RUN A
// === RUN A/B
// === RUN A/B/E
// === RUN A/B/D
// === RUN A/C
// === RUN A/C/D
// --- PASS: A
// --- PASS: A/B
// --- PASS: A/B/E
// --- PASS: A/B/D
// --- PASS: A/C
// --- PASS: A/C/D
// //
// Expressed as: // Expressed as:
// //
@ -374,3 +450,5 @@ func TestMatch(t *testing.T) {
// b.Child(E) // b.Child(E)
// b.And(c).Child(D) // b.And(c).Child(D)
// //
// The ergonomics with this case are quite poor. I'm not sure how to improve
// them.

Loading…
Cancel
Save