| Index: test/multitest.dart
|
| diff --git a/test/multitest.dart b/test/multitest.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..beb7114e51a780d88db87f3ab566e9759f0f54f6
|
| --- /dev/null
|
| +++ b/test/multitest.dart
|
| @@ -0,0 +1,171 @@
|
| +// Copyright (c) 2012, 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 file.
|
| +
|
| +// TODO(jmesserly): this was factored out of
|
| +// dart-lang/sdk/tools/testing/dart/multitest.dart
|
| +library dev_compiler.test.tools.multitest;
|
| +
|
| +final validMultitestOutcomes = new Set<String>.from([
|
| + 'ok',
|
| + 'compile-time error',
|
| + 'runtime error',
|
| + 'static type warning',
|
| + 'dynamic type error',
|
| + 'checked mode compile-time error'
|
| +]);
|
| +
|
| +// Require at least one non-space character before '///'
|
| +final _multiTestRegExp = new RegExp(r"\S */// \w+:(.*)");
|
| +
|
| +bool isMultiTest(String contents) => _multiTestRegExp.hasMatch(contents);
|
| +
|
| +// Multitests are Dart test scripts containing lines of the form
|
| +// " [some dart code] /// [key]: [error type]"
|
| +//
|
| +// For each key in the file, a new test file is made containing all
|
| +// the normal lines of the file, and all of the multitest lines containing
|
| +// that key, in the same order as in the source file. The new test is expected
|
| +// to pass if the error type listed is 'ok', or to fail if there is an error
|
| +// type of type 'compile-time error', 'runtime error', 'static type warning', or
|
| +// 'dynamic type error'. The type error tests fail only in checked mode.
|
| +// There is also a test created from only the untagged lines of the file,
|
| +// with key "none", which is expected to pass. This library extracts these
|
| +// tests, writes them into a temporary directory, and passes them to the test
|
| +// runner. These tests may be referred to in the status files with the
|
| +// pattern [test name]/[key].
|
| +//
|
| +// For example: file I_am_a_multitest.dart
|
| +// aaa
|
| +// bbb /// 02: runtime error
|
| +// ccc /// 02: continued
|
| +// ddd /// 07: static type warning
|
| +// eee /// 10: ok
|
| +// fff
|
| +//
|
| +// should create four tests:
|
| +// I_am_a_multitest_none.dart
|
| +// aaa
|
| +// fff
|
| +//
|
| +// I_am_a_multitest_02.dart
|
| +// aaa
|
| +// bbb /// 02: runtime error
|
| +// ccc /// 02: continued
|
| +// fff
|
| +//
|
| +// I_am_a_multitest_07.dart
|
| +// aaa
|
| +// ddd /// 07: static type warning
|
| +// fff
|
| +//
|
| +// and I_am_a_multitest_10.dart
|
| +// aaa
|
| +// eee /// 10: ok
|
| +// fff
|
| +//
|
| +// Note that it is possible to indicate more than one acceptable outcome
|
| +// in the case of dynamic and static type warnings
|
| +// aaa
|
| +// ddd /// 07: static type warning, dynamic type error
|
| +// fff
|
| +
|
| +void extractTestsFromMultitest(String filePath, String contents,
|
| + Map<String, String> tests, Map<String, Set<String>> outcomes) {
|
| + int first_newline = contents.indexOf('\n');
|
| + final String line_separator = (first_newline == 0 ||
|
| + contents[first_newline - 1] != '\r') ? '\n' : '\r\n';
|
| + List<String> lines = contents.split(line_separator);
|
| + if (lines.last == '') lines.removeLast();
|
| + contents = null;
|
| +
|
| + // Create the set of multitests, which will have a new test added each
|
| + // time we see a multitest line with a new key.
|
| + Map<String, List<String>> testsAsLines = new Map<String, List<String>>();
|
| +
|
| + // Add the default case with key "none".
|
| + testsAsLines['none'] = new List<String>();
|
| + outcomes['none'] = new Set<String>();
|
| +
|
| + int lineCount = 0;
|
| + for (String line in lines) {
|
| + lineCount++;
|
| + var annotation = new _Annotation.from(line);
|
| + if (annotation != null) {
|
| + testsAsLines.putIfAbsent(
|
| + annotation.key, () => new List<String>.from(testsAsLines["none"]));
|
| + // Add line to test with annotation.key as key, empty line to the rest.
|
| + for (var key in testsAsLines.keys) {
|
| + testsAsLines[key].add(annotation.key == key ? line : "");
|
| + }
|
| + outcomes.putIfAbsent(annotation.key, () => new Set<String>());
|
| + if (annotation.rest != 'continued') {
|
| + for (String nextOutcome in annotation.outcomesList) {
|
| + if (validMultitestOutcomes.contains(nextOutcome)) {
|
| + outcomes[annotation.key].add(nextOutcome);
|
| + } else {
|
| + print("Warning: Invalid test directive '$nextOutcome' on line "
|
| + "${lineCount}:\n${annotation.rest} ");
|
| + }
|
| + }
|
| + }
|
| + } else {
|
| + for (var test in testsAsLines.values) test.add(line);
|
| + }
|
| + }
|
| + // End marker, has a final line separator so we don't need to add it after
|
| + // joining the lines.
|
| + var marker = '// Test created from multitest named $filePath.'
|
| + '$line_separator';
|
| + for (var test in testsAsLines.values) test.add(marker);
|
| +
|
| + var keysToDelete = [];
|
| + // Check that every key (other than the none case) has at least one outcome
|
| + for (var outcomeKey in outcomes.keys) {
|
| + if (outcomeKey != 'none' && outcomes[outcomeKey].isEmpty) {
|
| + print("Warning: Test ${outcomeKey} has no valid annotated outcomes.\n"
|
| + "Expected one of: ${validMultitestOutcomes.toString()}");
|
| + // If this multitest doesn't have an outcome, mark the multitest for
|
| + // deletion.
|
| + keysToDelete.add(outcomeKey);
|
| + }
|
| + }
|
| + // If a key/multitest was marked for deletion, do the necessary cleanup.
|
| + keysToDelete.forEach(outcomes.remove);
|
| + keysToDelete.forEach(testsAsLines.remove);
|
| +
|
| + // Copy all the tests into the output map tests, as multiline strings.
|
| + for (String key in testsAsLines.keys) {
|
| + tests[key] = testsAsLines[key].join(line_separator);
|
| + }
|
| +}
|
| +
|
| +// Represents a mutlitest annotation in the special /// comment.
|
| +class _Annotation {
|
| + String key;
|
| + String rest;
|
| + List<String> outcomesList;
|
| + _Annotation() {}
|
| + factory _Annotation.from(String line) {
|
| + // Do an early return with "null" if this is not a valid multitest
|
| + // annotation.
|
| + if (!line.contains('///')) {
|
| + return null;
|
| + }
|
| + var parts = line.split('///')[1]
|
| + .split(':')
|
| + .map((s) => s.trim())
|
| + .where((s) => s.length > 0)
|
| + .toList();
|
| + if (parts.length <= 1) {
|
| + return null;
|
| + }
|
| +
|
| + var annotation = new _Annotation();
|
| + annotation.key = parts[0];
|
| + annotation.rest = parts[1];
|
| + annotation.outcomesList =
|
| + annotation.rest.split(',').map((s) => s.trim()).toList();
|
| + return annotation;
|
| + }
|
| +}
|
|
|