OLD | NEW |
(Empty) | |
| 1 package utils |
| 2 |
| 3 import ( |
| 4 "bytes" |
| 5 "fmt" |
| 6 "strings" |
| 7 "testing" |
| 8 ) |
| 9 |
| 10 //////////////////////////////////////////////////// |
| 11 // SimpleNode type |
| 12 //////////////////////////////////////////////////// |
| 13 |
| 14 // SimpleNode is a simple implementation of the |Node| interface used |
| 15 // for testing. |
| 16 type SimpleNode struct { |
| 17 // The value of this node |
| 18 value int |
| 19 |
| 20 // Is this node a square node? |
| 21 isSquare bool |
| 22 |
| 23 // The list of out-edges. The elements will be *SimpleNodes. |
| 24 edges []Node |
| 25 |
| 26 // Cache the fact that |SetKnownWellFounded| has been invoked. |
| 27 knownWellFounded bool |
| 28 } |
| 29 |
| 30 func (node *SimpleNode) DebugName() string { |
| 31 return fmt.Sprintf("Node%d", node.value) |
| 32 } |
| 33 |
| 34 func (node *SimpleNode) OutEdges() []Node { |
| 35 return node.edges |
| 36 } |
| 37 |
| 38 func (node *SimpleNode) IsSquare() bool { |
| 39 return node.isSquare |
| 40 } |
| 41 |
| 42 func (node *SimpleNode) KnownWellFounded() bool { |
| 43 return node.knownWellFounded |
| 44 } |
| 45 |
| 46 func (node *SimpleNode) SetKnownWellFounded() { |
| 47 if node.knownWellFounded { |
| 48 panic(fmt.Sprintf("SetKnownWellFounded invoked twice on %s", nod
e.DebugName())) |
| 49 } |
| 50 node.knownWellFounded = true |
| 51 } |
| 52 |
| 53 //Creates a new SimpleNode. |
| 54 func NewNode(value, numEdges int, isSquare bool) *SimpleNode { |
| 55 node := new(SimpleNode) |
| 56 node.value = value |
| 57 node.edges = make([]Node, numEdges) |
| 58 node.isSquare = isSquare |
| 59 return node |
| 60 } |
| 61 |
| 62 //////////////////////////////////////////////////// |
| 63 // Test Utilities |
| 64 //////////////////////////////////////////////////// |
| 65 |
| 66 // Builds a test graph containing only circle nodes. |
| 67 // The connection matrix should have the following form: |
| 68 // connectionMatrix[i][j] = k indicates that there is an edge in the graph |
| 69 // from Node i to Node K. k must be less than len(connectionMatrix). |
| 70 func buildCircleTestGraph(connectionMatrix [][]int) (nodes []*SimpleNode) { |
| 71 return buildTestGraph(connectionMatrix, nil) |
| 72 } |
| 73 |
| 74 // Builds a test graph. The connection matrix should have the following form: |
| 75 // connectionMatrix[i][j] = k indicates that there is an edge in the graph |
| 76 // from Node i to Node K. k must be less than len(connectionMatrix). |
| 77 // If |squares| is not nil then it must contain the list of indices of the nodes
that are squares. |
| 78 func buildTestGraph(connectionMatrix [][]int, squares []int) (nodes []*SimpleNod
e) { |
| 79 squareMap := make(map[int]bool) |
| 80 if squares != nil { |
| 81 for _, n := range squares { |
| 82 squareMap[n] = true |
| 83 } |
| 84 } |
| 85 nodes = make([]*SimpleNode, len(connectionMatrix)) |
| 86 for index, connectionList := range connectionMatrix { |
| 87 _, isSquare := squareMap[index] |
| 88 nodes[index] = NewNode(index, len(connectionList), isSquare) |
| 89 } |
| 90 for index, connectionList := range connectionMatrix { |
| 91 for i, target := range connectionList { |
| 92 nodes[index].edges[i] = nodes[target] |
| 93 } |
| 94 } |
| 95 return |
| 96 } |
| 97 |
| 98 // expectedDebugData is used to store an expectation for the value of the |debug
Data| populated |
| 99 // by the ethod |checkWellFounded|. This data captures the result of phase 1 of
the algorithm. |
| 100 type expectedDebugData struct { |
| 101 initialPendingSet []string |
| 102 initialFoundationSet []string |
| 103 } |
| 104 |
| 105 func compareDebugData(expected *expectedDebugData, actual *debugData) error { |
| 106 if expected == nil { |
| 107 panic("expected cannot be nil") |
| 108 return nil |
| 109 } |
| 110 if err := compareNodeSlice(expected.initialPendingSet, actual.initialPen
dingSet, "initialPendingSet"); err != nil { |
| 111 return err |
| 112 } |
| 113 return compareNodeSlice(expected.initialFoundationSet, actual.initialFou
ndationSet, "initialFoundationSet") |
| 114 } |
| 115 |
| 116 func compareNodeSlice(expected []string, actual []Node, name string) error { |
| 117 expectedMap := make(map[string]bool) |
| 118 actualMap := make(map[string]bool) |
| 119 for _, n := range expected { |
| 120 expectedMap[n] = true |
| 121 } |
| 122 for _, n := range actual { |
| 123 actualMap[n.DebugName()] = true |
| 124 } |
| 125 for _, n := range expected { |
| 126 if _, ok := actualMap[n]; !ok { |
| 127 return fmt.Errorf("%s: missing %s, actual: %s", name, n,
NodeSliceToString(actual)) |
| 128 } |
| 129 } |
| 130 for _, n := range actual { |
| 131 if _, ok := expectedMap[n.DebugName()]; !ok { |
| 132 return fmt.Errorf("%s: unexpected: %s, expected: %v, act
ual: %s", name, n.DebugName(), expected, NodeSliceToString(actual)) |
| 133 } |
| 134 } |
| 135 return nil |
| 136 } |
| 137 |
| 138 func NodeSliceToString(nodeSlice []Node) string { |
| 139 var buffer bytes.Buffer |
| 140 fmt.Fprintf(&buffer, "[") |
| 141 first := true |
| 142 for _, n := range nodeSlice { |
| 143 if !first { |
| 144 fmt.Fprintf(&buffer, ", ") |
| 145 } |
| 146 fmt.Fprintf(&buffer, "%s", n.DebugName()) |
| 147 first = false |
| 148 } |
| 149 fmt.Fprintln(&buffer, "]") |
| 150 return buffer.String() |
| 151 } |
| 152 |
| 153 //////////////////////////////////////////////////// |
| 154 // Tests |
| 155 //////////////////////////////////////////////////// |
| 156 |
| 157 type WellFoundedGraphTestCase struct { |
| 158 // The connection matrix describing the digraph for this case. |
| 159 connectionMatrix [][]int |
| 160 |
| 161 // If this is non-nil it should be the list of indices of the square nod
es. |
| 162 squares []int |
| 163 |
| 164 // The expected result when searching from Node0 |
| 165 expectedFirstA, expectedLastA, expectedPathA string |
| 166 |
| 167 // The expected result when searching from Node1 |
| 168 expectedFirstB, expectedLastB, expectedPathB string |
| 169 |
| 170 expectedDebugDataA, expectedDebugDataB expectedDebugData |
| 171 } |
| 172 |
| 173 // TestWellFoundedIsWellFoundedCirclesOnly tests the function |checkWellFounded| |
| 174 // on graphs that are well-founded and that contain only circles. For |
| 175 // graphs that contain only circles being well-founded is equivalent to |
| 176 // being acyclic. |
| 177 func TestWellFoundedIsWellFoundedCirclesOnly(t *testing.T) { |
| 178 cases := []WellFoundedGraphTestCase{ |
| 179 |
| 180 // Case: Single node, no self-loop |
| 181 { |
| 182 connectionMatrix: [][]int{ |
| 183 {}, |
| 184 }, |
| 185 }, |
| 186 |
| 187 // Case: Linear chain of length 4 |
| 188 { |
| 189 connectionMatrix: [][]int{ |
| 190 {1}, // 0 -> 1 |
| 191 {2}, // 1 -> 2 |
| 192 {3}, // 2 -> 3 |
| 193 {}, // 3 -> || |
| 194 }, |
| 195 }, |
| 196 |
| 197 // Case: Diamond |
| 198 // |
| 199 // 0 |
| 200 // 1 2 |
| 201 // 3 |
| 202 { |
| 203 connectionMatrix: [][]int{ |
| 204 {1, 2}, // 0 |
| 205 {3}, // 1 |
| 206 {3}, // 2 |
| 207 {}, // 3 |
| 208 }, |
| 209 }, |
| 210 |
| 211 // Case: Complex, double diamond with a tail |
| 212 // |
| 213 // 0 |
| 214 // 1 2 |
| 215 // 6 3 4 |
| 216 // 5 |
| 217 { |
| 218 connectionMatrix: [][]int{ |
| 219 {1, 2}, // 0 |
| 220 {3, 6}, // 1 |
| 221 {3, 4}, // 2 |
| 222 {5}, // 3 |
| 223 {5}, // 4 |
| 224 {}, // 5 |
| 225 {}, // 6 |
| 226 }, |
| 227 }, |
| 228 |
| 229 // Case: 0 and 1 are independent roots |
| 230 // |
| 231 // 0 1 |
| 232 // 7 2 | |
| 233 // 6 3 4 |
| 234 // 5 |
| 235 { |
| 236 connectionMatrix: [][]int{ |
| 237 {7, 2}, // 0 |
| 238 {4}, // 1 |
| 239 {3, 4}, // 2 |
| 240 {5}, // 3 |
| 241 {5}, // 4 |
| 242 {}, // 5 |
| 243 {}, // 6 |
| 244 {3, 6}, // 7 |
| 245 }, |
| 246 }, |
| 247 } |
| 248 |
| 249 for i, c := range cases { |
| 250 graph := buildCircleTestGraph(c.connectionMatrix) |
| 251 dbgData := debugData{} |
| 252 cycleDescription := checkWellFounded(graph[0], &dbgData) |
| 253 if cycleDescription != nil { |
| 254 t.Fatalf("Case %dA: Detected a cycle: %s", i, cycleDescr
iption) |
| 255 } |
| 256 if err := compareDebugData(&c.expectedDebugDataA, &dbgData); err
!= nil { |
| 257 t.Fatalf("Case %dA: %s", i, err.Error()) |
| 258 } |
| 259 if len(graph) > 1 { |
| 260 dbgData := debugData{} |
| 261 cycleDescription = checkWellFounded(graph[1], &dbgData) |
| 262 if cycleDescription != nil { |
| 263 t.Fatalf("Case %dB: Detected a cycle: %s", i, cy
cleDescription) |
| 264 } |
| 265 if err := compareDebugData(&c.expectedDebugDataB, &dbgDa
ta); err != nil { |
| 266 t.Fatalf("Case %dB: %s", i, err.Error()) |
| 267 } |
| 268 } |
| 269 } |
| 270 } |
| 271 |
| 272 // TestWellFoundedNotWellFoundedCirclesOnly tests the function |checkWellFounded
| |
| 273 // on graphs that are not well-founded and that contain only circles. For |
| 274 // graphs that contain only circles being well-founded is equivalent to |
| 275 // being acyclic. |
| 276 func TestWellFoundedNotWellFoundedCirclesOnly(t *testing.T) { |
| 277 |
| 278 cases := []WellFoundedGraphTestCase{ |
| 279 |
| 280 // Case: Single node with self-loop |
| 281 { |
| 282 connectionMatrix: [][]int{ |
| 283 {0}, |
| 284 }, |
| 285 |
| 286 expectedFirstA: "Node0", |
| 287 expectedLastA: "Node0", |
| 288 expectedPathA: "Node0", |
| 289 expectedDebugDataA: expectedDebugData{ |
| 290 initialPendingSet: []string{"Node0"}, |
| 291 }, |
| 292 }, |
| 293 |
| 294 // Case: Loop of length 4 |
| 295 { |
| 296 connectionMatrix: [][]int{ |
| 297 {1}, // 0 -> 1 |
| 298 {2}, // 1 -> 2 |
| 299 {3}, // 2 -> 3 |
| 300 {0}, // 3 -> 0 |
| 301 }, |
| 302 |
| 303 expectedFirstA: "Node0", |
| 304 expectedLastA: "Node3", |
| 305 expectedPathA: "Node0, Node1, Node2, Node3", |
| 306 expectedDebugDataA: expectedDebugData{ |
| 307 initialPendingSet: []string{"Node0", "Node1", "N
ode2", "Node3"}, |
| 308 }, |
| 309 |
| 310 expectedFirstB: "Node1", |
| 311 expectedLastB: "Node0", |
| 312 expectedPathB: "Node1, Node2, Node3, Node0", |
| 313 expectedDebugDataB: expectedDebugData{ |
| 314 initialPendingSet: []string{"Node0", "Node1", "N
ode2", "Node3"}, |
| 315 }, |
| 316 }, |
| 317 |
| 318 // Case: Diamond with a loop-back from bottom to top |
| 319 // |
| 320 // 0 <-- |
| 321 // 1 2 | |
| 322 // 3 ---| |
| 323 { |
| 324 connectionMatrix: [][]int{ |
| 325 {1, 2}, // 0 |
| 326 {3}, // 1 |
| 327 {3}, // 2 |
| 328 {0}, // 3 |
| 329 }, |
| 330 expectedFirstA: "Node0", |
| 331 expectedLastA: "Node3", |
| 332 expectedPathA: "Node0, Node1, Node3", |
| 333 expectedDebugDataA: expectedDebugData{ |
| 334 initialPendingSet: []string{"Node0", "Node1", "N
ode2", "Node3"}, |
| 335 }, |
| 336 |
| 337 expectedFirstB: "Node1", |
| 338 expectedLastB: "Node0", |
| 339 expectedPathB: "Node1, Node3, Node0", |
| 340 expectedDebugDataB: expectedDebugData{ |
| 341 initialPendingSet: []string{"Node0", "Node1", "N
ode2", "Node3"}, |
| 342 }, |
| 343 }, |
| 344 |
| 345 // Case: Complex, double diamond with a tail and a bridge and |
| 346 // a loop back to the top |
| 347 // |
| 348 // 0 |
| 349 // 1 2 <---- |
| 350 // 6 3 4 | |
| 351 // 5 ------| |
| 352 { |
| 353 connectionMatrix: [][]int{ |
| 354 {1, 2}, // 0 |
| 355 {3, 6}, // 1 |
| 356 {3, 4}, // 2 |
| 357 {5}, // 3 |
| 358 {5}, // 4 |
| 359 {2}, // 5 |
| 360 {}, // 6 |
| 361 }, |
| 362 expectedFirstA: "Node3", |
| 363 expectedLastA: "Node2", |
| 364 expectedPathA: "Node3, Node5, Node2", |
| 365 expectedDebugDataA: expectedDebugData{ |
| 366 initialPendingSet: []string{"Node0", "Node1", "N
ode2", "Node3", "Node4", "Node5"}, |
| 367 }, |
| 368 |
| 369 expectedFirstB: "Node3", |
| 370 expectedLastB: "Node2", |
| 371 expectedPathB: "Node3, Node5, Node2", |
| 372 expectedDebugDataB: expectedDebugData{ |
| 373 initialPendingSet: []string{"Node1", "Node2", "N
ode3", "Node4", "Node5"}, |
| 374 }, |
| 375 }, |
| 376 |
| 377 // Case: More complex |
| 378 // |
| 379 // |
| 380 // 0 |
| 381 // 1 2 3 <------ |
| 382 // 4 5 | |
| 383 // 6 8 7 | |
| 384 // 11 9 -------| |
| 385 // 12 10 |
| 386 { |
| 387 connectionMatrix: [][]int{ |
| 388 {1, 2, 3}, // 0 |
| 389 {4}, // 1 |
| 390 {4, 5}, // 2 |
| 391 {5}, // 3 |
| 392 {6}, // 4 |
| 393 {6, 7, 8}, // 5 |
| 394 {11}, // 6 |
| 395 {}, // 7 |
| 396 {9}, // 8 |
| 397 {10, 3}, // 9 |
| 398 {}, // 10 |
| 399 {12}, // 11 |
| 400 {}, // 12 |
| 401 }, |
| 402 expectedFirstA: "Node5", |
| 403 expectedLastA: "Node3", |
| 404 expectedPathA: "Node5, Node8, Node9, Node3", |
| 405 expectedDebugDataA: expectedDebugData{ |
| 406 initialPendingSet: []string{"Node0", "Node2", "N
ode3", "Node5", "Node8", "Node9"}, |
| 407 }, |
| 408 }, |
| 409 |
| 410 // Case: No loop from 0 but loop from 1 |
| 411 // |
| 412 // |
| 413 // 1 |
| 414 // 0 2 3 <---- |
| 415 // 4 5 | |
| 416 // 6 8 7 | |
| 417 // 11 9 ------| |
| 418 // 12 10 |
| 419 { |
| 420 connectionMatrix: [][]int{ |
| 421 {4}, // 0 |
| 422 {0, 2, 3}, // 1 |
| 423 {4, 5}, // 2 |
| 424 {5}, // 3 |
| 425 {6}, // 4 |
| 426 {6, 7, 8}, // 5 |
| 427 {11}, // 6 |
| 428 {}, // 7 |
| 429 {9}, // 8 |
| 430 {10, 3}, // 9 |
| 431 {}, // 10 |
| 432 {12}, // 11 |
| 433 {}, // 12 |
| 434 }, |
| 435 expectedFirstB: "Node5", |
| 436 expectedLastB: "Node3", |
| 437 expectedPathB: "Node5, Node8, Node9, Node3", |
| 438 expectedDebugDataB: expectedDebugData{ |
| 439 initialPendingSet: []string{"Node1", "Node2", "N
ode3", "Node5", "Node8", "Node9"}, |
| 440 }, |
| 441 }, |
| 442 } |
| 443 |
| 444 for i, c := range cases { |
| 445 graph := buildCircleTestGraph(c.connectionMatrix) |
| 446 // Test from root=Node0 |
| 447 dbgData := debugData{} |
| 448 cycleDescription := checkWellFounded(graph[0], &dbgData) |
| 449 if err := compareDebugData(&c.expectedDebugDataA, &dbgData); err
!= nil { |
| 450 t.Fatalf("Case %dA: %s", i, err.Error()) |
| 451 } |
| 452 if len(c.expectedPathA) == 0 { |
| 453 if cycleDescription != nil { |
| 454 t.Fatalf("Case %dA: Detected a cycle: %s", i, cy
cleDescription) |
| 455 } |
| 456 } else { |
| 457 if cycleDescription == nil { |
| 458 t.Fatalf("Case %d: Expected a cycle.", i) |
| 459 } |
| 460 if !strings.Contains(cycleDescription.String(), fmt.Spri
ntf("first:%s", c.expectedFirstA)) { |
| 461 t.Fatalf("Case %d: got=%s expectedFirst=%q", i,
cycleDescription.String(), c.expectedFirstA) |
| 462 } |
| 463 if !strings.Contains(cycleDescription.String(), fmt.Spri
ntf("last:%s", c.expectedLastA)) { |
| 464 t.Fatalf("Case %d: got=%s expectedLast=%q", i, c
ycleDescription.String(), c.expectedLastA) |
| 465 } |
| 466 if !strings.Contains(cycleDescription.String(), fmt.Spri
ntf("{%s}", c.expectedPathA)) { |
| 467 t.Fatalf("Case %d: got=%s expectedPath=%q", i, c
ycleDescription.String(), c.expectedPathA) |
| 468 } |
| 469 } |
| 470 |
| 471 // Test from root=Node1 |
| 472 if len(graph) > 1 { |
| 473 dbgData := debugData{} |
| 474 cycleDescription := checkWellFounded(graph[1], &dbgData) |
| 475 if err := compareDebugData(&c.expectedDebugDataB, &dbgDa
ta); err != nil { |
| 476 t.Fatalf("Case %dB: %s", i, err.Error()) |
| 477 } |
| 478 if len(c.expectedPathB) == 0 { |
| 479 if cycleDescription != nil { |
| 480 t.Fatalf("Case %dB: Detected a cycle: %s
", i, cycleDescription) |
| 481 } |
| 482 } else { |
| 483 if cycleDescription == nil { |
| 484 t.Fatalf("Case %dB: Expected a cycle.",
i) |
| 485 } |
| 486 if !strings.Contains(cycleDescription.String(),
fmt.Sprintf("first:%s", c.expectedFirstB)) { |
| 487 t.Fatalf("Case %dB: got=%s expectedFirst
=%q", i, cycleDescription.String(), c.expectedFirstB) |
| 488 } |
| 489 if !strings.Contains(cycleDescription.String(),
fmt.Sprintf("last:%s", c.expectedLastB)) { |
| 490 t.Fatalf("Case %dB: got=%s expectedLast=
%q", i, cycleDescription.String(), c.expectedLastB) |
| 491 } |
| 492 if !strings.Contains(cycleDescription.String(),
fmt.Sprintf("{%s}", c.expectedPathB)) { |
| 493 t.Fatalf("Case %dB: got=%s expectedPath=
%q", i, cycleDescription.String(), c.expectedPathB) |
| 494 } |
| 495 } |
| 496 } |
| 497 } |
| 498 } |
| 499 |
| 500 // TestWellFoundedIsWellFounded tests the function |checkWellFounded| on graphs |
| 501 // that contain both circles and squares and are well-founded. |
| 502 func TestWellFoundedIsWellFounded(t *testing.T) { |
| 503 cases := []WellFoundedGraphTestCase{ |
| 504 |
| 505 // Case: Single square node, no self-loop |
| 506 { |
| 507 connectionMatrix: [][]int{ |
| 508 {}, |
| 509 }, |
| 510 squares: []int{0}, |
| 511 }, |
| 512 |
| 513 // Case: Loop through right branch but not through left branch. |
| 514 // |
| 515 // [0] <--| |
| 516 // 1 2--| |
| 517 { |
| 518 connectionMatrix: [][]int{ |
| 519 {1, 2}, // 0 |
| 520 {}, // 1 |
| 521 {0}, // 2 |
| 522 }, |
| 523 squares: []int{0}, |
| 524 }, |
| 525 |
| 526 // Case: Loop through left branch but not through rigt branch. |
| 527 // |
| 528 // [0] <--| |
| 529 // 2 1--| |
| 530 { |
| 531 connectionMatrix: [][]int{ |
| 532 {1, 2}, // 0 |
| 533 {0}, // 1 |
| 534 {}, // 2 |
| 535 }, |
| 536 squares: []int{0}, |
| 537 expectedDebugDataA: expectedDebugData{ |
| 538 initialPendingSet: []string{"Node1"}, |
| 539 initialFoundationSet: []string{"Node0"}, |
| 540 }, |
| 541 }, |
| 542 |
| 543 // Case: Loop through right branch below the root |
| 544 // |
| 545 // 0 <---| |
| 546 // [1] | |
| 547 // 2 3--| |
| 548 { |
| 549 connectionMatrix: [][]int{ |
| 550 {1}, // 0 |
| 551 {2, 3}, // 1 |
| 552 {}, // 2 |
| 553 {0}, // 3 |
| 554 }, |
| 555 squares: []int{1}, |
| 556 expectedDebugDataA: expectedDebugData{ |
| 557 initialPendingSet: []string{"Node3"}, |
| 558 initialFoundationSet: []string{"Node0"}, |
| 559 }, |
| 560 }, |
| 561 |
| 562 // Case: Loop through left branch below the root |
| 563 // |
| 564 // 0 <---| |
| 565 // [1] | |
| 566 // 3 2--| |
| 567 { |
| 568 connectionMatrix: [][]int{ |
| 569 {1}, // 0 |
| 570 {2, 3}, // 1 |
| 571 {0}, // 2 |
| 572 {}, // 3 |
| 573 }, |
| 574 squares: []int{1}, |
| 575 expectedDebugDataA: expectedDebugData{ |
| 576 initialPendingSet: []string{"Node2"}, |
| 577 initialFoundationSet: []string{"Node0"}, |
| 578 }, |
| 579 }, |
| 580 |
| 581 // Case: We know 0 is well-founded before we see the loop from 4
to 0 |
| 582 // |
| 583 // |
| 584 // 1 <-- [0] <--| |
| 585 // [2] | |
| 586 // 3 4--| |
| 587 { |
| 588 connectionMatrix: [][]int{ |
| 589 {1, 2}, // 0 |
| 590 {}, // 1 |
| 591 {3, 4}, // 2 |
| 592 {}, // 3 |
| 593 {0}, // 4 |
| 594 }, |
| 595 squares: []int{0, 2}, |
| 596 }, |
| 597 |
| 598 // Case: Well-founded becuase of 8 |
| 599 // 0 |
| 600 // 1 |
| 601 // 2 <-- |
| 602 // 3 | |
| 603 // 8 <-- [4] | |
| 604 // 5 | |
| 605 // 6---| |
| 606 // 7 |
| 607 { |
| 608 connectionMatrix: [][]int{ |
| 609 {1}, // 0 |
| 610 {2}, // 1 |
| 611 {3}, // 2 |
| 612 {4}, // 3 |
| 613 {5, 8}, // 4 |
| 614 {6}, // 5 |
| 615 {2, 7}, // 6 |
| 616 {}, // 7 |
| 617 {}, // 8 |
| 618 }, |
| 619 squares: []int{4}, |
| 620 expectedDebugDataA: expectedDebugData{ |
| 621 initialPendingSet: []string{"Node5", "Node6"}
, |
| 622 initialFoundationSet: []string{"Node2"}, |
| 623 }, |
| 624 }, |
| 625 |
| 626 // Case: Double loops through right branches |
| 627 // |
| 628 // 0 <---| |
| 629 // [1]<---|----- |
| 630 // 2 3--| | |
| 631 // [4] | |
| 632 // 5 6 7 --| |
| 633 // 8 9 10 |
| 634 { |
| 635 connectionMatrix: [][]int{ |
| 636 {1}, // 0 |
| 637 {2, 3}, // 1 |
| 638 {}, // 2 |
| 639 {0, 4}, // 3 |
| 640 {5, 6, 7}, // 4 |
| 641 {8}, // 5 |
| 642 {9}, // 6 |
| 643 {1, 10}, // 7 |
| 644 {}, // 8 |
| 645 {}, // 9 |
| 646 {}, // 10 |
| 647 }, |
| 648 squares: []int{1, 4}, |
| 649 expectedDebugDataA: expectedDebugData{ |
| 650 initialPendingSet: []string{"Node3"}, |
| 651 initialFoundationSet: []string{"Node0"}, |
| 652 }, |
| 653 }, |
| 654 |
| 655 // Case: Double loops through left branches |
| 656 // |
| 657 // 0 <---| |
| 658 // [1]<---|----- |
| 659 // 3 2--| | |
| 660 // [4] | |
| 661 // 7 6 5 --| |
| 662 // 8 9 10 |
| 663 { |
| 664 connectionMatrix: [][]int{ |
| 665 {1}, // 0 |
| 666 {2, 3}, // 1 |
| 667 {0, 4}, // 2 |
| 668 {}, // 3 |
| 669 {5, 6, 7}, // 4 |
| 670 {1, 10}, // 5 |
| 671 {9}, // 6 |
| 672 {8}, // 7 |
| 673 {}, // 8 |
| 674 {}, // 9 |
| 675 {}, // 10 |
| 676 }, |
| 677 squares: []int{1, 4}, |
| 678 expectedDebugDataA: expectedDebugData{ |
| 679 initialPendingSet: []string{"Node2", "Node5"}
, |
| 680 initialFoundationSet: []string{"Node0", "Node1"}
, |
| 681 }, |
| 682 }, |
| 683 |
| 684 // Case: Crossing up and down on right |
| 685 // |
| 686 // 0 -----| |
| 687 // 1 | |
| 688 // [2] <---|----- |
| 689 // 3 4 <-| | |
| 690 // 5 | |
| 691 // 6 | |
| 692 // 7 -------| |
| 693 // |
| 694 { |
| 695 connectionMatrix: [][]int{ |
| 696 {1, 4}, // 0 |
| 697 {2}, // 1 |
| 698 {3, 4}, // 2 |
| 699 {}, // 3 |
| 700 {5}, // 4 |
| 701 {6}, // 5 |
| 702 {7}, // 6 |
| 703 {2}, // 7 |
| 704 }, |
| 705 squares: []int{2}, |
| 706 }, |
| 707 |
| 708 // Case: Crossing up and down on left |
| 709 // |
| 710 // 0 -----| |
| 711 // 1 | |
| 712 // [2] <---|----- |
| 713 // 4 3 <-| | |
| 714 // 5 | |
| 715 // 6 | |
| 716 // 7 -------| |
| 717 // |
| 718 { |
| 719 connectionMatrix: [][]int{ |
| 720 {1, 3}, // 0 |
| 721 {2}, // 1 |
| 722 {3, 4}, // 2 |
| 723 {5}, // 3 |
| 724 {}, // 4 |
| 725 {6}, // 5 |
| 726 {7}, // 6 |
| 727 {2}, // 7 |
| 728 }, |
| 729 squares: []int{2}, |
| 730 expectedDebugDataA: expectedDebugData{ |
| 731 initialPendingSet: []string{"Node0", "Node3",
"Node5", "Node6", "Node7"}, |
| 732 initialFoundationSet: []string{"Node2"}, |
| 733 }, |
| 734 }, |
| 735 |
| 736 // Case: |
| 737 // |
| 738 // [0] <------| |
| 739 // |---->1 2 | |
| 740 // |<---[3]-------------| |
| 741 // |
| 742 // |
| 743 // |
| 744 { |
| 745 connectionMatrix: [][]int{ |
| 746 {1, 2}, // 0 |
| 747 {3}, // 1 |
| 748 {}, // 2 |
| 749 {0, 1}, // 3 |
| 750 }, |
| 751 squares: []int{0, 3}, |
| 752 expectedDebugDataA: expectedDebugData{ |
| 753 initialPendingSet: []string{"Node1", "Node3"}
, |
| 754 initialFoundationSet: []string{"Node0"}, |
| 755 }, |
| 756 }, |
| 757 |
| 758 // Case: |
| 759 // |
| 760 // |-----> 0 <------\ |
| 761 // | [1] --> [2]---|--> 4 |
| 762 // | ^ \ |
| 763 // |----|-------3 |
| 764 // |
| 765 // |
| 766 { |
| 767 connectionMatrix: [][]int{ |
| 768 {1, 2}, // 0 |
| 769 {2}, // 1 |
| 770 {0, 3, 4}, // 2 |
| 771 {0, 1}, // 3 |
| 772 {}, // 4 |
| 773 }, |
| 774 squares: []int{1, 2}, |
| 775 expectedDebugDataA: expectedDebugData{ |
| 776 initialPendingSet: []string{"Node3"}, |
| 777 initialFoundationSet: []string{"Node0", "Node1"}
, |
| 778 }, |
| 779 }, |
| 780 |
| 781 // Case: |
| 782 // |
| 783 // |-->[1] |
| 784 // [2]< -| 0 |
| 785 // ^ | |
| 786 // |----| |
| 787 // |
| 788 // |
| 789 { |
| 790 connectionMatrix: [][]int{ |
| 791 {}, // 0 |
| 792 {0, 2}, // 1 |
| 793 {1, 2}, // 2 |
| 794 }, |
| 795 squares: []int{1, 2}, |
| 796 }, |
| 797 |
| 798 // Case: Double loops through left branches |
| 799 // |
| 800 // 0 <---| |
| 801 // [1]<---|----- |
| 802 // 3 2--| | |
| 803 // [4] | |
| 804 // 7 6 5 --| |
| 805 // 8 9 10 |
| 806 { |
| 807 connectionMatrix: [][]int{ |
| 808 {1}, // 0 |
| 809 {2, 3}, // 1 |
| 810 {0, 4}, // 2 |
| 811 {}, // 3 |
| 812 {5, 6, 7}, // 4 |
| 813 {1, 10}, // 5 |
| 814 {9}, // 6 |
| 815 {8}, // 7 |
| 816 {}, // 8 |
| 817 {}, // 9 |
| 818 {}, // 10 |
| 819 }, |
| 820 squares: []int{1, 4}, |
| 821 expectedDebugDataA: expectedDebugData{ |
| 822 initialPendingSet: []string{"Node2", "Node5"}
, |
| 823 initialFoundationSet: []string{"Node0", "Node1"}
, |
| 824 }, |
| 825 }, |
| 826 } |
| 827 |
| 828 for i, c := range cases { |
| 829 graph := buildTestGraph(c.connectionMatrix, c.squares) |
| 830 dbgData := debugData{} |
| 831 cycleDescription := checkWellFounded(graph[0], &dbgData) |
| 832 if err := compareDebugData(&c.expectedDebugDataA, &dbgData); err
!= nil { |
| 833 t.Fatalf("Case %dA: %s", i, err.Error()) |
| 834 } |
| 835 if cycleDescription != nil { |
| 836 t.Fatalf("Case %dA: Detected a cycle: %s", i, cycleDescr
iption) |
| 837 } |
| 838 if len(graph) > 1 { |
| 839 dbgData := debugData{} |
| 840 cycleDescription := checkWellFounded(graph[1], &dbgData) |
| 841 if err := compareDebugData(&c.expectedDebugDataB, &dbgDa
ta); err != nil { |
| 842 t.Fatalf("Case %dB: %s", i, err.Error()) |
| 843 } |
| 844 if cycleDescription != nil { |
| 845 t.Fatalf("Case %dB: Detected a cycle: %s", i, cy
cleDescription) |
| 846 } |
| 847 } |
| 848 } |
| 849 } |
| 850 |
| 851 // TestWellFoundedNotWellFounded tests the function |checkWellFounded| on graphs |
| 852 // that contain both circles and squares and are not well-founded. |
| 853 func TestWellFoundedNotWellFounded(t *testing.T) { |
| 854 |
| 855 cases := []WellFoundedGraphTestCase{ |
| 856 |
| 857 // Case: Single square node with self-loop |
| 858 { |
| 859 connectionMatrix: [][]int{ |
| 860 {0}, |
| 861 }, |
| 862 |
| 863 squares: []int{0}, |
| 864 |
| 865 expectedFirstA: "Node0", |
| 866 expectedLastA: "Node0", |
| 867 expectedPathA: "Node0", |
| 868 expectedDebugDataA: expectedDebugData{ |
| 869 initialPendingSet: []string{"Node0"}, |
| 870 initialFoundationSet: []string{}, |
| 871 }, |
| 872 }, |
| 873 |
| 874 // Case: Loop of length 4 with 1 square at the root |
| 875 { |
| 876 connectionMatrix: [][]int{ |
| 877 {1}, // 0 -> 1 |
| 878 {2}, // 1 -> 2 |
| 879 {3}, // 2 -> 3 |
| 880 {0}, // 3 -> 0 |
| 881 }, |
| 882 |
| 883 squares: []int{0}, |
| 884 |
| 885 expectedFirstA: "Node0", |
| 886 expectedLastA: "Node3", |
| 887 expectedPathA: "Node0, Node1, Node2, Node3", |
| 888 expectedDebugDataA: expectedDebugData{ |
| 889 initialPendingSet: []string{"Node0", "Node1",
"Node2", "Node3"}, |
| 890 initialFoundationSet: []string{}, |
| 891 }, |
| 892 |
| 893 expectedFirstB: "Node1", |
| 894 expectedLastB: "Node0", |
| 895 expectedPathB: "Node1, Node2, Node3, Node0", |
| 896 expectedDebugDataB: expectedDebugData{ |
| 897 initialPendingSet: []string{"Node0", "Node1",
"Node2", "Node3"}, |
| 898 initialFoundationSet: []string{}, |
| 899 }, |
| 900 }, |
| 901 |
| 902 // Case: Loop of length 4 with 2 squares |
| 903 { |
| 904 connectionMatrix: [][]int{ |
| 905 {1}, // 0 -> 1 |
| 906 {2}, // 1 -> 2 |
| 907 {3}, // 2 -> 3 |
| 908 {0}, // 3 -> 0 |
| 909 }, |
| 910 |
| 911 squares: []int{1, 2}, |
| 912 |
| 913 expectedFirstA: "Node0", |
| 914 expectedLastA: "Node3", |
| 915 expectedPathA: "Node0, Node1, Node2, Node3", |
| 916 expectedDebugDataA: expectedDebugData{ |
| 917 initialPendingSet: []string{"Node0", "Node1",
"Node2", "Node3"}, |
| 918 initialFoundationSet: []string{}, |
| 919 }, |
| 920 |
| 921 expectedFirstB: "Node1", |
| 922 expectedLastB: "Node0", |
| 923 expectedPathB: "Node1, Node2, Node3, Node0", |
| 924 expectedDebugDataB: expectedDebugData{ |
| 925 initialPendingSet: []string{"Node0", "Node1",
"Node2", "Node3"}, |
| 926 initialFoundationSet: []string{}, |
| 927 }, |
| 928 }, |
| 929 |
| 930 // Case: Loop through left and right branches |
| 931 // |
| 932 // |--> [0] <--| |
| 933 // |--1 2--| |
| 934 { |
| 935 connectionMatrix: [][]int{ |
| 936 {1, 2}, // 0 |
| 937 {0}, // 1 |
| 938 {0}, // 2 |
| 939 }, |
| 940 squares: []int{0}, |
| 941 |
| 942 expectedFirstA: "Node0", |
| 943 expectedLastA: "Node1", |
| 944 expectedPathA: "Node0, Node1", |
| 945 expectedDebugDataA: expectedDebugData{ |
| 946 initialPendingSet: []string{"Node0", "Node1",
"Node2"}, |
| 947 initialFoundationSet: []string{}, |
| 948 }, |
| 949 |
| 950 expectedFirstB: "Node1", |
| 951 expectedLastB: "Node0", |
| 952 expectedPathB: "Node1, Node0", |
| 953 expectedDebugDataB: expectedDebugData{ |
| 954 initialPendingSet: []string{"Node0", "Node1",
"Node2"}, |
| 955 initialFoundationSet: []string{}, |
| 956 }, |
| 957 }, |
| 958 |
| 959 // Case: Loop through left and right branches below the root |
| 960 // |
| 961 // |----> 0 <---| |
| 962 // | [1] | |
| 963 // |-- 2 3--| |
| 964 { |
| 965 connectionMatrix: [][]int{ |
| 966 {1}, // 0 |
| 967 {2, 3}, // 1 |
| 968 {0}, // 2 |
| 969 {0}, // 3 |
| 970 }, |
| 971 squares: []int{1}, |
| 972 |
| 973 expectedFirstA: "Node0", |
| 974 expectedLastA: "Node2", |
| 975 expectedPathA: "Node0, Node1, Node2", |
| 976 expectedDebugDataA: expectedDebugData{ |
| 977 initialPendingSet: []string{"Node0", "Node1",
"Node2", "Node3"}, |
| 978 initialFoundationSet: []string{}, |
| 979 }, |
| 980 |
| 981 expectedFirstB: "Node1", |
| 982 expectedLastB: "Node0", |
| 983 expectedPathB: "Node1, Node2, Node0", |
| 984 expectedDebugDataB: expectedDebugData{ |
| 985 initialPendingSet: []string{"Node0", "Node1",
"Node2", "Node3"}, |
| 986 initialFoundationSet: []string{}, |
| 987 }, |
| 988 }, |
| 989 |
| 990 // Case: Self loop at one node below a square |
| 991 // |
| 992 // [0] |
| 993 // 1 2--| |
| 994 // ^ | |
| 995 // | | |
| 996 // ---- |
| 997 { |
| 998 connectionMatrix: [][]int{ |
| 999 {1, 2}, // 0 |
| 1000 {}, // 1 |
| 1001 {2}, // 2 |
| 1002 }, |
| 1003 squares: []int{0}, |
| 1004 |
| 1005 expectedFirstA: "Node2", |
| 1006 expectedLastA: "Node2", |
| 1007 expectedPathA: "Node2", |
| 1008 expectedDebugDataA: expectedDebugData{ |
| 1009 initialPendingSet: []string{"Node2"}, |
| 1010 initialFoundationSet: []string{}, |
| 1011 }, |
| 1012 }, |
| 1013 |
| 1014 // Case: Self loop at one node below a square, other side |
| 1015 // |
| 1016 // [0] |
| 1017 // 2 1--| |
| 1018 // ^ | |
| 1019 // | | |
| 1020 // ---- |
| 1021 { |
| 1022 connectionMatrix: [][]int{ |
| 1023 {1, 2}, // 0 |
| 1024 {1}, // 1 |
| 1025 {}, // 2 |
| 1026 }, |
| 1027 squares: []int{0}, |
| 1028 |
| 1029 expectedFirstA: "Node1", |
| 1030 expectedLastA: "Node1", |
| 1031 expectedPathA: "Node1", |
| 1032 expectedDebugDataA: expectedDebugData{ |
| 1033 initialPendingSet: []string{"Node1"}, |
| 1034 initialFoundationSet: []string{}, |
| 1035 }, |
| 1036 |
| 1037 expectedFirstB: "Node1", |
| 1038 expectedLastB: "Node1", |
| 1039 expectedPathB: "Node1", |
| 1040 expectedDebugDataB: expectedDebugData{ |
| 1041 initialPendingSet: []string{"Node1"}, |
| 1042 initialFoundationSet: []string{}, |
| 1043 }, |
| 1044 }, |
| 1045 |
| 1046 // Case: Self loop off of one branch of a square. |
| 1047 // |
| 1048 // [0] |
| 1049 // 1 2 |
| 1050 // 3 -| |
| 1051 // ^ | |
| 1052 // |--- |
| 1053 { |
| 1054 connectionMatrix: [][]int{ |
| 1055 {1, 2}, // 0 |
| 1056 {3}, // 1 |
| 1057 {}, // 2 |
| 1058 {3}, // 3 |
| 1059 }, |
| 1060 squares: []int{0}, |
| 1061 |
| 1062 expectedFirstA: "Node3", |
| 1063 expectedLastA: "Node3", |
| 1064 expectedPathA: "Node3", |
| 1065 expectedDebugDataA: expectedDebugData{ |
| 1066 initialPendingSet: []string{"Node1", "Node3"}
, |
| 1067 initialFoundationSet: []string{}, |
| 1068 }, |
| 1069 |
| 1070 expectedFirstB: "Node3", |
| 1071 expectedLastB: "Node3", |
| 1072 expectedPathB: "Node3", |
| 1073 expectedDebugDataB: expectedDebugData{ |
| 1074 initialPendingSet: []string{"Node1", "Node3"}
, |
| 1075 initialFoundationSet: []string{}, |
| 1076 }, |
| 1077 }, |
| 1078 |
| 1079 // Case: Hard loop off of one branch of a square. |
| 1080 // |
| 1081 // [0] |
| 1082 // --> 1 2 |
| 1083 // | 3 |
| 1084 // |---4 |
| 1085 { |
| 1086 connectionMatrix: [][]int{ |
| 1087 {1, 2}, // 0 |
| 1088 {3}, // 1 |
| 1089 {}, // 2 |
| 1090 {4}, // 3 |
| 1091 {1}, // 4 |
| 1092 }, |
| 1093 squares: []int{0}, |
| 1094 |
| 1095 expectedFirstA: "Node1", |
| 1096 expectedLastA: "Node4", |
| 1097 expectedPathA: "Node1, Node3, Node4", |
| 1098 expectedDebugDataA: expectedDebugData{ |
| 1099 initialPendingSet: []string{"Node1", "Node3",
"Node4"}, |
| 1100 initialFoundationSet: []string{}, |
| 1101 }, |
| 1102 |
| 1103 expectedFirstB: "Node1", |
| 1104 expectedLastB: "Node4", |
| 1105 expectedPathB: "Node1, Node3, Node4", |
| 1106 expectedDebugDataB: expectedDebugData{ |
| 1107 initialPendingSet: []string{"Node1", "Node3",
"Node4"}, |
| 1108 initialFoundationSet: []string{}, |
| 1109 }, |
| 1110 }, |
| 1111 |
| 1112 // Case: Graph below 0 is well-founded and 1 is well-founded but
not the graph below 1. |
| 1113 // |
| 1114 // [1] |
| 1115 // 2 -| 0 |
| 1116 // ^ | |
| 1117 // |--| |
| 1118 // |
| 1119 // |
| 1120 { |
| 1121 connectionMatrix: [][]int{ |
| 1122 {}, // 0 |
| 1123 {0, 2}, // 1 |
| 1124 {2}, // 2 |
| 1125 }, |
| 1126 squares: []int{1}, |
| 1127 |
| 1128 expectedFirstB: "Node2", |
| 1129 expectedLastB: "Node2", |
| 1130 expectedPathB: "Node2", |
| 1131 expectedDebugDataB: expectedDebugData{ |
| 1132 initialPendingSet: []string{"Node2"}, |
| 1133 initialFoundationSet: []string{}, |
| 1134 }, |
| 1135 }, |
| 1136 |
| 1137 // Case: Different loops through both branches sharing nodes. |
| 1138 // |
| 1139 // 0 <------| |
| 1140 // 1 | |
| 1141 // [2] | |
| 1142 // 3 4 | |
| 1143 // \ 5 | |
| 1144 // \----> 6 | |
| 1145 // 7 --| |
| 1146 { |
| 1147 connectionMatrix: [][]int{ |
| 1148 {1}, // 0 |
| 1149 {2}, // 1 |
| 1150 {3, 4}, // 2 |
| 1151 {6}, // 3 |
| 1152 {5}, // 4 |
| 1153 {6}, // 5 |
| 1154 {7}, // 6 |
| 1155 {0}, // 7 |
| 1156 }, |
| 1157 squares: []int{2}, |
| 1158 |
| 1159 expectedFirstA: "Node0", |
| 1160 expectedLastA: "Node7", |
| 1161 expectedPathA: "Node0, Node1, Node2, Node3, Node6, Node
7", |
| 1162 expectedDebugDataA: expectedDebugData{ |
| 1163 initialPendingSet: []string{"Node0", "Node1",
"Node2", "Node3", "Node4", "Node5", "Node6", "Node7"}, |
| 1164 initialFoundationSet: []string{}, |
| 1165 }, |
| 1166 |
| 1167 expectedFirstB: "Node1", |
| 1168 expectedLastB: "Node0", |
| 1169 expectedPathB: "Node1, Node2, Node3, Node6, Node7, Node
0", |
| 1170 expectedDebugDataB: expectedDebugData{ |
| 1171 initialPendingSet: []string{"Node0", "Node1",
"Node2", "Node3", "Node4", "Node5", "Node6", "Node7"}, |
| 1172 initialFoundationSet: []string{}, |
| 1173 }, |
| 1174 }, |
| 1175 |
| 1176 // Case: Different loops through both branches sharing nodes--sw
itch sides. |
| 1177 // |
| 1178 // 0 <------| |
| 1179 // 1 | |
| 1180 // [2] | |
| 1181 // 4 3 | |
| 1182 // \ 5 | |
| 1183 // \----> 6 | |
| 1184 // 7 --| |
| 1185 { |
| 1186 connectionMatrix: [][]int{ |
| 1187 {1}, // 0 |
| 1188 {2}, // 1 |
| 1189 {3, 4}, // 2 |
| 1190 {5}, // 3 |
| 1191 {6}, // 4 |
| 1192 {6}, // 5 |
| 1193 {7}, // 6 |
| 1194 {0}, // 7 |
| 1195 }, |
| 1196 squares: []int{2}, |
| 1197 |
| 1198 expectedFirstA: "Node0", |
| 1199 expectedLastA: "Node7", |
| 1200 expectedPathA: "Node0, Node1, Node2, Node3, Node5, Node
6, Node7", |
| 1201 expectedDebugDataA: expectedDebugData{ |
| 1202 initialPendingSet: []string{"Node0", "Node1",
"Node2", "Node3", "Node4", "Node5", "Node6", "Node7"}, |
| 1203 initialFoundationSet: []string{}, |
| 1204 }, |
| 1205 |
| 1206 expectedFirstB: "Node1", |
| 1207 expectedLastB: "Node0", |
| 1208 expectedPathB: "Node1, Node2, Node3, Node5, Node6, Node
7, Node0", |
| 1209 expectedDebugDataB: expectedDebugData{ |
| 1210 initialPendingSet: []string{"Node0", "Node1",
"Node2", "Node3", "Node4", "Node5", "Node6", "Node7"}, |
| 1211 initialFoundationSet: []string{}, |
| 1212 }, |
| 1213 }, |
| 1214 |
| 1215 // Case: Not Well-founded becuase of 8 becuase 4 is a circle |
| 1216 // 0 |
| 1217 // 1 |
| 1218 // 2 <-- |
| 1219 // 3 | |
| 1220 // 8 <-- 4 | |
| 1221 // 5 | |
| 1222 // 6---| |
| 1223 // 7 |
| 1224 { |
| 1225 connectionMatrix: [][]int{ |
| 1226 {1}, // 0 |
| 1227 {2}, // 1 |
| 1228 {3}, // 2 |
| 1229 {4}, // 3 |
| 1230 {5, 8}, // 4 |
| 1231 {6}, // 5 |
| 1232 {2, 7}, // 6 |
| 1233 {}, // 7 |
| 1234 {}, // 8 |
| 1235 }, |
| 1236 squares: []int{}, |
| 1237 |
| 1238 expectedFirstA: "Node2", |
| 1239 expectedLastA: "Node6", |
| 1240 expectedPathA: "Node2, Node3, Node4, Node5, Node6", |
| 1241 expectedDebugDataA: expectedDebugData{ |
| 1242 initialPendingSet: []string{"Node0", "Node1",
"Node2", "Node3", "Node4", "Node5", "Node6"}, |
| 1243 initialFoundationSet: []string{}, |
| 1244 }, |
| 1245 |
| 1246 expectedFirstB: "Node2", |
| 1247 expectedLastB: "Node6", |
| 1248 expectedPathB: "Node2, Node3, Node4, Node5, Node6", |
| 1249 expectedDebugDataB: expectedDebugData{ |
| 1250 initialPendingSet: []string{"Node1", "Node2",
"Node3", "Node4", "Node5", "Node6"}, |
| 1251 initialFoundationSet: []string{}, |
| 1252 }, |
| 1253 }, |
| 1254 |
| 1255 // Case: Not Well-founded becuase of 8 becuase there is no 8 |
| 1256 // 0 |
| 1257 // 1 |
| 1258 // 2 <-- |
| 1259 // 3 | |
| 1260 // [4] | |
| 1261 // 5 | |
| 1262 // 6---| |
| 1263 // 7 |
| 1264 { |
| 1265 connectionMatrix: [][]int{ |
| 1266 {1}, // 0 |
| 1267 {2}, // 1 |
| 1268 {3}, // 2 |
| 1269 {4}, // 3 |
| 1270 {5}, // 4 |
| 1271 {6}, // 5 |
| 1272 {2, 7}, // 6 |
| 1273 {}, // 7 |
| 1274 }, |
| 1275 squares: []int{4}, |
| 1276 |
| 1277 expectedFirstA: "Node2", |
| 1278 expectedLastA: "Node6", |
| 1279 expectedPathA: "Node2, Node3, Node4, Node5, Node6", |
| 1280 expectedDebugDataA: expectedDebugData{ |
| 1281 initialPendingSet: []string{"Node0", "Node1",
"Node2", "Node3", "Node4", "Node5", "Node6"}, |
| 1282 initialFoundationSet: []string{}, |
| 1283 }, |
| 1284 |
| 1285 expectedFirstB: "Node2", |
| 1286 expectedLastB: "Node6", |
| 1287 expectedPathB: "Node2, Node3, Node4, Node5, Node6", |
| 1288 expectedDebugDataB: expectedDebugData{ |
| 1289 initialPendingSet: []string{"Node1", "Node2",
"Node3", "Node4", "Node5", "Node6"}, |
| 1290 initialFoundationSet: []string{}, |
| 1291 }, |
| 1292 }, |
| 1293 } |
| 1294 |
| 1295 for i, c := range cases { |
| 1296 graph := buildTestGraph(c.connectionMatrix, c.squares) |
| 1297 // Test from root=Node0 |
| 1298 dbgData := debugData{} |
| 1299 cycleDescription := checkWellFounded(graph[0], &dbgData) |
| 1300 if err := compareDebugData(&c.expectedDebugDataA, &dbgData); err
!= nil { |
| 1301 t.Fatalf("Case %dA: %s", i, err.Error()) |
| 1302 } |
| 1303 if len(c.expectedPathA) == 0 { |
| 1304 if cycleDescription != nil { |
| 1305 t.Fatalf("Case %dA: Detected a cycle: %s", i, cy
cleDescription) |
| 1306 } |
| 1307 } else { |
| 1308 if cycleDescription == nil { |
| 1309 t.Fatalf("Case %d: Expected a cycle.", i) |
| 1310 } |
| 1311 if !strings.Contains(cycleDescription.String(), fmt.Spri
ntf("first:%s", c.expectedFirstA)) { |
| 1312 t.Fatalf("Case %d: got=%s expectedFirst=%q", i,
cycleDescription.String(), c.expectedFirstA) |
| 1313 } |
| 1314 if !strings.Contains(cycleDescription.String(), fmt.Spri
ntf("last:%s", c.expectedLastA)) { |
| 1315 t.Fatalf("Case %d: got=%s expectedLast=%q", i, c
ycleDescription.String(), c.expectedLastA) |
| 1316 } |
| 1317 if !strings.Contains(cycleDescription.String(), fmt.Spri
ntf("{%s}", c.expectedPathA)) { |
| 1318 t.Fatalf("Case %d: got=%s expectedPath=%q", i, c
ycleDescription.String(), c.expectedPathA) |
| 1319 } |
| 1320 } |
| 1321 |
| 1322 // Test from root=Node1 |
| 1323 if len(graph) > 1 { |
| 1324 dbgData := debugData{} |
| 1325 cycleDescription := checkWellFounded(graph[1], &dbgData) |
| 1326 if err := compareDebugData(&c.expectedDebugDataB, &dbgDa
ta); err != nil { |
| 1327 t.Fatalf("Case %dB: %s", i, err.Error()) |
| 1328 } |
| 1329 if len(c.expectedPathB) == 0 { |
| 1330 if cycleDescription != nil { |
| 1331 t.Fatalf("Case %dB: Detected a cycle: %s
", i, cycleDescription) |
| 1332 } |
| 1333 } else { |
| 1334 if cycleDescription == nil { |
| 1335 t.Fatalf("Case %dB: Expected a cycle.",
i) |
| 1336 } |
| 1337 if !strings.Contains(cycleDescription.String(),
fmt.Sprintf("first:%s", c.expectedFirstB)) { |
| 1338 t.Fatalf("Case %dB: got=%s expectedFirst
=%q", i, cycleDescription.String(), c.expectedFirstB) |
| 1339 } |
| 1340 if !strings.Contains(cycleDescription.String(),
fmt.Sprintf("last:%s", c.expectedLastB)) { |
| 1341 t.Fatalf("Case %dB: got=%s expectedLast=
%q", i, cycleDescription.String(), c.expectedLastB) |
| 1342 } |
| 1343 if !strings.Contains(cycleDescription.String(),
fmt.Sprintf("{%s}", c.expectedPathB)) { |
| 1344 t.Fatalf("Case %dB: got=%s expectedPath=
%q", i, cycleDescription.String(), c.expectedPathB) |
| 1345 } |
| 1346 } |
| 1347 } |
| 1348 } |
| 1349 } |
OLD | NEW |