blob: 2c0bcdfbe8422156c8b22bf61be617c57ef1bf73 [file] [log] [blame]
// Copyright (c) 2017, 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.
/// Collects the configurations for all status files in the 'tests' folder that
/// mention one of the test names given as argument.
import 'dart:async';
import 'dart:math' hide log;
import 'dart:io';
import 'package:args/args.dart';
import 'package:gardening/src/util.dart';
void help(ArgParser argParser) {
print('Prints all status-file entries for the given tests.');
print('The test-names must be a substring (or full match) of the lines in ');
print('the status file. They can not be fully qualified');
print('Usage: status_summary [options] <test-name1> [<test-name2> ...]');
print('where options are:');
print(argParser.usage);
}
Future main(List<String> args) async {
ArgParser argParser = createArgParser();
ArgResults argResults = argParser.parse(args);
processArgResults(argResults);
if (argResults.rest.length == 0 || argResults['help']) {
help(argParser);
if (argResults['help']) return;
exit(1);
}
int maxStatusWidth = 0;
int maxConfigWidth = 0;
Directory testDirectory = findTestDirectory('tests');
List<Uri> statusFiles = await findStatusFiles(testDirectory);
Directory pkgDirectory = findTestDirectory('pkg');
statusFiles.addAll(await findStatusFiles(pkgDirectory));
Directory runtimeDirectory = findTestDirectory('runtime');
statusFiles.addAll(await findStatusFiles(runtimeDirectory));
Map<String, List<StatusFile>> statusMap = <String, List<StatusFile>>{};
for (Uri uri in statusFiles) {
Map<String, StatusFile> currentMap = <String, StatusFile>{};
log('Scanning $uri');
String currentConfig = '';
for (String line in new File.fromUri(uri).readAsLinesSync()) {
if (line.startsWith('[')) {
currentConfig = line;
} else {
int colonIndex = line.indexOf(':');
if (colonIndex != -1) {
String testName = line.substring(0, colonIndex).trim();
int hashIndex = line.indexOf('#', colonIndex + 1);
String status;
String comment;
if (hashIndex != -1) {
status = line.substring(colonIndex + 1, hashIndex).trim();
comment = line.substring(hashIndex + 1).trim();
} else {
status = line.substring(colonIndex + 1).trim();
comment = '';
}
for (String arg in argResults.rest) {
if (testName.contains(arg) || arg.contains(testName)) {
StatusFile statusFile =
currentMap.putIfAbsent(testName, () => new StatusFile(uri));
statusFile.entries
.add(new StatusEntry(currentConfig, status, comment));
maxStatusWidth = max(maxStatusWidth, status.length);
maxConfigWidth = max(maxConfigWidth, currentConfig.length);
}
}
}
}
}
currentMap.forEach((String testName, StatusFile configFile) {
statusMap.putIfAbsent(testName, () => <StatusFile>[]).add(configFile);
});
}
statusMap.forEach((String arg, List<StatusFile> statusFiles) {
print('$arg');
for (StatusFile statusFile in statusFiles) {
print(' ${statusFile.uri}');
statusFile.entries.forEach((StatusEntry entry) {
print(' ${padRight(entry.status, maxStatusWidth)}'
' ${padRight(entry.config, maxConfigWidth)} ${entry.comment}');
});
}
});
}
/// Finds the test directory.
///
/// First looks at a test-directory that is relative to the current
Directory findTestDirectory(String directoryName) {
var directory = new Directory(directoryName);
if (directory.existsSync()) return directory;
return new Directory.fromUri(
Platform.script.resolve("../../../$directoryName"));
}
/// Returns the [Uri]s for all `.status` files in [path] and subdirectories.
Future<List<Uri>> findStatusFiles(Directory testDirectory) async {
List<Uri> statusFiles = <Uri>[];
await for (FileSystemEntity entity in testDirectory.list(recursive: true)) {
if (entity.path.endsWith('.status')) {
statusFiles.add(entity.uri);
}
}
return statusFiles;
}
/// The entries collected for a single status file.
class StatusFile {
final Uri uri;
final List<StatusEntry> entries = <StatusEntry>[];
StatusFile(this.uri);
}
/// A single entry in a status file.
class StatusEntry {
/// The preceding config line, if any. I.e. the `[...]` line that contained
/// this entry, or the empty string otherwise.
final String config;
/// The status of the entry, e.g. `Pass, Slow`.
final String status;
/// The comment after the status, if any.
final String comment;
StatusEntry(this.config, this.status, this.comment);
String toString() => '$status $config $comment';
}