| OLD | NEW |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE.md file. |
| 4 | 4 |
| 5 library testing.chain; | 5 library testing.chain; |
| 6 | 6 |
| 7 import 'dart:async' show | 7 import 'dart:async' show |
| 8 Future, | 8 Future, |
| 9 Stream; | 9 Stream; |
| 10 | 10 |
| 11 import 'dart:convert' show | 11 import 'dart:convert' show |
| 12 JSON, | 12 JSON, |
| 13 JsonEncoder; | 13 JsonEncoder; |
| 14 | 14 |
| 15 import 'dart:io' show | 15 import 'dart:io' show |
| 16 Directory, | 16 Directory, |
| 17 File, | 17 File, |
| 18 FileSystemEntity, | 18 FileSystemEntity, |
| 19 exitCode; | 19 exitCode; |
| 20 | 20 |
| 21 import 'suite.dart' show | 21 import 'suite.dart' show |
| 22 Suite; | 22 Suite; |
| 23 | 23 |
| 24 import '../testing.dart' show | 24 import '../testing.dart' show |
| 25 TestDescription; | 25 TestDescription; |
| 26 | 26 |
| 27 import 'test_dart/status_file_parser.dart' show | 27 import 'test_dart/status_file_parser.dart' show |
| 28 Expectation, | |
| 29 ReadTestExpectations, | 28 ReadTestExpectations, |
| 30 TestExpectations; | 29 TestExpectations; |
| 31 | 30 |
| 32 import 'zone_helper.dart' show | 31 import 'zone_helper.dart' show |
| 33 runGuarded; | 32 runGuarded; |
| 34 | 33 |
| 35 import 'error_handling.dart' show | 34 import 'error_handling.dart' show |
| 36 withErrorHandling; | 35 withErrorHandling; |
| 37 | 36 |
| 38 import 'log.dart' show | 37 import 'log.dart' show |
| 39 logMessage, | 38 logMessage, |
| 40 logStepComplete, | 39 logStepComplete, |
| 41 logStepStart, | 40 logStepStart, |
| 42 logSuiteComplete, | 41 logSuiteComplete, |
| 43 logTestComplete, | 42 logTestComplete, |
| 44 logUnexpectedResult, | 43 logUnexpectedResult, |
| 45 splitLines; | 44 splitLines; |
| 46 | 45 |
| 47 import 'multitest.dart' show | 46 import 'multitest.dart' show |
| 48 MultitestTransformer, | 47 MultitestTransformer, |
| 49 isError; | 48 isError; |
| 50 | 49 |
| 50 import 'expectation.dart' show |
| 51 Expectation, |
| 52 ExpectationSet; |
| 53 |
| 51 typedef Future<ChainContext> CreateContext( | 54 typedef Future<ChainContext> CreateContext( |
| 52 Chain suite, Map<String, String> environment); | 55 Chain suite, Map<String, String> environment); |
| 53 | 56 |
| 54 /// A test suite for tool chains, for example, a compiler. | 57 /// A test suite for tool chains, for example, a compiler. |
| 55 class Chain extends Suite { | 58 class Chain extends Suite { |
| 56 final Uri source; | 59 final Uri source; |
| 57 | 60 |
| 58 final Uri uri; | 61 final Uri uri; |
| 59 | 62 |
| 60 final List<RegExp> pattern; | 63 final List<RegExp> pattern; |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 112 "exclude": []..addAll(exclude.map((RegExp r) => r.pattern)), | 115 "exclude": []..addAll(exclude.map((RegExp r) => r.pattern)), |
| 113 }; | 116 }; |
| 114 } | 117 } |
| 115 } | 118 } |
| 116 | 119 |
| 117 abstract class ChainContext { | 120 abstract class ChainContext { |
| 118 const ChainContext(); | 121 const ChainContext(); |
| 119 | 122 |
| 120 List<Step> get steps; | 123 List<Step> get steps; |
| 121 | 124 |
| 125 ExpectationSet get expectationSet => ExpectationSet.Default; |
| 126 |
| 122 Future<Null> run(Chain suite, Set<String> selectors) async { | 127 Future<Null> run(Chain suite, Set<String> selectors) async { |
| 123 TestExpectations expectations = await ReadTestExpectations( | 128 TestExpectations expectations = await ReadTestExpectations( |
| 124 <String>[suite.statusFile.toFilePath()], {}); | 129 <String>[suite.statusFile.toFilePath()], {}, expectationSet); |
| 125 Stream<TestDescription> stream = list(suite); | 130 Stream<TestDescription> stream = list(suite); |
| 126 if (suite.processMultitests) { | 131 if (suite.processMultitests) { |
| 127 stream = stream.transform(new MultitestTransformer()); | 132 stream = stream.transform(new MultitestTransformer()); |
| 128 } | 133 } |
| 129 List<TestDescription> descriptions = await stream.toList(); | 134 List<TestDescription> descriptions = await stream.toList(); |
| 130 descriptions.sort(); | 135 descriptions.sort(); |
| 131 Map<TestDescription, Result> unexpectedResults = | 136 Map<TestDescription, Result> unexpectedResults = |
| 132 <TestDescription, Result>{}; | 137 <TestDescription, Result>{}; |
| 133 Map<TestDescription, Set<Expectation>> unexpectedOutcomes = | 138 Map<TestDescription, Set<Expectation>> unexpectedOutcomes = |
| 134 <TestDescription, Set<Expectation>>{}; | 139 <TestDescription, Set<Expectation>>{}; |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 182 } | 187 } |
| 183 }, printLineOnStdout: sb.writeln); | 188 }, printLineOnStdout: sb.writeln); |
| 184 } else { | 189 } else { |
| 185 future = new Future.value(null); | 190 future = new Future.value(null); |
| 186 } | 191 } |
| 187 future = future.then((Result currentResult) { | 192 future = future.then((Result currentResult) { |
| 188 if (currentResult != null) { | 193 if (currentResult != null) { |
| 189 logStepComplete(completed, unexpectedResults.length, | 194 logStepComplete(completed, unexpectedResults.length, |
| 190 descriptions.length, suite, description, lastStepRun); | 195 descriptions.length, suite, description, lastStepRun); |
| 191 result = currentResult; | 196 result = currentResult; |
| 192 if (currentResult.outcome == Expectation.PASS) { | 197 if (currentResult.outcome == Expectation.Pass) { |
| 193 // The input to the next step is the output of this step. | 198 // The input to the next step is the output of this step. |
| 194 return doStep(result.output); | 199 return doStep(result.output); |
| 195 } | 200 } |
| 196 } | 201 } |
| 197 if (description.multitestExpectations != null) { | 202 if (description.multitestExpectations != null) { |
| 198 if (isError(description.multitestExpectations)) { | 203 if (isError(description.multitestExpectations)) { |
| 199 result = toNegativeTestResult( | 204 result = toNegativeTestResult( |
| 200 result, description.multitestExpectations); | 205 result, description.multitestExpectations); |
| 201 } | 206 } |
| 202 } else if (lastStep == lastStepRun && | 207 } else if (lastStep == lastStepRun && |
| 203 description.shortName.endsWith("negative_test")) { | 208 description.shortName.endsWith("negative_test")) { |
| 204 if (result.outcome == Expectation.PASS) { | 209 if (result.outcome == Expectation.Pass) { |
| 205 result.addLog("Negative test didn't report an error.\n"); | 210 result.addLog("Negative test didn't report an error.\n"); |
| 206 } else if (result.outcome == Expectation.FAIL) { | 211 } else if (result.outcome == Expectation.Fail) { |
| 207 result.addLog("Negative test reported an error as expeceted.\n"); | 212 result.addLog("Negative test reported an error as expeceted.\n"); |
| 208 } | 213 } |
| 209 result = toNegativeTestResult(result); | 214 result = toNegativeTestResult(result); |
| 210 } | 215 } |
| 211 if (!expectedOutcomes.contains(result.outcome)) { | 216 if (!expectedOutcomes.contains(result.outcome)) { |
| 212 result.addLog("$sb"); | 217 result.addLog("$sb"); |
| 213 unexpectedResults[description] = result; | 218 unexpectedResults[description] = result; |
| 214 unexpectedOutcomes[description] = expectedOutcomes; | 219 unexpectedOutcomes[description] = expectedOutcomes; |
| 215 logUnexpectedResult(suite, description, result, expectedOutcomes); | 220 logUnexpectedResult(suite, description, result, expectedOutcomes); |
| 216 } else { | 221 } else { |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 254 String path = entity.uri.path; | 259 String path = entity.uri.path; |
| 255 if (suite.exclude.any((RegExp r) => path.contains(r))) continue; | 260 if (suite.exclude.any((RegExp r) => path.contains(r))) continue; |
| 256 if (suite.pattern.any((RegExp r) => path.contains(r))) { | 261 if (suite.pattern.any((RegExp r) => path.contains(r))) { |
| 257 yield new TestDescription(suite.uri, entity); | 262 yield new TestDescription(suite.uri, entity); |
| 258 } | 263 } |
| 259 } | 264 } |
| 260 } else { | 265 } else { |
| 261 throw "${suite.uri} isn't a directory"; | 266 throw "${suite.uri} isn't a directory"; |
| 262 } | 267 } |
| 263 } | 268 } |
| 269 |
| 270 Result toNegativeTestResult(Result result, [Set<String> expectations]) { |
| 271 Expectation outcome = result.outcome; |
| 272 if (outcome == Expectation.Pass) { |
| 273 if (expectations == null) { |
| 274 outcome = Expectation.Fail; |
| 275 } else if (expectations.contains("compile-time error")) { |
| 276 outcome = expectationSet["MissingCompileTimeError"]; |
| 277 } else if (expectations.contains("runtime error") || |
| 278 expectations.contains("dynamic type error")) { |
| 279 outcome = expectationSet["MissingRuntimeError"]; |
| 280 } else { |
| 281 outcome = Expectation.Fail; |
| 282 } |
| 283 } else if (outcome == Expectation.Fail) { |
| 284 outcome = Expectation.Pass; |
| 285 } |
| 286 return result.copyWithOutcome(outcome); |
| 287 } |
| 264 } | 288 } |
| 265 | 289 |
| 266 abstract class Step<I, O, C extends ChainContext> { | 290 abstract class Step<I, O, C extends ChainContext> { |
| 267 const Step(); | 291 const Step(); |
| 268 | 292 |
| 269 String get name; | 293 String get name; |
| 270 | 294 |
| 271 bool get isAsync => false; | 295 bool get isAsync => false; |
| 272 | 296 |
| 273 bool get isCompiler => false; | 297 bool get isCompiler => false; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 296 | 320 |
| 297 final error; | 321 final error; |
| 298 | 322 |
| 299 final StackTrace trace; | 323 final StackTrace trace; |
| 300 | 324 |
| 301 final List<String> logs = <String>[]; | 325 final List<String> logs = <String>[]; |
| 302 | 326 |
| 303 Result(this.output, this.outcome, this.error, this.trace); | 327 Result(this.output, this.outcome, this.error, this.trace); |
| 304 | 328 |
| 305 Result.pass(O output) | 329 Result.pass(O output) |
| 306 : this(output, Expectation.PASS, null, null); | 330 : this(output, Expectation.Pass, null, null); |
| 307 | 331 |
| 308 Result.crash(error, StackTrace trace) | 332 Result.crash(error, StackTrace trace) |
| 309 : this(null, Expectation.CRASH, error, trace); | 333 : this(null, Expectation.Crash, error, trace); |
| 310 | 334 |
| 311 Result.fail(O output, [error, StackTrace trace]) | 335 Result.fail(O output, [error, StackTrace trace]) |
| 312 : this(output, Expectation.FAIL, error, trace); | 336 : this(output, Expectation.Fail, error, trace); |
| 313 | 337 |
| 314 String get log => logs.join(); | 338 String get log => logs.join(); |
| 315 | 339 |
| 316 void addLog(String log) { | 340 void addLog(String log) { |
| 317 logs.add(log); | 341 logs.add(log); |
| 318 } | 342 } |
| 319 | 343 |
| 320 Result<O> copyWithOutcome(Expectation outcome) { | 344 Result<O> copyWithOutcome(Expectation outcome) { |
| 321 return new Result<O>(output, outcome, error, trace) | 345 return new Result<O>(output, outcome, error, trace) |
| 322 ..logs.addAll(logs); | 346 ..logs.addAll(logs); |
| 323 } | 347 } |
| 324 } | 348 } |
| 325 | 349 |
| 326 /// This is called from generated code. | 350 /// This is called from generated code. |
| 327 Future<Null> runChain( | 351 Future<Null> runChain( |
| 328 CreateContext f, Map<String, String> environment, Set<String> selectors, | 352 CreateContext f, Map<String, String> environment, Set<String> selectors, |
| 329 String json) { | 353 String json) { |
| 330 return withErrorHandling(() async { | 354 return withErrorHandling(() async { |
| 331 Chain suite = new Suite.fromJsonMap(Uri.base, JSON.decode(json)); | 355 Chain suite = new Suite.fromJsonMap(Uri.base, JSON.decode(json)); |
| 332 print("Running ${suite.name}"); | 356 print("Running ${suite.name}"); |
| 333 ChainContext context = await f(suite, environment); | 357 ChainContext context = await f(suite, environment); |
| 334 return context.run(suite, selectors); | 358 return context.run(suite, selectors); |
| 335 }); | 359 }); |
| 336 } | 360 } |
| 337 | |
| 338 Result toNegativeTestResult(Result result, [Set<String> expectations]) { | |
| 339 Expectation outcome = result.outcome; | |
| 340 if (outcome == Expectation.PASS) { | |
| 341 if (expectations == null) { | |
| 342 outcome = Expectation.FAIL; | |
| 343 } else if (expectations.contains("compile-time error")) { | |
| 344 outcome = Expectation.MISSING_COMPILETIME_ERROR; | |
| 345 } else if (expectations.contains("runtime error") || | |
| 346 expectations.contains("dynamic type error")) { | |
| 347 outcome = Expectation.MISSING_RUNTIME_ERROR; | |
| 348 } else { | |
| 349 outcome = Expectation.FAIL; | |
| 350 } | |
| 351 } else if (outcome == Expectation.FAIL) { | |
| 352 outcome = Expectation.PASS; | |
| 353 } | |
| 354 return result.copyWithOutcome(outcome); | |
| 355 } | |
| OLD | NEW |