dart-sdk/tools/status_clean.dart
Bob Nystrom f6ca2c1d8f Remove the Dart 1 tests.
This deletes:

tests/co19
tests/corelib
tests/html
tests/isolate
tests/language
tests/lib

It does not delete tests/standalone because apparently there are tests
in there that are not in standalone_2. (I assume they were added after
the test migration. I don't know why they were added there.)

I have tried to remove references to the old tests from various scripts
and tools but may have missed some. (As you can imagine, grepping for
"lib" does not have the best signal-to-noise ratio.)

"It was a pleasure to burn. It was a special pleasure to see things
eaten, to see things blackened and changed. With the brass nozzle in his
fists, with this great python spitting its venomous kerosene upon the
world, the blood pounded in his head, and his hands were the hands of
some amazing conductor playing all the symphonies of blazing and burning
to bring down the tatters and charcoal ruins of history."

- Ray Bradbury, Fahrenheit 451

Change-Id: If3db4a50e7a5ee25aff8058b1483e2ce8e68424e
Reviewed-on: https://dart-review.googlesource.com/c/75420
Commit-Queue: Bob Nystrom <rnystrom@google.com>
Auto-Submit: Bob Nystrom <rnystrom@google.com>
Reviewed-by: William Hesse <whesse@google.com>
Reviewed-by: Terry Lucas <terry@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
2018-10-11 23:45:18 +00:00

420 lines
14 KiB
Dart

// Copyright (c) 2014, 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.
library status_clean;
import "dart:async";
import "dart:convert" show json, utf8;
import "dart:io";
import "testing/dart/multitest.dart";
import "testing/dart/status_file_parser.dart";
import "testing/dart/test_suite.dart"
show
multiHtmlTestGroupRegExp,
multiTestRegExp,
multiHtmlTestRegExp,
TestUtils;
import "testing/dart/utils.dart" show Path;
// [STATUS_TUPLES] is a list of (suite-name, directory, status-file)-tuples.
final STATUS_TUPLES = [
["corelib_2", "tests/corelib_2", "tests/corelib_2/corelib_2.status"],
["standalone", "tests/standalone", "tests/standalone/standalone.status"],
["pkg", "pkg", "pkg/pkg.status"],
["utils", "tests/utils", "tests/utils/utils.status"],
["samples", "samples", "samples/samples.status"],
["analyze_library", "sdk", "tests/lib_2/analyzer/analyze_library.status"],
[
"dart2js_extra",
"tests/compiler/dart2js_extra",
"tests/compiler/dart2js_extra/dart2js_extra.status"
],
[
"dart2js_native",
"tests/compiler/dart2js_native",
"tests/compiler/dart2js_native/dart2js_native.status"
],
[
"dart2js",
"tests/compiler/dart2js",
"tests/compiler/dart2js/dart2js.status"
],
[
"benchmark_smoke",
"tests/benchmark_smoke",
"tests/benchmark_smoke/benchmark_smoke.status"
],
];
void main(List<String> args) {
TestUtils.setDartDirUri(Platform.script.resolve('..'));
usage() {
print("Usage: ${Platform.executable} <deflake|remove-nonexistent-tests>");
exit(1);
}
if (args.length == 0) usage();
if (args[0] == 'deflake') {
run(new StatusFileDeflaker());
} else if (args[0] == 'remove-nonexistent-tests') {
run(new StatusFileNonExistentTestRemover());
} else {
usage();
}
}
run(StatusFileProcessor processor) {
Future.forEach(STATUS_TUPLES, (List tuple) {
String suiteName = tuple[0];
String directory = tuple[1];
String filePath = tuple[2];
print("Processing $filePath");
return processor.run(suiteName, directory, filePath);
});
}
abstract class StatusFileProcessor {
Future run(String suiteName, String directory, String filePath);
Future<List<Section>> _readSections(String filePath) {
File file = new File(filePath);
if (file.existsSync()) {
var completer = new Completer();
List<Section> sections = new List<Section>();
ReadConfigurationInto(new Path(file.path), sections, () {
completer.complete(sections);
});
return completer.future;
}
return new Future.value([]);
}
}
class StatusFileNonExistentTestRemover extends StatusFileProcessor {
final MultiTestDetector multiTestDetector = new MultiTestDetector();
final TestFileLister testFileLister = new TestFileLister();
Future run(String suiteName, String directory, String filePath) {
return _readSections(filePath).then((List<Section> sections) {
Set<int> invalidLines = _analyzeStatusFile(directory, filePath, sections);
if (invalidLines.length > 0) {
return _writeFixedStatusFile(filePath, invalidLines);
}
return new Future.value();
});
}
bool _testExists(String filePath, List<String> testFiles, String directory,
TestRule rule) {
// TODO: Unify this regular expression matching with status_file_parser.dart
List<RegExp> getRuleRegex(String name) {
return name
.split("/")
.map((name) => new RegExp(name.replaceAll('*', '.*')))
.toList();
}
bool matchRegexp(List<RegExp> patterns, String str) {
var parts = str.split("/");
if (patterns.length > parts.length) {
return false;
}
// NOTE: patterns.length <= parts.length
for (var i = 0; i < patterns.length; i++) {
if (!patterns[i].hasMatch(parts[i])) {
return false;
}
}
return true;
}
var rulePattern = getRuleRegex(rule.name);
return testFiles.any((String file) {
// TODO: Use test_suite.dart's [buildTestCaseDisplayName] instead.
var filePath = new Path(file).relativeTo(new Path(directory));
String baseTestName = _concat(
"${filePath.directoryPath}", "${filePath.filenameWithoutExtension}");
List<String> testNames = [];
for (var name in multiTestDetector.getMultitestNames(file)) {
testNames.add(_concat(baseTestName, name));
}
// If it is not a multitest the testname is [baseTestName]
if (testNames.isEmpty) {
testNames.add(baseTestName);
}
return testNames
.any((String testName) => matchRegexp(rulePattern, testName));
});
}
Set<int> _analyzeStatusFile(
String directory, String filePath, List<Section> sections) {
var invalidLines = new Set<int>();
var dartFiles = testFileLister.listTestFiles(directory);
for (var section in sections) {
for (var rule in section.testRules) {
if (!_testExists(filePath, dartFiles, directory, rule)) {
print("Invalid rule: ${rule.name} in file "
"$filePath:${rule.lineNumber}");
invalidLines.add(rule.lineNumber);
}
}
}
return invalidLines;
}
_writeFixedStatusFile(String statusFilePath, Set<int> invalidLines) {
var lines = new File(statusFilePath).readAsLinesSync();
var outputLines = <String>[];
for (int i = 0; i < lines.length; i++) {
// The status file parser numbers lines starting with 1, not 0.
if (!invalidLines.contains(i + 1)) {
outputLines.add(lines[i]);
}
}
var outputFile = new File("$statusFilePath.fixed");
outputFile.writeAsStringSync(outputLines.join("\n"));
}
String _concat(String base, String part) {
if (base == "") return part;
if (part == "") return base;
return "$base/$part";
}
}
class StatusFileDeflaker extends StatusFileProcessor {
TestOutcomeFetcher _testOutcomeFetcher = new TestOutcomeFetcher();
Future run(String suiteName, String directory, String filePath) {
return _readSections(filePath).then((List<Section> sections) {
return _generatedDeflakedLines(suiteName, sections)
.then((Map<int, String> fixedLines) {
if (fixedLines.length > 0) {
return _writeFixedStatusFile(filePath, fixedLines);
}
});
});
}
Future _generatedDeflakedLines(String suiteName, List<Section> sections) {
var fixedLines = new Map<int, String>();
return Future.forEach(sections, (Section section) {
return Future.forEach(section.testRules, (rule) {
return _maybeFixStatusfileLine(suiteName, section, rule, fixedLines);
});
}).then((_) => fixedLines);
}
Future _maybeFixStatusfileLine(String suiteName, Section section,
TestRule rule, Map<int, String> fixedLines) {
print("Processing ${section.statusFile.location}: ${rule.lineNumber}");
// None of our status file lines have expressions, so we pass {} here.
var notedOutcomes = rule.expression
.evaluate({})
.map((name) => Expectation.byName(name))
.where((Expectation expectation) => !expectation.isMetaExpectation)
.toSet();
if (notedOutcomes.isEmpty) return new Future.value();
// TODO: [rule.name] is actually a pattern not just a testname. We should
// find all possible testnames this rule matches against and unify the
// outcomes of these tests.
return _testOutcomeFetcher
.outcomesOf(suiteName, section, rule.name)
.then((Set<Expectation> actualOutcomes) {
var outcomesThatNeverHappened = new Set<Expectation>();
for (Expectation notedOutcome in notedOutcomes) {
bool found = false;
for (Expectation actualOutcome in actualOutcomes) {
if (actualOutcome.canBeOutcomeOf(notedOutcome)) {
found = true;
break;
}
}
if (!found) {
outcomesThatNeverHappened.add(notedOutcome);
}
}
if (outcomesThatNeverHappened.length > 0 && actualOutcomes.length > 0) {
// Print the change to stdout.
print("${rule.name} "
"(${section.statusFile.location}:${rule.lineNumber}):");
print(" Actual outcomes: ${actualOutcomes.toList()}");
print(" Outcomes in status file: ${notedOutcomes.toList()}");
print(" Outcomes in status file that never happened : "
"${outcomesThatNeverHappened.toList()}\n");
// Build the fixed status file line.
fixedLines[rule.lineNumber] =
'${rule.name}: ${actualOutcomes.join(', ')} '
'# before: ${notedOutcomes.join(', ')} / '
'never happened: ${outcomesThatNeverHappened.join(', ')}';
}
});
}
_writeFixedStatusFile(String filePath, Map<int, String> fixedLines) {
var lines = new File(filePath).readAsLinesSync();
var outputLines = <String>[];
for (int i = 0; i < lines.length; i++) {
if (fixedLines.containsKey(i + 1)) {
outputLines.add(fixedLines[i + 1]);
} else {
outputLines.add(lines[i]);
}
}
var output = outputLines.join("\n");
var outputFile = new File("$filePath.deflaked");
outputFile.writeAsStringSync(output);
}
}
class MultiTestDetector {
final multiTestsCache = new Map<String, List<String>>();
final multiHtmlTestsCache = new Map<String, List<String>>();
List<String> getMultitestNames(String file) {
List<String> names = [];
names.addAll(getStandardMultitestNames(file));
names.addAll(getHtmlMultitestNames(file));
return names;
}
List<String> getStandardMultitestNames(String file) {
return multiTestsCache.putIfAbsent(file, () {
try {
var tests = new Map<String, String>();
var outcomes = new Map<String, Set<String>>();
if (multiTestRegExp.hasMatch(new File(file).readAsStringSync())) {
extractTestsFromMultitest(new Path(file), tests, outcomes);
}
return tests.keys.toList();
} catch (error) {
print("WARNING: Couldn't determine multitests in file ${file}: $error");
return [];
}
});
}
List<String> getHtmlMultitestNames(String file) {
return multiHtmlTestsCache.putIfAbsent(file, () {
try {
List<String> subtestNames = [];
var content = new File(file).readAsStringSync();
if (multiHtmlTestRegExp.hasMatch(content)) {
var matchesIter =
multiHtmlTestGroupRegExp.allMatches(content).iterator;
while (matchesIter.moveNext()) {
String fullMatch = matchesIter.current.group(0);
subtestNames.add(fullMatch.substring(fullMatch.indexOf("'") + 1));
}
}
return subtestNames;
} catch (error) {
print("WARNING: Couldn't determine multitests in file ${file}: $error");
}
return [];
});
}
}
class TestFileLister {
final Map<String, List<String>> _filesCache = {};
List<String> listTestFiles(String directory) {
return _filesCache.putIfAbsent(directory, () {
var dir = new Directory(directory);
// Cannot test for _test.dart because co19 tests don't have that ending.
var dartFiles = dir
.listSync(recursive: true)
.where((fe) => fe is File)
.where((file) =>
file.path.endsWith(".dart") || file.path.endsWith("_test.html"))
.map((file) => file.path)
.toList();
return dartFiles;
});
}
}
/*
* [TestOutcomeFetcher] will fetch test results from a server using a REST-like
* interface.
*/
class TestOutcomeFetcher {
static String SERVER = '108.170.219.8';
static int PORT = 4540;
HttpClient _client = new HttpClient();
Future<Set<Expectation>> outcomesOf(
String suiteName, Section section, String testName) {
var pathComponents = [
'json',
'test-outcomes',
'outcomes',
Uri.encodeComponent("$suiteName/$testName")
];
var path = pathComponents.join('/') + '/';
var url = new Uri(scheme: 'http', host: SERVER, port: PORT, path: path);
return _client
.getUrl(url)
.then((HttpClientRequest request) => request.close())
.then((HttpClientResponse response) {
return response
.transform(utf8.decoder)
.transform(json.decoder)
.first
.then((List testResults) {
var setOfActualOutcomes = new Set<Expectation>();
try {
for (var result in testResults) {
var config = result['configuration'];
var testResult = result['test_result'];
var outcome = testResult['outcome'];
// These variables are derived variables and will be set in
// tools/testing/dart/test_options.dart.
// [Mostly due to the fact that we don't have an unary !
// operator in status file expressions.]
config['unchecked'] = !config['checked'];
config['unminified'] = !config['minified'];
config['nocsp'] = !config['csp'];
config['browser'] = TestUtils.isBrowserRuntime(config['runtime']);
config['analyzer'] =
TestUtils.isCommandLineAnalyzer(config['compiler']);
config['jscl'] =
TestUtils.isJsCommandLineRuntime(config['runtime']);
if (section.condition == null ||
section.condition.evaluate(config)) {
setOfActualOutcomes.add(Expectation.byName(outcome));
}
}
return setOfActualOutcomes;
} catch (error) {
print("Warning: Error occured while processing testoutcomes"
": $error");
return [];
}
}).catchError((error) {
print("Warning: Error occured while fetching testoutcomes: $error");
return [];
});
});
}
}