| // Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE.md file. |
| |
| library kernel.test.graph_test; |
| |
| import 'package:expect/expect.dart' show Expect; |
| |
| import 'package:kernel/util/graph.dart'; |
| |
| const String A = 'A'; |
| const String B = 'B'; |
| const String C = 'C'; |
| const String D = 'D'; |
| const String E = 'E'; |
| const String F = 'F'; |
| |
| class TestGraph implements Graph<String> { |
| final Map<String, List<String>> graph; |
| |
| TestGraph(this.graph); |
| |
| @override |
| Iterable<String> get vertices => graph.keys; |
| |
| @override |
| Iterable<String> neighborsOf(String vertex) => graph[vertex]!; |
| } |
| |
| void test( |
| {required List<List<String>> expectedStrongComponents, |
| List<String> expectedSortedVertices = const [], |
| List<String> expectedCyclicVertices = const [], |
| List<List<String>> expectedLayers = const [], |
| List<List<List<String>>> expectedStrongLayers = const [], |
| required Map<String, List<String>> graphData}) { |
| Graph<String> graph = new TestGraph(graphData); |
| |
| List<List<String>> strongComponentResult = computeStrongComponents(graph); |
| Expect.equals( |
| expectedStrongComponents.length, |
| strongComponentResult.length, |
| "Unexpected strongly connected components count. " |
| "Expected ${expectedStrongComponents}, " |
| "actual ${strongComponentResult}"); |
| for (int index = 0; index < expectedStrongComponents.length; index++) { |
| Expect.listEquals( |
| expectedStrongComponents[index], |
| strongComponentResult[index], |
| "Unexpected strongly connected components. " |
| "Expected $expectedStrongComponents, actual $strongComponentResult."); |
| } |
| |
| TopologicalSortResult<String> topologicalResult = topologicalSort(graph); |
| Set<String> sortedAndCyclicVertices = topologicalResult.sortedVertices |
| .toSet() |
| .intersection(topologicalResult.cyclicVertices.toSet()); |
| Expect.isTrue(sortedAndCyclicVertices.isEmpty, |
| "Found vertices both sorted and cyclic: $sortedAndCyclicVertices"); |
| List<String> sortedOrCyclicVertices = [ |
| ...topologicalResult.sortedVertices, |
| ...topologicalResult.cyclicVertices |
| ]; |
| Expect.equals( |
| graphData.length, |
| sortedOrCyclicVertices.length, |
| "Unexpected vertex count. Expected ${graphData.length}, " |
| "found ${sortedOrCyclicVertices.length}."); |
| Expect.listEquals( |
| expectedSortedVertices, |
| topologicalResult.sortedVertices, |
| "Unexpected sorted vertices. " |
| "Expected $expectedSortedVertices, " |
| "actual ${topologicalResult.sortedVertices}."); |
| Expect.listEquals( |
| expectedCyclicVertices, |
| topologicalResult.cyclicVertices, |
| "Unexpected cyclic vertices. " |
| "Expected $expectedCyclicVertices, " |
| "actual ${topologicalResult.cyclicVertices}."); |
| Expect.equals( |
| expectedLayers.length, |
| topologicalResult.layers.length, |
| "Unexpected topological layer count. " |
| "Expected ${expectedLayers}, " |
| "actual ${topologicalResult.layers}"); |
| for (int index = 0; index < expectedLayers.length; index++) { |
| Expect.listEquals( |
| expectedLayers[index], |
| topologicalResult.layers[index], |
| "Unexpected topological layers. " |
| "Expected $expectedLayers, " |
| "actual ${topologicalResult.layers}."); |
| for (String vertex in topologicalResult.layers[index]) { |
| int actualIndex = topologicalResult.indexMap[vertex]!; |
| Expect.equals( |
| index, |
| actualIndex, |
| "Unexpected topological index for $vertex. " |
| "Expected $index, found $actualIndex."); |
| } |
| } |
| |
| StrongComponentGraph<String> strongComponentGraph = |
| new StrongComponentGraph(graph, strongComponentResult); |
| TopologicalSortResult<List<String>> strongTopologicalResult = |
| topologicalSort(strongComponentGraph); |
| Expect.equals( |
| expectedStrongLayers.length, |
| strongTopologicalResult.layers.length, |
| "Unexpected strong topological layer count. " |
| "Expected ${expectedStrongLayers}, " |
| "actual ${strongTopologicalResult.layers}"); |
| for (int index = 0; index < expectedStrongLayers.length; index++) { |
| List<List<String>> expectedStrongLayer = expectedStrongLayers[index]; |
| List<List<String>> strongLayer = strongTopologicalResult.layers[index]; |
| Expect.equals( |
| expectedStrongLayer.length, |
| strongLayer.length, |
| "Unexpected strong topological layer $index count. " |
| "Expected ${expectedStrongLayers}, " |
| "actual ${strongTopologicalResult.layers}"); |
| |
| for (int subIndex = 0; subIndex < expectedStrongLayer.length; subIndex++) { |
| Expect.listEquals( |
| expectedStrongLayer[subIndex], |
| strongLayer[subIndex], |
| "Unexpected strong topological layer $index. " |
| "Expected $expectedStrongLayer, " |
| "actual $strongLayer."); |
| } |
| for (List<String> vertex in strongTopologicalResult.layers[index]) { |
| int actualIndex = strongTopologicalResult.indexMap[vertex]!; |
| Expect.equals( |
| index, |
| actualIndex, |
| "Unexpected strong topological index for $vertex. " |
| "Expected $index, found $actualIndex."); |
| } |
| } |
| } |
| |
| void main() { |
| test(graphData: { |
| A: [B], |
| B: [A], |
| C: [A], |
| D: [C], |
| }, expectedStrongComponents: [ |
| [B, A], |
| [C], |
| [D] |
| ], expectedCyclicVertices: [ |
| B, |
| A, |
| C, |
| D |
| ], expectedStrongLayers: [ |
| [ |
| [B, A] |
| ], |
| [ |
| [C] |
| ], |
| [ |
| [D] |
| ] |
| ]); |
| |
| test(graphData: {}, expectedStrongComponents: []); |
| |
| test(graphData: { |
| A: [], |
| B: [], |
| C: [], |
| D: [], |
| }, expectedStrongComponents: [ |
| [A], |
| [B], |
| [C], |
| [D] |
| ], expectedSortedVertices: [ |
| A, |
| B, |
| C, |
| D |
| ], expectedLayers: [ |
| [A, B, C, D] |
| ], expectedStrongLayers: [ |
| [ |
| [A], |
| [B], |
| [C], |
| [D] |
| ] |
| ]); |
| |
| test(graphData: { |
| D: [C], |
| C: [A], |
| B: [A], |
| A: [B], |
| }, expectedStrongComponents: [ |
| [B, A], |
| [C], |
| [D] |
| ], expectedCyclicVertices: [ |
| B, |
| A, |
| C, |
| D |
| ], expectedStrongLayers: [ |
| [ |
| [B, A] |
| ], |
| [ |
| [C] |
| ], |
| [ |
| [D] |
| ] |
| ]); |
| |
| test(graphData: { |
| A: [B], |
| B: [C], |
| C: [D], |
| D: [], |
| }, expectedStrongComponents: [ |
| [D], |
| [C], |
| [B], |
| [A] |
| ], expectedSortedVertices: [ |
| D, |
| C, |
| B, |
| A |
| ], expectedLayers: [ |
| [D], |
| [C], |
| [B], |
| [A] |
| ], expectedStrongLayers: [ |
| [ |
| [D] |
| ], |
| [ |
| [C] |
| ], |
| [ |
| [B] |
| ], |
| [ |
| [A] |
| ] |
| ]); |
| |
| test(graphData: { |
| D: [], |
| C: [D], |
| B: [C], |
| A: [B], |
| }, expectedStrongComponents: [ |
| [D], |
| [C], |
| [B], |
| [A] |
| ], expectedSortedVertices: [ |
| D, |
| C, |
| B, |
| A |
| ], expectedLayers: [ |
| [D], |
| [C], |
| [B], |
| [A] |
| ], expectedStrongLayers: [ |
| [ |
| [D] |
| ], |
| [ |
| [C] |
| ], |
| [ |
| [B] |
| ], |
| [ |
| [A] |
| ] |
| ]); |
| |
| test(graphData: { |
| A: [], |
| B: [A], |
| C: [A], |
| D: [B, C], |
| }, expectedStrongComponents: [ |
| [A], |
| [B], |
| [C], |
| [D] |
| ], expectedSortedVertices: [ |
| A, |
| B, |
| C, |
| D |
| ], expectedLayers: [ |
| [A], |
| [B, C], |
| [D] |
| ], expectedStrongLayers: [ |
| [ |
| [A] |
| ], |
| [ |
| [B], |
| [C] |
| ], |
| [ |
| [D] |
| ] |
| ]); |
| |
| // Test a complex graph. |
| test(graphData: { |
| A: [B, C], |
| B: [C, D], |
| C: [], |
| D: [C, E], |
| E: [B, F], |
| F: [C, D] |
| }, expectedStrongComponents: [ |
| [C], |
| [F, E, D, B], |
| [A], |
| ], expectedSortedVertices: [ |
| C |
| ], expectedCyclicVertices: [ |
| E, |
| D, |
| B, |
| A, |
| F |
| ], expectedLayers: [ |
| [C] |
| ], expectedStrongLayers: [ |
| [ |
| [C] |
| ], |
| [ |
| [F, E, D, B] |
| ], |
| [ |
| [A] |
| ] |
| ]); |
| |
| // Test a diamond-shaped graph. |
| test(graphData: { |
| A: [B, C], |
| B: [D], |
| C: [D], |
| D: [] |
| }, expectedStrongComponents: [ |
| [D], |
| [B], |
| [C], |
| [A], |
| ], expectedSortedVertices: [ |
| D, |
| B, |
| C, |
| A |
| ], expectedLayers: [ |
| [D], |
| [B, C], |
| [A] |
| ], expectedStrongLayers: [ |
| [ |
| [D] |
| ], |
| [ |
| [B], |
| [C] |
| ], |
| [ |
| [A] |
| ] |
| ]); |
| |
| // Test a graph with a single node. |
| test(graphData: { |
| A: [] |
| }, expectedStrongComponents: [ |
| [A] |
| ], expectedSortedVertices: [ |
| A |
| ], expectedLayers: [ |
| [A] |
| ], expectedStrongLayers: [ |
| [ |
| [A] |
| ] |
| ]); |
| |
| // Test a graph with a single node and a trivial cycle. |
| test(graphData: { |
| A: [A] |
| }, expectedStrongComponents: [ |
| [A] |
| ], expectedCyclicVertices: [ |
| A |
| ], expectedStrongLayers: [ |
| [ |
| [A] |
| ] |
| ]); |
| |
| // Test a graph with three nodes with circular dependencies. |
| test(graphData: { |
| A: [B], |
| B: [C], |
| C: [A], |
| }, expectedStrongComponents: [ |
| [C, B, A] |
| ], expectedCyclicVertices: [ |
| C, |
| B, |
| A |
| ], expectedStrongLayers: [ |
| [ |
| [C, B, A] |
| ] |
| ]); |
| |
| // Test a graph A->B->C->D, where D points back to B and then C. |
| test(graphData: { |
| A: [B], |
| B: [C], |
| C: [D], |
| D: [B, C] |
| }, expectedStrongComponents: [ |
| [D, C, B], |
| [A] |
| ], expectedCyclicVertices: [ |
| D, |
| C, |
| B, |
| A |
| ], expectedStrongLayers: [ |
| [ |
| [D, C, B] |
| ], |
| [ |
| [A] |
| ] |
| ]); |
| |
| // Test a graph A->B->C->D, where D points back to C and then B. |
| test(graphData: { |
| A: [B], |
| B: [C], |
| C: [D], |
| D: [C, B] |
| }, expectedStrongComponents: [ |
| [D, C, B], |
| [A] |
| ], expectedCyclicVertices: [ |
| D, |
| C, |
| B, |
| A |
| ], expectedStrongLayers: [ |
| [ |
| [D, C, B] |
| ], |
| [ |
| [A] |
| ] |
| ]); |
| |
| // Test a graph with two nodes with circular dependencies. |
| test(graphData: { |
| A: [B], |
| B: [A] |
| }, expectedStrongComponents: [ |
| [B, A] |
| ], expectedCyclicVertices: [ |
| B, |
| A |
| ], expectedStrongLayers: [ |
| [ |
| [B, A] |
| ] |
| ]); |
| |
| // Test a graph with two nodes and a single dependency. |
| test(graphData: { |
| A: [B], |
| B: [] |
| }, expectedStrongComponents: [ |
| [B], |
| [A] |
| ], expectedSortedVertices: [ |
| B, |
| A |
| ], expectedLayers: [ |
| [B], |
| [A] |
| ], expectedStrongLayers: [ |
| [ |
| [B] |
| ], |
| [ |
| [A] |
| ] |
| ]); |
| |
| test(graphData: { |
| A: [], |
| B: [A], |
| C: [B, D], |
| D: [C], |
| E: [A], |
| F: [B], |
| }, expectedStrongComponents: [ |
| [A], |
| [B], |
| [D, C], |
| [E], |
| [F], |
| ], expectedSortedVertices: [ |
| A, |
| B, |
| E, |
| F, |
| ], expectedCyclicVertices: [ |
| D, |
| C, |
| ], expectedLayers: [ |
| [A], |
| [B, E], |
| [F], |
| ], expectedStrongLayers: [ |
| [ |
| [A] |
| ], |
| [ |
| [B], |
| [E] |
| ], |
| [ |
| [D, C], |
| [F] |
| ] |
| ]); |
| } |