Files
sdk/pkg/dev_compiler/test/multitest.dart
T
Vijay Menon 68d2121fd0 Use .packages instead of pubspec.yaml
This runs tests against sdk versions of packages instead of whatever
random version we have in pubspec.yaml.

Travis may be unhappy with the lack of a pubspec.yaml.  Here's the try:

https://travis-ci.org/dart-lang/sdk/builds/163270204

R=jmesserly@google.com, rnystrom@google.com

Review URL: https://codereview.chromium.org/2371113004 .
2016-09-28 09:49:31 -07:00

175 lines
5.9 KiB
Dart

// 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;
}
}