| // Copyright (c) 2016, 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.md file. | |
| library testing.multitest; | |
| import 'dart:async' show Stream, StreamTransformerBase; | |
| import 'dart:io' show Directory, File; | |
| import 'log.dart' show splitLines; | |
| import 'test_description.dart' show FileBasedTestDescription, TestDescription; | |
| bool isError(Set<String> expectations) { | |
| if (expectations.contains("compile-time error")) return true; | |
| if (expectations.contains("runtime error")) return true; | |
| return false; | |
| } | |
| bool isCheckedModeError(Set<String> expectations) { | |
| if (expectations.contains("checked mode compile-time error")) return true; | |
| if (expectations.contains("dynamic type error")) return true; | |
| return isError(expectations); | |
| } | |
| class MultitestTransformer | |
| extends StreamTransformerBase<TestDescription, TestDescription> { | |
| static RegExp multitestMarker = RegExp(r"//[#/]"); | |
| static final int _multitestMarkerLength = 3; | |
| static const List<String> validOutcomesList = <String>[ | |
| "ok", | |
| "syntax error", | |
| "compile-time error", | |
| "runtime error", | |
| "static type warning", | |
| "dynamic type error", | |
| "checked mode compile-time error", | |
| ]; | |
| static final Set<String> validOutcomes = Set<String>.from(validOutcomesList); | |
| @override | |
| Stream<TestDescription> bind(Stream<TestDescription> stream) async* { | |
| List<String> errors = <String>[]; | |
| reportError(String error) { | |
| errors.add(error); | |
| print(error); | |
| } | |
| nextTest: | |
| await for (TestDescription test in stream) { | |
| FileBasedTestDescription? multitest; | |
| String? contents; | |
| if (test is FileBasedTestDescription) { | |
| contents = await test.file.readAsString(); | |
| if (contents.contains(multitestMarker)) { | |
| multitest = test; | |
| } | |
| } | |
| if (multitest == null) { | |
| yield test; | |
| continue nextTest; | |
| } | |
| // Note: this is modified in the loop below. | |
| List<String> linesWithoutAnnotations = <String>[]; | |
| Map<String, List<String>> testsAsLines = <String, List<String>>{ | |
| "none": linesWithoutAnnotations, | |
| }; | |
| Map<String, Set<String>> outcomes = <String, Set<String>>{ | |
| "none": <String>{}, | |
| }; | |
| int lineNumber = 0; | |
| for (String line in splitLines(contents!)) { | |
| lineNumber++; | |
| int index = line.indexOf(multitestMarker); | |
| String? subtestName; | |
| List<String>? subtestOutcomesList; | |
| if (index != -1) { | |
| String annotationText = | |
| line.substring(index + _multitestMarkerLength).trim(); | |
| index = annotationText.indexOf(":"); | |
| if (index != -1) { | |
| subtestName = annotationText.substring(0, index).trim(); | |
| subtestOutcomesList = annotationText | |
| .substring(index + 1) | |
| .split(",") | |
| .map((s) => s.trim()) | |
| .toList(); | |
| if (subtestOutcomesList.isEmpty) { | |
| reportError(multitest | |
| .formatError("$lineNumber: Expected <testname>:<outcomes>")); | |
| continue nextTest; | |
| } | |
| } | |
| } | |
| if (subtestName != null) { | |
| List<String> lines = testsAsLines.putIfAbsent( | |
| subtestName, () => List<String>.from(linesWithoutAnnotations)); | |
| lines.add(line); | |
| Set<String> subtestOutcomes = | |
| outcomes.putIfAbsent(subtestName, () => <String>{}); | |
| if (subtestOutcomesList!.length != 1 || | |
| subtestOutcomesList.single != "continued") { | |
| for (String outcome in subtestOutcomesList) { | |
| if (validOutcomes.contains(outcome)) { | |
| subtestOutcomes.add(outcome); | |
| } else { | |
| reportError(multitest.formatError( | |
| "$lineNumber: '$outcome' isn't a recognized outcome.")); | |
| continue nextTest; | |
| } | |
| } | |
| } | |
| } else { | |
| for (List<String> lines in testsAsLines.values) { | |
| // This will also modify [linesWithoutAnnotations]. | |
| lines.add(line); | |
| } | |
| } | |
| } | |
| Uri root = Uri.base.resolve("generated/"); | |
| Directory generated = | |
| Directory.fromUri(root.resolve(multitest.shortName)); | |
| generated = await generated.create(recursive: true); | |
| for (MapEntry<String, List<String>> entry in testsAsLines.entries) { | |
| String name = entry.key; | |
| List<String> lines = entry.value; | |
| Uri uri = generated.uri.resolve("${name}_generated.dart"); | |
| FileBasedTestDescription subtest = | |
| FileBasedTestDescription(root, File.fromUri(uri)); | |
| subtest.multitestExpectations = outcomes[name]; | |
| await subtest.file.writeAsString(lines.join("")); | |
| yield subtest; | |
| } | |
| } | |
| if (errors.isNotEmpty) { | |
| throw "Error: ${errors.join("\n")}"; | |
| } | |
| } | |
| } |