github.com/cmalfait/terraform@v0.11.12-beta1/dag/walk_test.go (about)

     1  package dag
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  )
    10  
    11  func TestWalker_basic(t *testing.T) {
    12  	var g AcyclicGraph
    13  	g.Add(1)
    14  	g.Add(2)
    15  	g.Connect(BasicEdge(1, 2))
    16  
    17  	// Run it a bunch of times since it is timing dependent
    18  	for i := 0; i < 50; i++ {
    19  		var order []interface{}
    20  		w := &Walker{Callback: walkCbRecord(&order)}
    21  		w.Update(&g)
    22  
    23  		// Wait
    24  		if err := w.Wait(); err != nil {
    25  			t.Fatalf("err: %s", err)
    26  		}
    27  
    28  		// Check
    29  		expected := []interface{}{1, 2}
    30  		if !reflect.DeepEqual(order, expected) {
    31  			t.Fatalf("bad: %#v", order)
    32  		}
    33  	}
    34  }
    35  
    36  func TestWalker_updateNilGraph(t *testing.T) {
    37  	var g AcyclicGraph
    38  	g.Add(1)
    39  	g.Add(2)
    40  	g.Connect(BasicEdge(1, 2))
    41  
    42  	// Run it a bunch of times since it is timing dependent
    43  	for i := 0; i < 50; i++ {
    44  		var order []interface{}
    45  		w := &Walker{Callback: walkCbRecord(&order)}
    46  		w.Update(&g)
    47  		w.Update(nil)
    48  
    49  		// Wait
    50  		if err := w.Wait(); err != nil {
    51  			t.Fatalf("err: %s", err)
    52  		}
    53  	}
    54  }
    55  
    56  func TestWalker_error(t *testing.T) {
    57  	var g AcyclicGraph
    58  	g.Add(1)
    59  	g.Add(2)
    60  	g.Add(3)
    61  	g.Add(4)
    62  	g.Connect(BasicEdge(1, 2))
    63  	g.Connect(BasicEdge(2, 3))
    64  	g.Connect(BasicEdge(3, 4))
    65  
    66  	// Record function
    67  	var order []interface{}
    68  	recordF := walkCbRecord(&order)
    69  
    70  	// Build a callback that delays until we close a channel
    71  	cb := func(v Vertex) error {
    72  		if v == 2 {
    73  			return fmt.Errorf("error!")
    74  		}
    75  
    76  		return recordF(v)
    77  	}
    78  
    79  	w := &Walker{Callback: cb}
    80  	w.Update(&g)
    81  
    82  	// Wait
    83  	if err := w.Wait(); err == nil {
    84  		t.Fatal("expect error")
    85  	}
    86  
    87  	// Check
    88  	expected := []interface{}{1}
    89  	if !reflect.DeepEqual(order, expected) {
    90  		t.Fatalf("bad: %#v", order)
    91  	}
    92  }
    93  
    94  func TestWalker_newVertex(t *testing.T) {
    95  	var g AcyclicGraph
    96  	g.Add(1)
    97  	g.Add(2)
    98  	g.Connect(BasicEdge(1, 2))
    99  
   100  	// Record function
   101  	var order []interface{}
   102  	recordF := walkCbRecord(&order)
   103  	done2 := make(chan int)
   104  
   105  	// Build a callback that notifies us when 2 has been walked
   106  	var w *Walker
   107  	cb := func(v Vertex) error {
   108  		if v == 2 {
   109  			defer close(done2)
   110  		}
   111  		return recordF(v)
   112  	}
   113  
   114  	// Add the initial vertices
   115  	w = &Walker{Callback: cb}
   116  	w.Update(&g)
   117  
   118  	// if 2 has been visited, the walk is complete so far
   119  	<-done2
   120  
   121  	// Update the graph
   122  	g.Add(3)
   123  	w.Update(&g)
   124  
   125  	// Update the graph again but with the same vertex
   126  	g.Add(3)
   127  	w.Update(&g)
   128  
   129  	// Wait
   130  	if err := w.Wait(); err != nil {
   131  		t.Fatalf("err: %s", err)
   132  	}
   133  
   134  	// Check
   135  	expected := []interface{}{1, 2, 3}
   136  	if !reflect.DeepEqual(order, expected) {
   137  		t.Fatalf("bad: %#v", order)
   138  	}
   139  }
   140  
   141  func TestWalker_removeVertex(t *testing.T) {
   142  	var g AcyclicGraph
   143  	g.Add(1)
   144  	g.Add(2)
   145  	g.Connect(BasicEdge(1, 2))
   146  
   147  	// Record function
   148  	var order []interface{}
   149  	recordF := walkCbRecord(&order)
   150  
   151  	var w *Walker
   152  	cb := func(v Vertex) error {
   153  		if v == 1 {
   154  			g.Remove(2)
   155  			w.Update(&g)
   156  		}
   157  
   158  		return recordF(v)
   159  	}
   160  
   161  	// Add the initial vertices
   162  	w = &Walker{Callback: cb}
   163  	w.Update(&g)
   164  
   165  	// Wait
   166  	if err := w.Wait(); err != nil {
   167  		t.Fatalf("err: %s", err)
   168  	}
   169  
   170  	// Check
   171  	expected := []interface{}{1}
   172  	if !reflect.DeepEqual(order, expected) {
   173  		t.Fatalf("bad: %#v", order)
   174  	}
   175  }
   176  
   177  func TestWalker_newEdge(t *testing.T) {
   178  	var g AcyclicGraph
   179  	g.Add(1)
   180  	g.Add(2)
   181  	g.Connect(BasicEdge(1, 2))
   182  
   183  	// Record function
   184  	var order []interface{}
   185  	recordF := walkCbRecord(&order)
   186  
   187  	var w *Walker
   188  	cb := func(v Vertex) error {
   189  		// record where we are first, otherwise the Updated vertex may get
   190  		// walked before the first visit.
   191  		err := recordF(v)
   192  
   193  		if v == 1 {
   194  			g.Add(3)
   195  			g.Connect(BasicEdge(3, 2))
   196  			w.Update(&g)
   197  		}
   198  		return err
   199  	}
   200  
   201  	// Add the initial vertices
   202  	w = &Walker{Callback: cb}
   203  	w.Update(&g)
   204  
   205  	// Wait
   206  	if err := w.Wait(); err != nil {
   207  		t.Fatalf("err: %s", err)
   208  	}
   209  
   210  	// Check
   211  	expected := []interface{}{1, 3, 2}
   212  	if !reflect.DeepEqual(order, expected) {
   213  		t.Fatalf("bad: %#v", order)
   214  	}
   215  }
   216  
   217  func TestWalker_removeEdge(t *testing.T) {
   218  	var g AcyclicGraph
   219  	g.Add(1)
   220  	g.Add(2)
   221  	g.Add(3)
   222  	g.Connect(BasicEdge(1, 2))
   223  	g.Connect(BasicEdge(1, 3))
   224  	g.Connect(BasicEdge(3, 2))
   225  
   226  	// Record function
   227  	var order []interface{}
   228  	recordF := walkCbRecord(&order)
   229  
   230  	// The way this works is that our original graph forces
   231  	// the order of 1 => 3 => 2. During the execution of 1, we
   232  	// remove the edge forcing 3 before 2. Then, during the execution
   233  	// of 3, we wait on a channel that is only closed by 2, implicitly
   234  	// forcing 2 before 3 via the callback (and not the graph). If
   235  	// 2 cannot execute before 3 (edge removal is non-functional), then
   236  	// this test will timeout.
   237  	var w *Walker
   238  	gateCh := make(chan struct{})
   239  	cb := func(v Vertex) error {
   240  		switch v {
   241  		case 1:
   242  			g.RemoveEdge(BasicEdge(3, 2))
   243  			w.Update(&g)
   244  
   245  		case 2:
   246  			// this visit isn't completed until we've recorded it
   247  			// Once the visit is official, we can then close the gate to
   248  			// let 3 continue.
   249  			defer close(gateCh)
   250  
   251  		case 3:
   252  			select {
   253  			case <-gateCh:
   254  			case <-time.After(50 * time.Millisecond):
   255  				return fmt.Errorf("timeout 3 waiting for 2")
   256  			}
   257  		}
   258  
   259  		return recordF(v)
   260  	}
   261  
   262  	// Add the initial vertices
   263  	w = &Walker{Callback: cb}
   264  	w.Update(&g)
   265  
   266  	// Wait
   267  	if err := w.Wait(); err != nil {
   268  		t.Fatalf("err: %s", err)
   269  	}
   270  
   271  	// Check
   272  	expected := []interface{}{1, 2, 3}
   273  	if !reflect.DeepEqual(order, expected) {
   274  		t.Fatalf("bad: %#v", order)
   275  	}
   276  }
   277  
   278  // walkCbRecord is a test helper callback that just records the order called.
   279  func walkCbRecord(order *[]interface{}) WalkFunc {
   280  	var l sync.Mutex
   281  	return func(v Vertex) error {
   282  		l.Lock()
   283  		defer l.Unlock()
   284  		*order = append(*order, v)
   285  		return nil
   286  	}
   287  }