github.com/cmalfait/terraform@v0.11.12-beta1/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 `