OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2016, 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.md file. |
| 4 |
| 5 library testing.run; |
| 6 |
| 7 import 'dart:async' show |
| 8 Future, |
| 9 Stream; |
| 10 |
| 11 import 'dart:convert' show |
| 12 JSON; |
| 13 |
| 14 import 'dart:io' show |
| 15 Platform; |
| 16 |
| 17 import 'dart:isolate' show |
| 18 Isolate, |
| 19 ReceivePort; |
| 20 |
| 21 import 'test_root.dart' show |
| 22 TestRoot; |
| 23 |
| 24 import 'test_description.dart' show |
| 25 TestDescription; |
| 26 |
| 27 import 'error_handling.dart' show |
| 28 withErrorHandling; |
| 29 |
| 30 import 'chain.dart' show |
| 31 CreateContext; |
| 32 |
| 33 import '../testing.dart' show |
| 34 Chain, |
| 35 ChainContext, |
| 36 TestDescription, |
| 37 listTests; |
| 38 |
| 39 import 'analyze.dart' show |
| 40 Analyze; |
| 41 |
| 42 import 'log.dart' show |
| 43 isVerbose, |
| 44 logMessage, |
| 45 logNumberedLines, |
| 46 splitLines; |
| 47 |
| 48 import 'suite.dart' show |
| 49 Dart, |
| 50 Suite; |
| 51 |
| 52 import 'zone_helper.dart' show |
| 53 acknowledgeControlMessages; |
| 54 |
| 55 Future<TestRoot> computeTestRoot(String configurationPath, Uri base) { |
| 56 Uri configuration = configurationPath == null |
| 57 ? Uri.base.resolve("testing.json") |
| 58 : base.resolve(configurationPath); |
| 59 return TestRoot.fromUri(configuration); |
| 60 } |
| 61 |
| 62 /// This is called from a Chain suite, and helps implement main. In most cases, |
| 63 /// main will look like this: |
| 64 /// |
| 65 /// main(List<String> arguments) => runMe(arguments, createContext); |
| 66 /// |
| 67 /// The optional argument [configurationPath] should be used when |
| 68 /// `testing.json` isn't located in the current working directory and is a path |
| 69 /// relative to `Platform.script`. |
| 70 Future<Null> runMe( |
| 71 List<String> arguments, CreateContext f, [String configurationPath]) { |
| 72 return withErrorHandling(() async { |
| 73 TestRoot testRoot = |
| 74 await computeTestRoot(configurationPath, Platform.script); |
| 75 for (Chain suite in testRoot.toolChains) { |
| 76 if (Platform.script == suite.source) { |
| 77 print("Running suite ${suite.name}..."); |
| 78 ChainContext context = await f(suite, <String, String>{}); |
| 79 await context.run(suite, new Set<String>()); |
| 80 } |
| 81 } |
| 82 }); |
| 83 } |
| 84 |
| 85 /// This is called from a `_test.dart` file, and helps integration in other |
| 86 /// test runner frameworks. |
| 87 /// |
| 88 /// For example, to run the suite `my_suite` from `test.dart`, create a file |
| 89 /// with this content: |
| 90 /// |
| 91 /// import 'package:async_helper/async_helper.dart' show asyncTest; |
| 92 /// |
| 93 /// import 'package:testing/testing.dart' show run; |
| 94 /// |
| 95 /// main(List<String> arguments) => asyncTest(run(arguments, ["my_suite"])); |
| 96 /// |
| 97 /// To run run the same suite from `package:test`, create a file with this |
| 98 /// content: |
| 99 /// |
| 100 /// import 'package:test/test.dart' show Timeout, test; |
| 101 /// |
| 102 /// import 'package:testing/testing.dart' show run; |
| 103 /// |
| 104 /// main() { |
| 105 /// test("my_suite", () => run([], ["my_suite"]), |
| 106 /// timeout: new Timeout(new Duration(minutes: 5))); |
| 107 /// } |
| 108 /// |
| 109 /// The optional argument [configurationPath] should be used when |
| 110 /// `testing.json` isn't located in the current working directory and is a path |
| 111 /// relative to `Uri.base`. |
| 112 Future<Null> run( |
| 113 List<String> arguments, List<String> suiteNames, |
| 114 [String configurationPath]) { |
| 115 return withErrorHandling(() async { |
| 116 TestRoot root = await computeTestRoot(configurationPath, Uri.base); |
| 117 List<Suite> suites = root.suites.where( |
| 118 (Suite suite) => suiteNames.contains(suite.name)).toList(); |
| 119 SuiteRunner runner = new SuiteRunner(suites, <String, String>{}, null); |
| 120 String program = await runner.generateDartProgram(); |
| 121 await runner.analyze(root.packages); |
| 122 if (program != null) { |
| 123 await runProgram(program, root.packages); |
| 124 } |
| 125 }); |
| 126 } |
| 127 |
| 128 Future<Null> runProgram(String program, Uri packages) async { |
| 129 logMessage("Running:"); |
| 130 logNumberedLines(program); |
| 131 Uri dataUri = new Uri.dataFromString(program); |
| 132 ReceivePort exitPort = new ReceivePort(); |
| 133 Isolate isolate = await Isolate.spawnUri(dataUri, <String>[], null, |
| 134 paused: true, onExit: exitPort.sendPort, errorsAreFatal: false, |
| 135 checked: true, packageConfig: packages); |
| 136 List error; |
| 137 var subscription = isolate.errors.listen((data) { |
| 138 error = data; |
| 139 exitPort.close(); |
| 140 }); |
| 141 await acknowledgeControlMessages(isolate, resume: isolate.pauseCapability); |
| 142 await for (var _ in exitPort) { |
| 143 exitPort.close(); |
| 144 } |
| 145 subscription.cancel(); |
| 146 return error == null |
| 147 ? null |
| 148 : new Future<Null>.error(error[0], new StackTrace.fromString(error[1])); |
| 149 } |
| 150 |
| 151 class SuiteRunner { |
| 152 final List<Suite> suites; |
| 153 |
| 154 final Map<String, String> environment; |
| 155 |
| 156 final List<String> selectors; |
| 157 |
| 158 List<Uri> testUris; |
| 159 |
| 160 SuiteRunner(this.suites, this.environment, Iterable<String> selectors) |
| 161 : selectors = selectors.toList(growable: false); |
| 162 |
| 163 Future<String> generateDartProgram() async { |
| 164 List<TestDescription> descriptions = await list().toList(); |
| 165 testUris = <Uri>[]; |
| 166 StringBuffer imports = new StringBuffer(); |
| 167 StringBuffer dart = new StringBuffer(); |
| 168 StringBuffer chain = new StringBuffer(); |
| 169 |
| 170 for (TestDescription description in descriptions) { |
| 171 testUris.add(await Isolate.resolvePackageUri(description.uri)); |
| 172 description.writeImportOn(imports); |
| 173 description.writeClosureOn(dart); |
| 174 } |
| 175 |
| 176 for (Chain suite in suites.where((Suite suite) => suite is Chain)) { |
| 177 testUris.add(await Isolate.resolvePackageUri(suite.source)); |
| 178 suite.writeImportOn(imports); |
| 179 suite.writeClosureOn(chain); |
| 180 } |
| 181 |
| 182 if (testUris.isEmpty) return null; |
| 183 |
| 184 return """ |
| 185 library testing.generated; |
| 186 |
| 187 import 'dart:async' show Future; |
| 188 |
| 189 import 'dart:convert' show JSON; |
| 190 |
| 191 import 'package:testing/src/run_tests.dart' show runTests; |
| 192 |
| 193 import 'package:testing/src/chain.dart' show runChain; |
| 194 |
| 195 import 'package:testing/src/log.dart' show enableVerboseOutput, isVerbose; |
| 196 |
| 197 ${imports.toString().trim()} |
| 198 |
| 199 Future<Null> main() async { |
| 200 if ($isVerbose) enableVerboseOutput(); |
| 201 Map<String, String> environment = JSON.decode('${JSON.encode(environment)}'); |
| 202 Set<String> selectors = JSON.decode('${JSON.encode(selectors)}').toSet(); |
| 203 await runTests(<String, Function> { |
| 204 ${splitLines(dart.toString().trim()).join(' ')} |
| 205 }); |
| 206 ${splitLines(chain.toString().trim()).join(' ')} |
| 207 } |
| 208 """; |
| 209 } |
| 210 |
| 211 Future<Null> analyze(Uri packages) async { |
| 212 for (Analyze suite in suites.where((Suite suite) => suite is Analyze)) { |
| 213 await suite.run(packages, testUris); |
| 214 } |
| 215 } |
| 216 |
| 217 Stream<TestDescription> list() async* { |
| 218 for (Dart suite in suites.where((Suite suite) => suite is Dart)) { |
| 219 await for (TestDescription description in |
| 220 listTests(<Uri>[suite.uri], pattern: "")) { |
| 221 String path = description.file.uri.path; |
| 222 if (suite.exclude.any((RegExp r) => path.contains(r))) continue; |
| 223 if (suite.pattern.any((RegExp r) => path.contains(r))) { |
| 224 yield description; |
| 225 } |
| 226 } |
| 227 } |
| 228 } |
| 229 } |
OLD | NEW |