d7f200b97d
(Part of https://github.com/dart-lang/sdk/issues/63288) This change migrates the kernel package to use the new constructor declaration syntax, described in https://github.com/dart-lang/language/blob/main/accepted/future-releases/primary-constructors/feature-specification.md#abbreviations-of-in-body-constructor-declarations. This change was performed in an automated fashion, by (a) bumping the package's SDK constraint to `3.13.0-0`, (b) enabling the lints `unnecessary_type_name_in_constructor` and `unnecessary_const_in_enum_constructor`, (c) fixing the resulting lint failures using `dart fix`, and then (d) reformatting the affected files. To ease code review, I've reverted unrelated formatting changes. Change-Id: I8d32a0b26450a44dfa2ebd9fe2dff9df6a6a6964 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/508426 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
681 lines
13 KiB
Dart
681 lines
13 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;
|
|
|
|
new(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);
|
|
}
|