// Copyright ©2017 The Gonum Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package flow import ( "reflect" "testing" "gonum.org/v1/gonum/graph" "gonum.org/v1/gonum/graph/simple" "gonum.org/v1/gonum/internal/order" ) var dominatorsTests = []struct { n graph.Node edges []simple.Edge want DominatorTree }{ { // Example from Lengauer and Tarjan http://www.dtic.mil/dtic/tr/fulltext/u2/a054144.pdf fig 1. n: char('R'), edges: []simple.Edge{ {F: char('A'), T: char('D')}, {F: char('B'), T: char('A')}, {F: char('B'), T: char('D')}, {F: char('B'), T: char('E')}, {F: char('C'), T: char('F')}, {F: char('C'), T: char('G')}, {F: char('D'), T: char('L')}, {F: char('E'), T: char('H')}, {F: char('F'), T: char('I')}, {F: char('G'), T: char('I')}, {F: char('G'), T: char('J')}, {F: char('H'), T: char('E')}, {F: char('H'), T: char('K')}, {F: char('I'), T: char('K')}, {F: char('J'), T: char('I')}, {F: char('K'), T: char('I')}, {F: char('K'), T: char('R')}, {F: char('L'), T: char('H')}, {F: char('R'), T: char('A')}, {F: char('R'), T: char('B')}, {F: char('R'), T: char('C')}, }, want: DominatorTree{ root: char('R'), dominatorOf: map[int64]graph.Node{ 'A': char('R'), 'B': char('R'), 'C': char('R'), 'D': char('R'), 'E': char('R'), 'F': char('C'), 'G': char('C'), 'H': char('R'), 'I': char('R'), 'J': char('G'), 'K': char('R'), 'L': char('D'), }, dominatedBy: map[int64][]graph.Node{ 'C': {char('F'), char('G')}, 'D': {char('L')}, 'G': {char('J')}, 'R': {char('A'), char('B'), char('C'), char('D'), char('E'), char('H'), char('I'), char('K')}, }, }, }, { // WP example: https://en.wikipedia.org/w/index.php?title=Dominator_(graph_theory)&oldid=758099236. n: simple.Node(1), edges: []simple.Edge{ {F: simple.Node(1), T: simple.Node(2)}, {F: simple.Node(2), T: simple.Node(3)}, {F: simple.Node(2), T: simple.Node(4)}, {F: simple.Node(2), T: simple.Node(6)}, {F: simple.Node(3), T: simple.Node(5)}, {F: simple.Node(4), T: simple.Node(5)}, {F: simple.Node(5), T: simple.Node(2)}, }, want: DominatorTree{ root: simple.Node(1), dominatorOf: map[int64]graph.Node{ 2: simple.Node(1), 3: simple.Node(2), 4: simple.Node(2), 5: simple.Node(2), 6: simple.Node(2), }, dominatedBy: map[int64][]graph.Node{ 1: {simple.Node(2)}, 2: {simple.Node(3), simple.Node(4), simple.Node(5), simple.Node(6)}, }, }, }, { // WP example with node IDs decremented by 1. n: simple.Node(0), edges: []simple.Edge{ {F: simple.Node(0), T: simple.Node(1)}, {F: simple.Node(1), T: simple.Node(2)}, {F: simple.Node(1), T: simple.Node(3)}, {F: simple.Node(1), T: simple.Node(5)}, {F: simple.Node(2), T: simple.Node(4)}, {F: simple.Node(3), T: simple.Node(4)}, {F: simple.Node(4), T: simple.Node(1)}, }, want: DominatorTree{ root: simple.Node(0), dominatorOf: map[int64]graph.Node{ 1: simple.Node(0), 2: simple.Node(1), 3: simple.Node(1), 4: simple.Node(1), 5: simple.Node(1), }, dominatedBy: map[int64][]graph.Node{ 0: {simple.Node(1)}, 1: {simple.Node(2), simple.Node(3), simple.Node(4), simple.Node(5)}, }, }, }, } type char rune func (n char) ID() int64 { return int64(n) } func (n char) String() string { return string(n) } func TestDominators(t *testing.T) { // The dominator functions are non-deterministic due // to map iteration ordering, so repeat the tests to // ensure consistent coverage. The value of 100 was // chosen empirically to have no observed reduction // in coverage in several hundred runs of go test -cover. for i := 0; i < 100; i++ { for _, test := range dominatorsTests { g := simple.NewDirectedGraph() for _, e := range test.edges { g.SetEdge(e) } for _, alg := range []struct { name string fn func(graph.Node, graph.Directed) DominatorTree }{ {"Dominators", Dominators}, {"DominatorsSLT", DominatorsSLT}, } { got := alg.fn(test.n, g) if !reflect.DeepEqual(got.Root(), test.want.root) { t.Errorf("unexpected dominator tree root from %s: got:%v want:%v", alg.name, got.root, test.want.root) } if !reflect.DeepEqual(got.dominatorOf, test.want.dominatorOf) { t.Errorf("unexpected dominator tree from %s: got:%v want:%v", alg.name, got.dominatorOf, test.want.dominatorOf) } for q, want := range test.want.dominatorOf { node := got.DominatorOf(q) if node != want { t.Errorf("unexpected dominator tree result from %s dominated of %v: got:%v want:%v", alg.name, q, node, want) } } for _, nodes := range got.dominatedBy { order.ByID(nodes) } if !reflect.DeepEqual(got.dominatedBy, test.want.dominatedBy) { t.Errorf("unexpected dominator tree from %s: got:%v want:%v", alg.name, got.dominatedBy, test.want.dominatedBy) } for q, want := range test.want.dominatedBy { nodes := got.DominatedBy(q) if !reflect.DeepEqual(nodes, want) { t.Errorf("unexpected dominator tree result from %s dominated by %v: got:%v want:%v", alg.name, q, nodes, want) } } } } } }