Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(290)

Side by Side Diff: lib/src/runner.dart

Issue 1239613002: Factor out a Runner class from executable.dart. (Closed) Base URL: git@github.com:dart-lang/test@master
Patch Set: Code review changes Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/src/executable.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library test.runner;
6
7 import 'dart:async';
8 import 'dart:io';
9
10 import 'package:async/async.dart';
11
12 import 'backend/metadata.dart';
13 import 'runner/application_exception.dart';
14 import 'runner/configuration.dart';
15 import 'runner/engine.dart';
16 import 'runner/load_exception.dart';
17 import 'runner/load_suite.dart';
18 import 'runner/loader.dart';
19 import 'runner/reporter/compact.dart';
20 import 'runner/reporter/expanded.dart';
21 import 'util/async_thunk.dart';
22 import 'utils.dart';
23
24 /// A class that loads and runs tests based on a [Configuration].
25 ///
26 /// This maintains a [Loader] and an [Engine] and passes test suites from one to
27 /// the other, as well as printing out tests with a [CompactReporter] or an
28 /// [ExpandedReporter].
29 class Runner {
30 /// The configuration for the runner.
31 final Configuration _configuration;
32
33 /// The loader that loads the test suites from the filesystem.
34 final Loader _loader;
35
36 /// The engine that runs the test suites.
37 final Engine _engine;
38
39 /// The thunk for ensuring [close] only runs once.
40 final _closeThunk = new AsyncThunk();
41 bool get _closed => _closeThunk.hasRun;
42
43 /// Whether [run] has been called.
44 bool _hasRun = false;
45
46 /// Creates a new runner based on [configuration].
47 factory Runner(Configuration configuration) {
48 var metadata = new Metadata(
49 verboseTrace: configuration.verboseTrace);
50 var loader = new Loader(configuration.platforms,
51 pubServeUrl: configuration.pubServeUrl,
52 packageRoot: configuration.packageRoot,
53 color: configuration.color,
54 metadata: metadata,
55 jsTrace: configuration.jsTrace);
56
57 var engine = new Engine(concurrency: configuration.concurrency);
58
59 var watch = configuration.reporter == "compact"
60 ? CompactReporter.watch
61 : ExpandedReporter.watch;
62
63 watch(
64 engine,
65 color: configuration.color,
66 verboseTrace: configuration.verboseTrace,
67 printPath: configuration.paths.length > 1 ||
68 new Directory(configuration.paths.single).existsSync(),
69 printPlatform: configuration.platforms.length > 1);
70
71 return new Runner._(configuration, loader, engine);
72 }
73
74 Runner._(this._configuration, this._loader, this._engine);
75
76 /// Starts the runner.
77 ///
78 /// This starts running tests and printing their progress. It returns whether
79 /// or not they ran successfully.
80 Future<bool> run() async {
81 _hasRun = true;
82
83 if (_closed) {
84 throw new StateError("run() may not be called on a closed Runner.");
85 }
86
87 var success;
88 var results = await Future.wait([
89 _loadSuites(),
90 _engine.run()
91 ], eagerError: true);
92 success = results.last;
93
94 if (_closed) return false;
95
96 if (_engine.passed.length == 0 && _engine.failed.length == 0 &&
97 _engine.skipped.length == 0 && _configuration.pattern != null) {
98 var message = 'No tests match ';
99
100 if (_configuration.pattern is RegExp) {
101 var pattern = (_configuration.pattern as RegExp).pattern;
102 message += 'regular expression "$pattern".';
103 } else {
104 message += '"${_configuration.pattern}".';
105 }
106 throw new ApplicationException(message);
107 }
108
109 // Explicitly check "== true" here because [Engine.run] can return `null`
110 // if the engine was closed prematurely.
111 return success == true;
112 }
113
114 /// Closes the runner.
115 ///
116 /// This stops any future test suites from running. It will wait for any
117 /// currently-running VM tests, in case they have stuff to clean up on the
118 /// filesystem.
119 Future close() => _closeThunk.run(() async {
120 var timer;
121 if (_hasRun) {
122 // Wait a bit to print this message, since printing it eagerly looks weird
123 // if the tests then finish immediately.
124 timer = new Timer(new Duration(seconds: 1), () {
125 // Print a blank line first to ensure that this doesn't interfere with
126 // the compact reporter's unfinished line.
127 print('');
128 print("Waiting for current test(s) to finish.");
129 print("Press Control-C again to terminate immediately.");
130 });
131 }
132
133 // Make sure we close the engine *before* the loader. Otherwise,
134 // LoadSuites provided by the loader may get into bad states.
135 await _engine.close();
136 if (timer != null) timer.cancel();
137 await _loader.close();
138 });
139
140 /// Load the test suites in [_configuration.paths] that match
141 /// [_configuration.pattern].
142 Future _loadSuites() async {
143 var group = new FutureGroup();
144
145 mergeStreams(_configuration.paths.map((path) {
146 if (new Directory(path).existsSync()) return _loader.loadDir(path);
147 if (new File(path).existsSync()) return _loader.loadFile(path);
148
149 return new Stream.fromIterable([
150 new LoadSuite("loading $path", () =>
151 throw new LoadException(path, 'Does not exist.'))
152 ]);
153 })).listen((loadSuite) {
154 group.add(new Future.sync(() {
155 _engine.suiteSink.add(loadSuite.changeSuite((suite) {
156 if (_configuration.pattern == null) return suite;
157 return suite.change(tests: suite.tests.where(
158 (test) => test.name.contains(_configuration.pattern)));
159 }));
160 }));
161 }, onError: (error, stackTrace) {
162 group.add(new Future.error(error, stackTrace));
163 }, onDone: group.close);
164
165 await group.future;
166
167 // Once we've loaded all the suites, notify the engine that no more will be
168 // coming.
169 _engine.suiteSink.close();
170 }
171 }
OLDNEW
« no previous file with comments | « lib/src/executable.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698