9394b30b77
Bug: b/286184681 Change-Id: I903528c4adfbc576644aec7541903df6b9633e26 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/325523 Reviewed-by: Jonas Termansen <sortie@google.com> Reviewed-by: Samuel Rawlins <srawlins@google.com> Commit-Queue: Alexander Thomas <athom@google.com>
659 lines
12 KiB
Dart
659 lines
12 KiB
Dart
// 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 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]
|
|
]
|
|
]);
|
|
|
|
testTransitiveDependencies(graphData: {
|
|
A: [B],
|
|
B: [A],
|
|
}, of: {
|
|
A
|
|
}, expected: {
|
|
A,
|
|
B,
|
|
});
|
|
|
|
testTransitiveDependencies(graphData: {}, of: {
|
|
A,
|
|
B,
|
|
C,
|
|
D,
|
|
E,
|
|
F,
|
|
}, expected: {});
|
|
|
|
{
|
|
String MAIN = "MAIN";
|
|
String DARTFFI = "DARTFFI";
|
|
testTransitiveDependencies(graphData: {
|
|
MAIN: [DARTFFI],
|
|
}, of: {
|
|
DARTFFI
|
|
}, expected: {
|
|
MAIN,
|
|
});
|
|
}
|
|
|
|
testTransitiveDependencies(graphData: {
|
|
A: [B],
|
|
B: [C],
|
|
C: [B, D],
|
|
D: [C],
|
|
E: [A],
|
|
F: [B],
|
|
}, of: {
|
|
A
|
|
}, expected: {
|
|
A,
|
|
E
|
|
});
|
|
|
|
{
|
|
String MAIN = "MAIN";
|
|
String TARGET = "TARGET";
|
|
|
|
testTransitiveDependencies(graphData: {
|
|
MAIN: [A, E],
|
|
A: [B],
|
|
B: [A, C],
|
|
C: [TARGET],
|
|
D: [],
|
|
E: [D],
|
|
}, of: {
|
|
TARGET,
|
|
}, expected: {
|
|
C,
|
|
B,
|
|
A,
|
|
MAIN,
|
|
});
|
|
|
|
testTransitiveDependencies(graphData: {
|
|
MAIN: [A, E],
|
|
A: [B],
|
|
B: [A, C],
|
|
C: [],
|
|
D: [],
|
|
E: [D],
|
|
}, of: {
|
|
TARGET,
|
|
}, expected: {});
|
|
|
|
testTransitiveDependencies(graphData: {
|
|
MAIN: [A, C, E, TARGET],
|
|
A: [B],
|
|
B: [A, C],
|
|
C: [],
|
|
D: [],
|
|
E: [D],
|
|
}, of: {
|
|
TARGET,
|
|
}, expected: {
|
|
MAIN,
|
|
});
|
|
}
|
|
}
|
|
|
|
void testTransitiveDependencies(
|
|
{required Set<String> of,
|
|
required Set<String> expected,
|
|
required Map<String, List<String>> graphData}) {
|
|
Graph<String> graph = new TestGraph(graphData);
|
|
Set<String> actual = calculateTransitiveDependenciesOf(graph, of);
|
|
Expect.setEquals(expected, actual);
|
|
}
|