blob: 67029b938a8c53844f0a644dda379571f6b15102 [file] [edit]
// 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);
}