github.com/paultyng/terraform@v0.6.11-0.20180227224804-66ff8f8bed40/dag/dag_test.go (about)

     1  package dag
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"log"
     8  	"os"
     9  	"reflect"
    10  	"strconv"
    11  	"strings"
    12  	"sync"
    13  	"testing"
    14  
    15  	"github.com/hashicorp/terraform/helper/logging"
    16  )
    17  
    18  func TestMain(m *testing.M) {
    19  	flag.Parse()
    20  	if testing.Verbose() {
    21  		// if we're verbose, use the logging requested by TF_LOG
    22  		logging.SetOutput()
    23  	} else {
    24  		// otherwise silence all logs
    25  		log.SetOutput(ioutil.Discard)
    26  	}
    27  
    28  	os.Exit(m.Run())
    29  }
    30  
    31  func TestAcyclicGraphRoot(t *testing.T) {
    32  	var g AcyclicGraph
    33  	g.Add(1)
    34  	g.Add(2)
    35  	g.Add(3)
    36  	g.Connect(BasicEdge(3, 2))
    37  	g.Connect(BasicEdge(3, 1))
    38  
    39  	if root, err := g.Root(); err != nil {
    40  		t.Fatalf("err: %s", err)
    41  	} else if root != 3 {
    42  		t.Fatalf("bad: %#v", root)
    43  	}
    44  }
    45  
    46  func TestAcyclicGraphRoot_cycle(t *testing.T) {
    47  	var g AcyclicGraph
    48  	g.Add(1)
    49  	g.Add(2)
    50  	g.Add(3)
    51  	g.Connect(BasicEdge(1, 2))
    52  	g.Connect(BasicEdge(2, 3))
    53  	g.Connect(BasicEdge(3, 1))
    54  
    55  	if _, err := g.Root(); err == nil {
    56  		t.Fatal("should error")
    57  	}
    58  }
    59  
    60  func TestAcyclicGraphRoot_multiple(t *testing.T) {
    61  	var g AcyclicGraph
    62  	g.Add(1)
    63  	g.Add(2)
    64  	g.Add(3)
    65  	g.Connect(BasicEdge(3, 2))
    66  
    67  	if _, err := g.Root(); err == nil {
    68  		t.Fatal("should error")
    69  	}
    70  }
    71  
    72  func TestAyclicGraphTransReduction(t *testing.T) {
    73  	var g AcyclicGraph
    74  	g.Add(1)
    75  	g.Add(2)
    76  	g.Add(3)
    77  	g.Connect(BasicEdge(1, 2))
    78  	g.Connect(BasicEdge(1, 3))
    79  	g.Connect(BasicEdge(2, 3))
    80  	g.TransitiveReduction()
    81  
    82  	actual := strings.TrimSpace(g.String())
    83  	expected := strings.TrimSpace(testGraphTransReductionStr)
    84  	if actual != expected {
    85  		t.Fatalf("bad: %s", actual)
    86  	}
    87  }
    88  
    89  func TestAyclicGraphTransReduction_more(t *testing.T) {
    90  	var g AcyclicGraph
    91  	g.Add(1)
    92  	g.Add(2)
    93  	g.Add(3)
    94  	g.Add(4)
    95  	g.Connect(BasicEdge(1, 2))
    96  	g.Connect(BasicEdge(1, 3))
    97  	g.Connect(BasicEdge(1, 4))
    98  	g.Connect(BasicEdge(2, 3))
    99  	g.Connect(BasicEdge(2, 4))
   100  	g.Connect(BasicEdge(3, 4))
   101  	g.TransitiveReduction()
   102  
   103  	actual := strings.TrimSpace(g.String())
   104  	expected := strings.TrimSpace(testGraphTransReductionMoreStr)
   105  	if actual != expected {
   106  		t.Fatalf("bad: %s", actual)
   107  	}
   108  }
   109  
   110  // use this to simulate slow sort operations
   111  type counter struct {
   112  	Name  string
   113  	Calls int64
   114  }
   115  
   116  func (s *counter) String() string {
   117  	s.Calls++
   118  	return s.Name
   119  }
   120  
   121  // Make sure we can reduce a sizable, fully-connected graph.
   122  func TestAyclicGraphTransReduction_fullyConnected(t *testing.T) {
   123  	var g AcyclicGraph
   124  
   125  	const nodeCount = 200
   126  	nodes := make([]*counter, nodeCount)
   127  	for i := 0; i < nodeCount; i++ {
   128  		nodes[i] = &counter{Name: strconv.Itoa(i)}
   129  	}
   130  
   131  	// Add them all to the graph
   132  	for _, n := range nodes {
   133  		g.Add(n)
   134  	}
   135  
   136  	// connect them all
   137  	for i := range nodes {
   138  		for j := range nodes {
   139  			if i == j {
   140  				continue
   141  			}
   142  			g.Connect(BasicEdge(nodes[i], nodes[j]))
   143  		}
   144  	}
   145  
   146  	g.TransitiveReduction()
   147  
   148  	vertexNameCalls := int64(0)
   149  	for _, n := range nodes {
   150  		vertexNameCalls += n.Calls
   151  	}
   152  
   153  	switch {
   154  	case vertexNameCalls > 2*nodeCount:
   155  		// Make calling it more the 2x per node fatal.
   156  		// If we were sorting this would give us roughly ln(n)(n^3) calls, or
   157  		// >59000000 calls for 200 vertices.
   158  		t.Fatalf("VertexName called %d times", vertexNameCalls)
   159  	case vertexNameCalls > 0:
   160  		// we don't expect any calls, but a change here isn't necessarily fatal
   161  		t.Logf("WARNING: VertexName called %d times", vertexNameCalls)
   162  	}
   163  }
   164  
   165  func TestAcyclicGraphValidate(t *testing.T) {
   166  	var g AcyclicGraph
   167  	g.Add(1)
   168  	g.Add(2)
   169  	g.Add(3)
   170  	g.Connect(BasicEdge(3, 2))
   171  	g.Connect(BasicEdge(3, 1))
   172  
   173  	if err := g.Validate(); err != nil {
   174  		t.Fatalf("err: %s", err)
   175  	}
   176  }
   177  
   178  func TestAcyclicGraphValidate_cycle(t *testing.T) {
   179  	var g AcyclicGraph
   180  	g.Add(1)
   181  	g.Add(2)
   182  	g.Add(3)
   183  	g.Connect(BasicEdge(3, 2))
   184  	g.Connect(BasicEdge(3, 1))
   185  	g.Connect(BasicEdge(1, 2))
   186  	g.Connect(BasicEdge(2, 1))
   187  
   188  	if err := g.Validate(); err == nil {
   189  		t.Fatal("should error")
   190  	}
   191  }
   192  
   193  func TestAcyclicGraphValidate_cycleSelf(t *testing.T) {
   194  	var g AcyclicGraph
   195  	g.Add(1)
   196  	g.Add(2)
   197  	g.Connect(BasicEdge(1, 1))
   198  
   199  	if err := g.Validate(); err == nil {
   200  		t.Fatal("should error")
   201  	}
   202  }
   203  
   204  func TestAcyclicGraphAncestors(t *testing.T) {
   205  	var g AcyclicGraph
   206  	g.Add(1)
   207  	g.Add(2)
   208  	g.Add(3)
   209  	g.Add(4)
   210  	g.Add(5)
   211  	g.Connect(BasicEdge(0, 1))
   212  	g.Connect(BasicEdge(1, 2))
   213  	g.Connect(BasicEdge(2, 3))
   214  	g.Connect(BasicEdge(3, 4))
   215  	g.Connect(BasicEdge(4, 5))
   216  
   217  	actual, err := g.Ancestors(2)
   218  	if err != nil {
   219  		t.Fatalf("err: %#v", err)
   220  	}
   221  
   222  	expected := []Vertex{3, 4, 5}
   223  
   224  	if actual.Len() != len(expected) {
   225  		t.Fatalf("bad length! expected %#v to have len %d", actual, len(expected))
   226  	}
   227  
   228  	for _, e := range expected {
   229  		if !actual.Include(e) {
   230  			t.Fatalf("expected: %#v to include: %#v", expected, actual)
   231  		}
   232  	}
   233  }
   234  
   235  func TestAcyclicGraphDescendents(t *testing.T) {
   236  	var g AcyclicGraph
   237  	g.Add(1)
   238  	g.Add(2)
   239  	g.Add(3)
   240  	g.Add(4)
   241  	g.Add(5)
   242  	g.Connect(BasicEdge(0, 1))
   243  	g.Connect(BasicEdge(1, 2))
   244  	g.Connect(BasicEdge(2, 3))
   245  	g.Connect(BasicEdge(3, 4))
   246  	g.Connect(BasicEdge(4, 5))
   247  
   248  	actual, err := g.Descendents(2)
   249  	if err != nil {
   250  		t.Fatalf("err: %#v", err)
   251  	}
   252  
   253  	expected := []Vertex{0, 1}
   254  
   255  	if actual.Len() != len(expected) {
   256  		t.Fatalf("bad length! expected %#v to have len %d", actual, len(expected))
   257  	}
   258  
   259  	for _, e := range expected {
   260  		if !actual.Include(e) {
   261  			t.Fatalf("expected: %#v to include: %#v", expected, actual)
   262  		}
   263  	}
   264  }
   265  
   266  func TestAcyclicGraphWalk(t *testing.T) {
   267  	var g AcyclicGraph
   268  	g.Add(1)
   269  	g.Add(2)
   270  	g.Add(3)
   271  	g.Connect(BasicEdge(3, 2))
   272  	g.Connect(BasicEdge(3, 1))
   273  
   274  	var visits []Vertex
   275  	var lock sync.Mutex
   276  	err := g.Walk(func(v Vertex) error {
   277  		lock.Lock()
   278  		defer lock.Unlock()
   279  		visits = append(visits, v)
   280  		return nil
   281  	})
   282  	if err != nil {
   283  		t.Fatalf("err: %s", err)
   284  	}
   285  
   286  	expected := [][]Vertex{
   287  		{1, 2, 3},
   288  		{2, 1, 3},
   289  	}
   290  	for _, e := range expected {
   291  		if reflect.DeepEqual(visits, e) {
   292  			return
   293  		}
   294  	}
   295  
   296  	t.Fatalf("bad: %#v", visits)
   297  }
   298  
   299  func TestAcyclicGraphWalk_error(t *testing.T) {
   300  	var g AcyclicGraph
   301  	g.Add(1)
   302  	g.Add(2)
   303  	g.Add(3)
   304  	g.Add(4)
   305  	g.Connect(BasicEdge(4, 3))
   306  	g.Connect(BasicEdge(3, 2))
   307  	g.Connect(BasicEdge(2, 1))
   308  
   309  	var visits []Vertex
   310  	var lock sync.Mutex
   311  	err := g.Walk(func(v Vertex) error {
   312  		lock.Lock()
   313  		defer lock.Unlock()
   314  
   315  		if v == 2 {
   316  			return fmt.Errorf("error")
   317  		}
   318  
   319  		visits = append(visits, v)
   320  		return nil
   321  	})
   322  	if err == nil {
   323  		t.Fatal("should error")
   324  	}
   325  
   326  	expected := [][]Vertex{
   327  		{1},
   328  	}
   329  	for _, e := range expected {
   330  		if reflect.DeepEqual(visits, e) {
   331  			return
   332  		}
   333  	}
   334  
   335  	t.Fatalf("bad: %#v", visits)
   336  }
   337  
   338  func TestAcyclicGraph_ReverseDepthFirstWalk_WithRemoval(t *testing.T) {
   339  	var g AcyclicGraph
   340  	g.Add(1)
   341  	g.Add(2)
   342  	g.Add(3)
   343  	g.Connect(BasicEdge(3, 2))
   344  	g.Connect(BasicEdge(2, 1))
   345  
   346  	var visits []Vertex
   347  	var lock sync.Mutex
   348  	err := g.ReverseDepthFirstWalk([]Vertex{1}, func(v Vertex, d int) error {
   349  		lock.Lock()
   350  		defer lock.Unlock()
   351  		visits = append(visits, v)
   352  		g.Remove(v)
   353  		return nil
   354  	})
   355  	if err != nil {
   356  		t.Fatalf("err: %s", err)
   357  	}
   358  
   359  	expected := []Vertex{1, 2, 3}
   360  	if !reflect.DeepEqual(visits, expected) {
   361  		t.Fatalf("expected: %#v, got: %#v", expected, visits)
   362  	}
   363  }
   364  
   365  const testGraphTransReductionStr = `
   366  1
   367    2
   368  2
   369    3
   370  3
   371  `
   372  
   373  const testGraphTransReductionMoreStr = `
   374  1
   375    2
   376  2
   377    3
   378  3
   379    4
   380  4
   381  `