github.com/magodo/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 }