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