github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/dag/dag_test.go (about)

     1  package dag
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"reflect"
     8  	"strconv"
     9  	"strings"
    10  	"sync"
    11  	"testing"
    12  
    13  	"github.com/hashicorp/terraform/internal/tfdiags"
    14  
    15  	_ "github.com/hashicorp/terraform/internal/logging"
    16  )
    17  
    18  func TestMain(m *testing.M) {
    19  	flag.Parse()
    20  	os.Exit(m.Run())
    21  }
    22  
    23  func TestAcyclicGraphRoot(t *testing.T) {
    24  	var g AcyclicGraph
    25  	g.Add(1)
    26  	g.Add(2)
    27  	g.Add(3)
    28  	g.Connect(BasicEdge(3, 2))
    29  	g.Connect(BasicEdge(3, 1))
    30  
    31  	if root, err := g.Root(); err != nil {
    32  		t.Fatalf("err: %s", err)
    33  	} else if root != 3 {
    34  		t.Fatalf("bad: %#v", root)
    35  	}
    36  }
    37  
    38  func TestAcyclicGraphRoot_cycle(t *testing.T) {
    39  	var g AcyclicGraph
    40  	g.Add(1)
    41  	g.Add(2)
    42  	g.Add(3)
    43  	g.Connect(BasicEdge(1, 2))
    44  	g.Connect(BasicEdge(2, 3))
    45  	g.Connect(BasicEdge(3, 1))
    46  
    47  	if _, err := g.Root(); err == nil {
    48  		t.Fatal("should error")
    49  	}
    50  }
    51  
    52  func TestAcyclicGraphRoot_multiple(t *testing.T) {
    53  	var g AcyclicGraph
    54  	g.Add(1)
    55  	g.Add(2)
    56  	g.Add(3)
    57  	g.Connect(BasicEdge(3, 2))
    58  
    59  	if _, err := g.Root(); err == nil {
    60  		t.Fatal("should error")
    61  	}
    62  }
    63  
    64  func TestAyclicGraphTransReduction(t *testing.T) {
    65  	var g AcyclicGraph
    66  	g.Add(1)
    67  	g.Add(2)
    68  	g.Add(3)
    69  	g.Connect(BasicEdge(1, 2))
    70  	g.Connect(BasicEdge(1, 3))
    71  	g.Connect(BasicEdge(2, 3))
    72  	g.TransitiveReduction()
    73  
    74  	actual := strings.TrimSpace(g.String())
    75  	expected := strings.TrimSpace(testGraphTransReductionStr)
    76  	if actual != expected {
    77  		t.Fatalf("bad: %s", actual)
    78  	}
    79  }
    80  
    81  func TestAyclicGraphTransReduction_more(t *testing.T) {
    82  	var g AcyclicGraph
    83  	g.Add(1)
    84  	g.Add(2)
    85  	g.Add(3)
    86  	g.Add(4)
    87  	g.Connect(BasicEdge(1, 2))
    88  	g.Connect(BasicEdge(1, 3))
    89  	g.Connect(BasicEdge(1, 4))
    90  	g.Connect(BasicEdge(2, 3))
    91  	g.Connect(BasicEdge(2, 4))
    92  	g.Connect(BasicEdge(3, 4))
    93  	g.TransitiveReduction()
    94  
    95  	actual := strings.TrimSpace(g.String())
    96  	expected := strings.TrimSpace(testGraphTransReductionMoreStr)
    97  	if actual != expected {
    98  		t.Fatalf("bad: %s", actual)
    99  	}
   100  }
   101  
   102  func TestAyclicGraphTransReduction_multipleRoots(t *testing.T) {
   103  	var g AcyclicGraph
   104  	g.Add(1)
   105  	g.Add(2)
   106  	g.Add(3)
   107  	g.Add(4)
   108  	g.Connect(BasicEdge(1, 2))
   109  	g.Connect(BasicEdge(1, 3))
   110  	g.Connect(BasicEdge(1, 4))
   111  	g.Connect(BasicEdge(2, 3))
   112  	g.Connect(BasicEdge(2, 4))
   113  	g.Connect(BasicEdge(3, 4))
   114  
   115  	g.Add(5)
   116  	g.Add(6)
   117  	g.Add(7)
   118  	g.Add(8)
   119  	g.Connect(BasicEdge(5, 6))
   120  	g.Connect(BasicEdge(5, 7))
   121  	g.Connect(BasicEdge(5, 8))
   122  	g.Connect(BasicEdge(6, 7))
   123  	g.Connect(BasicEdge(6, 8))
   124  	g.Connect(BasicEdge(7, 8))
   125  	g.TransitiveReduction()
   126  
   127  	actual := strings.TrimSpace(g.String())
   128  	expected := strings.TrimSpace(testGraphTransReductionMultipleRootsStr)
   129  	if actual != expected {
   130  		t.Fatalf("bad: %s", actual)
   131  	}
   132  }
   133  
   134  // use this to simulate slow sort operations
   135  type counter struct {
   136  	Name  string
   137  	Calls int64
   138  }
   139  
   140  func (s *counter) String() string {
   141  	s.Calls++
   142  	return s.Name
   143  }
   144  
   145  // Make sure we can reduce a sizable, fully-connected graph.
   146  func TestAyclicGraphTransReduction_fullyConnected(t *testing.T) {
   147  	var g AcyclicGraph
   148  
   149  	const nodeCount = 200
   150  	nodes := make([]*counter, nodeCount)
   151  	for i := 0; i < nodeCount; i++ {
   152  		nodes[i] = &counter{Name: strconv.Itoa(i)}
   153  	}
   154  
   155  	// Add them all to the graph
   156  	for _, n := range nodes {
   157  		g.Add(n)
   158  	}
   159  
   160  	// connect them all
   161  	for i := range nodes {
   162  		for j := range nodes {
   163  			if i == j {
   164  				continue
   165  			}
   166  			g.Connect(BasicEdge(nodes[i], nodes[j]))
   167  		}
   168  	}
   169  
   170  	g.TransitiveReduction()
   171  
   172  	vertexNameCalls := int64(0)
   173  	for _, n := range nodes {
   174  		vertexNameCalls += n.Calls
   175  	}
   176  
   177  	switch {
   178  	case vertexNameCalls > 2*nodeCount:
   179  		// Make calling it more the 2x per node fatal.
   180  		// If we were sorting this would give us roughly ln(n)(n^3) calls, or
   181  		// >59000000 calls for 200 vertices.
   182  		t.Fatalf("VertexName called %d times", vertexNameCalls)
   183  	case vertexNameCalls > 0:
   184  		// we don't expect any calls, but a change here isn't necessarily fatal
   185  		t.Logf("WARNING: VertexName called %d times", vertexNameCalls)
   186  	}
   187  }
   188  
   189  func TestAcyclicGraphValidate(t *testing.T) {
   190  	var g AcyclicGraph
   191  	g.Add(1)
   192  	g.Add(2)
   193  	g.Add(3)
   194  	g.Connect(BasicEdge(3, 2))
   195  	g.Connect(BasicEdge(3, 1))
   196  
   197  	if err := g.Validate(); err != nil {
   198  		t.Fatalf("err: %s", err)
   199  	}
   200  }
   201  
   202  func TestAcyclicGraphValidate_cycle(t *testing.T) {
   203  	var g AcyclicGraph
   204  	g.Add(1)
   205  	g.Add(2)
   206  	g.Add(3)
   207  	g.Connect(BasicEdge(3, 2))
   208  	g.Connect(BasicEdge(3, 1))
   209  	g.Connect(BasicEdge(1, 2))
   210  	g.Connect(BasicEdge(2, 1))
   211  
   212  	if err := g.Validate(); err == nil {
   213  		t.Fatal("should error")
   214  	}
   215  }
   216  
   217  func TestAcyclicGraphValidate_cycleSelf(t *testing.T) {
   218  	var g AcyclicGraph
   219  	g.Add(1)
   220  	g.Add(2)
   221  	g.Connect(BasicEdge(1, 1))
   222  
   223  	if err := g.Validate(); err == nil {
   224  		t.Fatal("should error")
   225  	}
   226  }
   227  
   228  func TestAcyclicGraphAncestors(t *testing.T) {
   229  	var g AcyclicGraph
   230  	g.Add(1)
   231  	g.Add(2)
   232  	g.Add(3)
   233  	g.Add(4)
   234  	g.Add(5)
   235  	g.Connect(BasicEdge(0, 1))
   236  	g.Connect(BasicEdge(1, 2))
   237  	g.Connect(BasicEdge(2, 3))
   238  	g.Connect(BasicEdge(3, 4))
   239  	g.Connect(BasicEdge(4, 5))
   240  
   241  	actual, err := g.Ancestors(2)
   242  	if err != nil {
   243  		t.Fatalf("err: %#v", err)
   244  	}
   245  
   246  	expected := []Vertex{3, 4, 5}
   247  
   248  	if actual.Len() != len(expected) {
   249  		t.Fatalf("bad length! expected %#v to have len %d", actual, len(expected))
   250  	}
   251  
   252  	for _, e := range expected {
   253  		if !actual.Include(e) {
   254  			t.Fatalf("expected: %#v to include: %#v", expected, actual)
   255  		}
   256  	}
   257  }
   258  
   259  func TestAcyclicGraphDescendents(t *testing.T) {
   260  	var g AcyclicGraph
   261  	g.Add(1)
   262  	g.Add(2)
   263  	g.Add(3)
   264  	g.Add(4)
   265  	g.Add(5)
   266  	g.Connect(BasicEdge(0, 1))
   267  	g.Connect(BasicEdge(1, 2))
   268  	g.Connect(BasicEdge(2, 3))
   269  	g.Connect(BasicEdge(3, 4))
   270  	g.Connect(BasicEdge(4, 5))
   271  
   272  	actual, err := g.Descendents(2)
   273  	if err != nil {
   274  		t.Fatalf("err: %#v", err)
   275  	}
   276  
   277  	expected := []Vertex{0, 1}
   278  
   279  	if actual.Len() != len(expected) {
   280  		t.Fatalf("bad length! expected %#v to have len %d", actual, len(expected))
   281  	}
   282  
   283  	for _, e := range expected {
   284  		if !actual.Include(e) {
   285  			t.Fatalf("expected: %#v to include: %#v", expected, actual)
   286  		}
   287  	}
   288  }
   289  
   290  func TestAcyclicGraphWalk(t *testing.T) {
   291  	var g AcyclicGraph
   292  	g.Add(1)
   293  	g.Add(2)
   294  	g.Add(3)
   295  	g.Connect(BasicEdge(3, 2))
   296  	g.Connect(BasicEdge(3, 1))
   297  
   298  	var visits []Vertex
   299  	var lock sync.Mutex
   300  	err := g.Walk(func(v Vertex) tfdiags.Diagnostics {
   301  		lock.Lock()
   302  		defer lock.Unlock()
   303  		visits = append(visits, v)
   304  		return nil
   305  	})
   306  	if err != nil {
   307  		t.Fatalf("err: %s", err)
   308  	}
   309  
   310  	expected := [][]Vertex{
   311  		{1, 2, 3},
   312  		{2, 1, 3},
   313  	}
   314  	for _, e := range expected {
   315  		if reflect.DeepEqual(visits, e) {
   316  			return
   317  		}
   318  	}
   319  
   320  	t.Fatalf("bad: %#v", visits)
   321  }
   322  
   323  func TestAcyclicGraphWalk_error(t *testing.T) {
   324  	var g AcyclicGraph
   325  	g.Add(1)
   326  	g.Add(2)
   327  	g.Add(3)
   328  	g.Add(4)
   329  	g.Connect(BasicEdge(4, 3))
   330  	g.Connect(BasicEdge(3, 2))
   331  	g.Connect(BasicEdge(2, 1))
   332  
   333  	var visits []Vertex
   334  	var lock sync.Mutex
   335  	err := g.Walk(func(v Vertex) tfdiags.Diagnostics {
   336  		lock.Lock()
   337  		defer lock.Unlock()
   338  
   339  		var diags tfdiags.Diagnostics
   340  
   341  		if v == 2 {
   342  			diags = diags.Append(fmt.Errorf("error"))
   343  			return diags
   344  		}
   345  
   346  		visits = append(visits, v)
   347  		return diags
   348  	})
   349  	if err == nil {
   350  		t.Fatal("should error")
   351  	}
   352  
   353  	expected := []Vertex{1}
   354  	if !reflect.DeepEqual(visits, expected) {
   355  		t.Errorf("wrong visits\ngot:  %#v\nwant: %#v", visits, expected)
   356  	}
   357  
   358  }
   359  
   360  func BenchmarkDAG(b *testing.B) {
   361  	for i := 0; i < b.N; i++ {
   362  		count := 150
   363  		b.StopTimer()
   364  		g := &AcyclicGraph{}
   365  
   366  		// create 4 layers of fully connected nodes
   367  		// layer A
   368  		for i := 0; i < count; i++ {
   369  			g.Add(fmt.Sprintf("A%d", i))
   370  		}
   371  
   372  		// layer B
   373  		for i := 0; i < count; i++ {
   374  			B := fmt.Sprintf("B%d", i)
   375  			g.Add(B)
   376  			for j := 0; j < count; j++ {
   377  				g.Connect(BasicEdge(B, fmt.Sprintf("A%d", j)))
   378  			}
   379  		}
   380  
   381  		// layer C
   382  		for i := 0; i < count; i++ {
   383  			c := fmt.Sprintf("C%d", i)
   384  			g.Add(c)
   385  			for j := 0; j < count; j++ {
   386  				// connect them to previous layers so we have something that requires reduction
   387  				g.Connect(BasicEdge(c, fmt.Sprintf("A%d", j)))
   388  				g.Connect(BasicEdge(c, fmt.Sprintf("B%d", j)))
   389  			}
   390  		}
   391  
   392  		// layer D
   393  		for i := 0; i < count; i++ {
   394  			d := fmt.Sprintf("D%d", i)
   395  			g.Add(d)
   396  			for j := 0; j < count; j++ {
   397  				g.Connect(BasicEdge(d, fmt.Sprintf("A%d", j)))
   398  				g.Connect(BasicEdge(d, fmt.Sprintf("B%d", j)))
   399  				g.Connect(BasicEdge(d, fmt.Sprintf("C%d", j)))
   400  			}
   401  		}
   402  
   403  		b.StartTimer()
   404  		// Find dependencies for every node
   405  		for _, v := range g.Vertices() {
   406  			_, err := g.Ancestors(v)
   407  			if err != nil {
   408  				b.Fatal(err)
   409  			}
   410  		}
   411  
   412  		// reduce the final graph
   413  		g.TransitiveReduction()
   414  	}
   415  }
   416  
   417  func TestAcyclicGraphWalkOrder(t *testing.T) {
   418  	/* Sample dependency graph,
   419  	   all edges pointing downwards.
   420  	       1    2
   421  	      / \  /  \
   422  	     3    4    5
   423  	    /      \  /
   424  	   6         7
   425  	           / | \
   426  	          8  9  10
   427  	           \ | /
   428  	             11
   429  	*/
   430  
   431  	var g AcyclicGraph
   432  	for i := 1; i <= 11; i++ {
   433  		g.Add(i)
   434  	}
   435  	g.Connect(BasicEdge(1, 3))
   436  	g.Connect(BasicEdge(1, 4))
   437  	g.Connect(BasicEdge(2, 4))
   438  	g.Connect(BasicEdge(2, 5))
   439  	g.Connect(BasicEdge(3, 6))
   440  	g.Connect(BasicEdge(4, 7))
   441  	g.Connect(BasicEdge(5, 7))
   442  	g.Connect(BasicEdge(7, 8))
   443  	g.Connect(BasicEdge(7, 9))
   444  	g.Connect(BasicEdge(7, 10))
   445  	g.Connect(BasicEdge(8, 11))
   446  	g.Connect(BasicEdge(9, 11))
   447  	g.Connect(BasicEdge(10, 11))
   448  
   449  	start := make(Set)
   450  	start.Add(2)
   451  	start.Add(1)
   452  	reverse := make(Set)
   453  	reverse.Add(11)
   454  	reverse.Add(6)
   455  
   456  	t.Run("DepthFirst", func(t *testing.T) {
   457  		var visits []vertexAtDepth
   458  		g.walk(depthFirst|downOrder, true, start, func(v Vertex, d int) error {
   459  			visits = append(visits, vertexAtDepth{v, d})
   460  			return nil
   461  
   462  		})
   463  		expect := []vertexAtDepth{
   464  			{2, 0}, {5, 1}, {7, 2}, {9, 3}, {11, 4}, {8, 3}, {10, 3}, {4, 1}, {1, 0}, {3, 1}, {6, 2},
   465  		}
   466  		if !reflect.DeepEqual(visits, expect) {
   467  			t.Errorf("expected visits:\n%v\ngot:\n%v\n", expect, visits)
   468  		}
   469  	})
   470  	t.Run("ReverseDepthFirst", func(t *testing.T) {
   471  		var visits []vertexAtDepth
   472  		g.walk(depthFirst|upOrder, true, reverse, func(v Vertex, d int) error {
   473  			visits = append(visits, vertexAtDepth{v, d})
   474  			return nil
   475  
   476  		})
   477  		expect := []vertexAtDepth{
   478  			{6, 0}, {3, 1}, {1, 2}, {11, 0}, {9, 1}, {7, 2}, {5, 3}, {2, 4}, {4, 3}, {8, 1}, {10, 1},
   479  		}
   480  		if !reflect.DeepEqual(visits, expect) {
   481  			t.Errorf("expected visits:\n%v\ngot:\n%v\n", expect, visits)
   482  		}
   483  	})
   484  	t.Run("BreadthFirst", func(t *testing.T) {
   485  		var visits []vertexAtDepth
   486  		g.walk(breadthFirst|downOrder, true, start, func(v Vertex, d int) error {
   487  			visits = append(visits, vertexAtDepth{v, d})
   488  			return nil
   489  
   490  		})
   491  		expect := []vertexAtDepth{
   492  			{1, 0}, {2, 0}, {3, 1}, {4, 1}, {5, 1}, {6, 2}, {7, 2}, {10, 3}, {8, 3}, {9, 3}, {11, 4},
   493  		}
   494  		if !reflect.DeepEqual(visits, expect) {
   495  			t.Errorf("expected visits:\n%v\ngot:\n%v\n", expect, visits)
   496  		}
   497  	})
   498  	t.Run("ReverseBreadthFirst", func(t *testing.T) {
   499  		var visits []vertexAtDepth
   500  		g.walk(breadthFirst|upOrder, true, reverse, func(v Vertex, d int) error {
   501  			visits = append(visits, vertexAtDepth{v, d})
   502  			return nil
   503  
   504  		})
   505  		expect := []vertexAtDepth{
   506  			{11, 0}, {6, 0}, {10, 1}, {8, 1}, {9, 1}, {3, 1}, {7, 2}, {1, 2}, {4, 3}, {5, 3}, {2, 4},
   507  		}
   508  		if !reflect.DeepEqual(visits, expect) {
   509  			t.Errorf("expected visits:\n%v\ngot:\n%v\n", expect, visits)
   510  		}
   511  	})
   512  
   513  	t.Run("TopologicalOrder", func(t *testing.T) {
   514  		order := g.topoOrder(downOrder)
   515  
   516  		// Validate the order by checking it against the initial graph. We only
   517  		// need to verify that each node has it's direct dependencies
   518  		// satisfied.
   519  		completed := map[Vertex]bool{}
   520  		for _, v := range order {
   521  			deps := g.DownEdges(v)
   522  			for _, dep := range deps {
   523  				if !completed[dep] {
   524  					t.Fatalf("walking node %v, but dependency %v was not yet seen", v, dep)
   525  				}
   526  			}
   527  			completed[v] = true
   528  		}
   529  	})
   530  	t.Run("ReverseTopologicalOrder", func(t *testing.T) {
   531  		order := g.topoOrder(upOrder)
   532  
   533  		// Validate the order by checking it against the initial graph. We only
   534  		// need to verify that each node has it's direct dependencies
   535  		// satisfied.
   536  		completed := map[Vertex]bool{}
   537  		for _, v := range order {
   538  			deps := g.UpEdges(v)
   539  			for _, dep := range deps {
   540  				if !completed[dep] {
   541  					t.Fatalf("walking node %v, but dependency %v was not yet seen", v, dep)
   542  				}
   543  			}
   544  			completed[v] = true
   545  		}
   546  	})
   547  }
   548  
   549  const testGraphTransReductionStr = `
   550  1
   551    2
   552  2
   553    3
   554  3
   555  `
   556  
   557  const testGraphTransReductionMoreStr = `
   558  1
   559    2
   560  2
   561    3
   562  3
   563    4
   564  4
   565  `
   566  
   567  const testGraphTransReductionMultipleRootsStr = `
   568  1
   569    2
   570  2
   571    3
   572  3
   573    4
   574  4
   575  5
   576    6
   577  6
   578    7
   579  7
   580    8
   581  8
   582  `