Index: tools/testing/dart/test_runner.dart |
diff --git a/tools/testing/dart/test_runner.dart b/tools/testing/dart/test_runner.dart |
index fd5598bf3dff4afff9261211f9e5c30bf1865c4d..0a32721152a041d7025b4a00f8da1369259a5b8b 100644 |
--- a/tools/testing/dart/test_runner.dart |
+++ b/tools/testing/dart/test_runner.dart |
@@ -22,7 +22,7 @@ import 'browser_controller.dart'; |
import 'command.dart'; |
import 'command_output.dart'; |
import 'configuration.dart'; |
-import 'dependency_graph.dart' as dgraph; |
+import 'dependency_graph.dart'; |
import 'expectation.dart'; |
import 'runtime_configuration.dart'; |
import 'test_progress.dart'; |
@@ -818,56 +818,56 @@ class BatchRunnerProcess { |
* on them, so we can safely use them as keys in Map/Set objects. |
*/ |
class TestCaseEnqueuer { |
- final dgraph.Graph graph; |
+ final Graph<Command> graph; |
final Function _onTestCaseAdded; |
- final command2node = new Map<Command, dgraph.Node>(); |
- final command2testCases = new Map<Command, List<TestCase>>(); |
+ final command2node = <Command, Node<Command>>{}; |
+ final command2testCases = <Command, List<TestCase>>{}; |
final remainingTestCases = new Set<TestCase>(); |
TestCaseEnqueuer(this.graph, this._onTestCaseAdded); |
void enqueueTestSuites(List<TestSuite> testSuites) { |
- void newTest(TestCase testCase) { |
- remainingTestCases.add(testCase); |
- |
- dgraph.Node lastNode; |
- for (var command in testCase.commands) { |
- // Make exactly *one* node in the dependency graph for every command. |
- // This ensures that we never have two commands c1 and c2 in the graph |
- // with "c1 == c2". |
- var node = command2node[command]; |
- if (node == null) { |
- var requiredNodes = (lastNode != null) ? [lastNode] : <dgraph.Node>[]; |
- node = graph.newNode(command, requiredNodes); |
- command2node[command] = node; |
- command2testCases[command] = <TestCase>[]; |
- } |
- // Keep mapping from command to all testCases that refer to it |
- command2testCases[command].add(testCase); |
- |
- lastNode = node; |
- } |
- _onTestCaseAdded(testCase); |
- } |
- |
// Cache information about test cases per test suite. For multiple |
// configurations there is no need to repeatedly search the file |
// system, generate tests, and search test files for options. |
- var testCache = new Map<String, List<TestInformation>>(); |
+ var testCache = <String, List<TestInformation>>{}; |
- Iterator<TestSuite> iterator = testSuites.iterator; |
+ var iterator = testSuites.iterator; |
void enqueueNextSuite() { |
if (!iterator.moveNext()) { |
// We're finished with building the dependency graph. |
- graph.sealGraph(); |
+ graph.seal(); |
} else { |
- iterator.current.forEachTest(newTest, testCache, enqueueNextSuite); |
+ iterator.current.forEachTest(_newTest, testCache, enqueueNextSuite); |
} |
} |
enqueueNextSuite(); |
} |
+ |
+ void _newTest(TestCase testCase) { |
+ remainingTestCases.add(testCase); |
+ |
+ Node<Command> lastNode; |
+ for (var command in testCase.commands) { |
+ // Make exactly *one* node in the dependency graph for every command. |
+ // This ensures that we never have two commands c1 and c2 in the graph |
+ // with "c1 == c2". |
+ var node = command2node[command]; |
+ if (node == null) { |
+ var requiredNodes = (lastNode != null) ? [lastNode] : <Node<Command>>[]; |
+ node = graph.add(command, requiredNodes); |
+ command2node[command] = node; |
+ command2testCases[command] = <TestCase>[]; |
+ } |
+ // Keep mapping from command to all testCases that refer to it |
+ command2testCases[command].add(testCase); |
+ |
+ lastNode = node; |
+ } |
+ _onTestCaseAdded(testCase); |
+ } |
} |
/* |
@@ -878,31 +878,23 @@ class TestCaseEnqueuer { |
* have a state of NodeState.Failed/NodeState.UnableToRun. |
*/ |
class CommandEnqueuer { |
- static final INIT_STATES = [ |
- dgraph.NodeState.Initialized, |
- dgraph.NodeState.Waiting |
- ]; |
- static final FINISHED_STATES = [ |
- dgraph.NodeState.Successful, |
- dgraph.NodeState.Failed, |
- dgraph.NodeState.UnableToRun |
+ static const _initStates = const [NodeState.initialized, NodeState.waiting]; |
+ |
+ static const _finishedStates = const [ |
+ NodeState.successful, |
+ NodeState.failed, |
+ NodeState.unableToRun |
]; |
- final dgraph.Graph _graph; |
- CommandEnqueuer(this._graph) { |
- var eventCondition = _graph.events.where; |
+ final Graph<Command> _graph; |
- eventCondition((e) => e is dgraph.NodeAddedEvent).listen((e) { |
- var event = e as dgraph.NodeAddedEvent; |
- dgraph.Node node = event.node; |
- _changeNodeStateIfNecessary(node); |
- }); |
+ CommandEnqueuer(this._graph) { |
+ _graph.added.listen(_changeNodeStateIfNecessary); |
- eventCondition((e) => e is dgraph.StateChangedEvent).listen((e) { |
- var event = e as dgraph.StateChangedEvent; |
- if ([dgraph.NodeState.Waiting, dgraph.NodeState.Processing] |
- .contains(event.from)) { |
- if (FINISHED_STATES.contains(event.to)) { |
+ _graph.changed.listen((event) { |
+ if (event.from == NodeState.waiting || |
+ event.from == NodeState.processing) { |
+ if (_finishedStates.contains(event.to)) { |
for (var dependendNode in event.node.neededFor) { |
_changeNodeStateIfNecessary(dependendNode); |
} |
@@ -913,22 +905,20 @@ class CommandEnqueuer { |
// Called when either a new node was added or if one of it's dependencies |
// changed it's state. |
- void _changeNodeStateIfNecessary(dgraph.Node node) { |
- if (INIT_STATES.contains(node.state)) { |
- bool anyDependenciesUnsuccessful = node.dependencies.any((dep) => [ |
- dgraph.NodeState.Failed, |
- dgraph.NodeState.UnableToRun |
- ].contains(dep.state)); |
- |
- var newState = dgraph.NodeState.Waiting; |
+ void _changeNodeStateIfNecessary(Node<Command> node) { |
+ if (_initStates.contains(node.state)) { |
+ bool anyDependenciesUnsuccessful = node.dependencies.any((dep) => |
+ [NodeState.failed, NodeState.unableToRun].contains(dep.state)); |
+ |
+ var newState = NodeState.waiting; |
if (anyDependenciesUnsuccessful) { |
- newState = dgraph.NodeState.UnableToRun; |
+ newState = NodeState.unableToRun; |
} else { |
- bool allDependenciesSuccessful = node.dependencies |
- .every((dep) => dep.state == dgraph.NodeState.Successful); |
+ bool allDependenciesSuccessful = |
+ node.dependencies.every((dep) => dep.state == NodeState.successful); |
if (allDependenciesSuccessful) { |
- newState = dgraph.NodeState.Enqueuing; |
+ newState = NodeState.enqueuing; |
} |
} |
if (node.state != newState) { |
@@ -952,7 +942,7 @@ class CommandEnqueuer { |
* and the [executor] has cleaned up it's resources. |
*/ |
class CommandQueue { |
- final dgraph.Graph graph; |
+ final Graph<Command> graph; |
final CommandExecutor executor; |
final TestCaseEnqueuer enqueuer; |
@@ -969,34 +959,29 @@ class CommandQueue { |
CommandQueue(this.graph, this.enqueuer, this.executor, this._maxProcesses, |
this._maxBrowserProcesses, this._verbose) { |
- var eventCondition = graph.events.where; |
- eventCondition((e) => e is dgraph.StateChangedEvent).listen((e) { |
- var event = e as dgraph.StateChangedEvent; |
- if (event.to == dgraph.NodeState.Enqueuing) { |
- assert(event.from == dgraph.NodeState.Initialized || |
- event.from == dgraph.NodeState.Waiting); |
- graph.changeState(event.node, dgraph.NodeState.Processing); |
- var command = event.node.userData as Command; |
- if (event.node.dependencies.length > 0) { |
+ graph.changed.listen((event) { |
+ if (event.to == NodeState.enqueuing) { |
+ assert(event.from == NodeState.initialized || |
+ event.from == NodeState.waiting); |
+ graph.changeState(event.node, NodeState.processing); |
+ var command = event.node.data; |
+ if (event.node.dependencies.isNotEmpty) { |
_runQueue.addFirst(command); |
} else { |
_runQueue.add(command); |
} |
Timer.run(() => _tryRunNextCommand()); |
+ } else if (event.to == NodeState.unableToRun) { |
+ _checkDone(); |
} |
}); |
+ |
// We're finished if the graph is sealed and all nodes are in a finished |
// state (Successful, Failed or UnableToRun). |
// So we're calling '_checkDone()' to check whether that condition is met |
// and we can cleanup. |
- graph.events.listen((dgraph.GraphEvent event) { |
- if (event is dgraph.GraphSealedEvent) { |
- _checkDone(); |
- } else if (event is dgraph.StateChangedEvent) { |
- if (event.to == dgraph.NodeState.UnableToRun) { |
- _checkDone(); |
- } |
- } |
+ graph.sealed.listen((event) { |
+ _checkDone(); |
}); |
} |
@@ -1039,9 +1024,9 @@ class CommandQueue { |
_commandOutputStream.add(output); |
if (output.canRunDependendCommands) { |
- graph.changeState(node, dgraph.NodeState.Successful); |
+ graph.changeState(node, NodeState.successful); |
} else { |
- graph.changeState(node, dgraph.NodeState.Failed); |
+ graph.changeState(node, NodeState.failed); |
} |
_numProcesses--; |
@@ -1058,10 +1043,10 @@ class CommandQueue { |
_runQueue.isEmpty && |
_numProcesses == 0 && |
graph.isSealed && |
- graph.stateCount(dgraph.NodeState.Initialized) == 0 && |
- graph.stateCount(dgraph.NodeState.Waiting) == 0 && |
- graph.stateCount(dgraph.NodeState.Enqueuing) == 0 && |
- graph.stateCount(dgraph.NodeState.Processing) == 0) { |
+ graph.stateCount(NodeState.initialized) == 0 && |
+ graph.stateCount(NodeState.waiting) == 0 && |
+ graph.stateCount(NodeState.enqueuing) == 0 && |
+ graph.stateCount(NodeState.processing) == 0) { |
_finishing = true; |
executor.cleanup().then((_) { |
_completer.complete(); |
@@ -1099,7 +1084,7 @@ abstract class CommandExecutor { |
Future cleanup(); |
// TODO(kustermann): The [timeout] parameter should be a property of Command |
Future<CommandOutput> runCommand( |
- dgraph.Node node, covariant Command command, int timeout); |
+ Node<Command> node, covariant Command command, int timeout); |
} |
class CommandExecutorImpl implements CommandExecutor { |
@@ -1401,53 +1386,51 @@ bool shouldRetryCommand(CommandOutput output) { |
* closed. |
*/ |
class TestCaseCompleter { |
- static final COMPLETED_STATES = [ |
- dgraph.NodeState.Failed, |
- dgraph.NodeState.Successful |
+ static const _completedStates = const [ |
+ NodeState.failed, |
+ NodeState.successful |
]; |
- final dgraph.Graph graph; |
- final TestCaseEnqueuer enqueuer; |
- final CommandQueue commandQueue; |
- Map<Command, CommandOutput> _outputs = new Map<Command, CommandOutput>(); |
+ final Graph<Command> _graph; |
+ final TestCaseEnqueuer _enqueuer; |
+ final CommandQueue _commandQueue; |
+ |
+ final Map<Command, CommandOutput> _outputs = {}; |
+ final StreamController<TestCase> _controller = new StreamController(); |
bool _closed = false; |
- StreamController<TestCase> _controller = new StreamController<TestCase>(); |
- TestCaseCompleter(this.graph, this.enqueuer, this.commandQueue) { |
- var eventCondition = graph.events.where; |
- bool finishedRemainingTestCases = false; |
+ TestCaseCompleter(this._graph, this._enqueuer, this._commandQueue) { |
+ var finishedRemainingTestCases = false; |
// Store all the command outputs -- they will be delivered synchronously |
// (i.e. before state changes in the graph) |
- commandQueue.completedCommands.listen((CommandOutput output) { |
+ _commandQueue.completedCommands.listen((CommandOutput output) { |
_outputs[output.command] = output; |
}, onDone: () { |
- _completeTestCasesIfPossible(new List.from(enqueuer.remainingTestCases)); |
+ _completeTestCasesIfPossible(new List.from(_enqueuer.remainingTestCases)); |
finishedRemainingTestCases = true; |
- assert(enqueuer.remainingTestCases.isEmpty); |
+ assert(_enqueuer.remainingTestCases.isEmpty); |
_checkDone(); |
}); |
// Listen for NodeState.Processing -> NodeState.{Successful,Failed} |
// changes. |
- eventCondition((event) => event is dgraph.StateChangedEvent).listen((e) { |
- var event = e as dgraph.StateChangedEvent; |
- if (event.from == dgraph.NodeState.Processing && |
- !finishedRemainingTestCases) { |
- var command = event.node.userData; |
+ _graph.changed.listen((event) { |
+ if (event.from == NodeState.processing && !finishedRemainingTestCases) { |
+ var command = event.node.data; |
- assert(COMPLETED_STATES.contains(event.to)); |
+ assert(_completedStates.contains(event.to)); |
assert(_outputs[command] != null); |
- _completeTestCasesIfPossible(enqueuer.command2testCases[command]); |
+ _completeTestCasesIfPossible(_enqueuer.command2testCases[command]); |
_checkDone(); |
} |
}); |
- // Listen also for GraphSealedEvent's. If there is not a single node in the |
+ // Listen also for GraphSealedEvents. If there is not a single node in the |
// graph, we still want to finish after the graph was sealed. |
- eventCondition((event) => event is dgraph.GraphSealedEvent).listen((_) { |
- if (!_closed && enqueuer.remainingTestCases.isEmpty) { |
+ _graph.sealed.listen((_) { |
+ if (!_closed && _enqueuer.remainingTestCases.isEmpty) { |
_controller.close(); |
_closed = true; |
} |
@@ -1457,7 +1440,7 @@ class TestCaseCompleter { |
Stream<TestCase> get finishedTestCases => _controller.stream; |
void _checkDone() { |
- if (!_closed && graph.isSealed && enqueuer.remainingTestCases.isEmpty) { |
+ if (!_closed && _graph.isSealed && _enqueuer.remainingTestCases.isEmpty) { |
_controller.close(); |
_closed = true; |
} |
@@ -1475,9 +1458,9 @@ class TestCaseCompleter { |
} |
void completeTestCase(TestCase testCase) { |
- if (enqueuer.remainingTestCases.contains(testCase)) { |
+ if (_enqueuer.remainingTestCases.contains(testCase)) { |
_controller.add(testCase); |
- enqueuer.remainingTestCases.remove(testCase); |
+ _enqueuer.remainingTestCases.remove(testCase); |
} else { |
DebugLogger.error("${testCase.displayName} would be finished twice"); |
} |
@@ -1498,7 +1481,7 @@ class ProcessQueue { |
Configuration _globalConfiguration; |
Function _allDone; |
- final dgraph.Graph _graph = new dgraph.Graph(); |
+ final Graph<Command> _graph = new Graph(); |
List<EventListener> _eventListener; |
ProcessQueue( |
@@ -1512,9 +1495,7 @@ class ProcessQueue { |
[bool verbose = false, |
AdbDevicePool adbDevicePool]) { |
void setupForListing(TestCaseEnqueuer testCaseEnqueuer) { |
- _graph.events |
- .where((event) => event is dgraph.GraphSealedEvent) |
- .listen((_) { |
+ _graph.sealed.listen((_) { |
var testCases = testCaseEnqueuer.remainingTestCases.toList(); |
testCases.sort((a, b) => a.displayName.compareTo(b.displayName)); |
@@ -1553,13 +1534,13 @@ class ProcessQueue { |
print(""); |
print("Graph is sealed: ${_graph.isSealed}"); |
print(""); |
- _graph.DumpCounts(); |
+ _graph.dumpCounts(); |
print(""); |
var unfinishedNodeStates = [ |
- dgraph.NodeState.Initialized, |
- dgraph.NodeState.Waiting, |
- dgraph.NodeState.Enqueuing, |
- dgraph.NodeState.Processing |
+ NodeState.initialized, |
+ NodeState.waiting, |
+ NodeState.enqueuing, |
+ NodeState.processing |
]; |
for (var nodeState in unfinishedNodeStates) { |
@@ -1569,7 +1550,7 @@ class ProcessQueue { |
print(""); |
for (var node in _graph.nodes) { |
if (node.state == nodeState) { |
- var command = node.userData; |
+ var command = node.data; |
var testCases = testCaseEnqueuer.command2testCases[command]; |
print(" Command: $command"); |
for (var testCase in testCases) { |
@@ -1591,9 +1572,7 @@ class ProcessQueue { |
} |
// When the graph building is finished, notify event listeners. |
- _graph.events |
- .where((event) => event is dgraph.GraphSealedEvent) |
- .listen((event) { |
+ _graph.sealed.listen((_) { |
eventAllTestsKnown(); |
}); |