| // Copyright (c) 2021, 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. |
| |
| import 'dart:async'; |
| import 'dart:io'; |
| |
| Future<int> main(List<String> args) async { |
| Options options; |
| try { |
| options = Options.read(args); |
| } on EmptyOptionsException { |
| return 0; |
| } on OptionsException catch (e) { |
| print(e.text); |
| return 1; |
| } |
| |
| Map<String, List<String>> map; |
| try { |
| map = findAndReadStatusFiles(options.sdk); |
| } on StatusFilesException catch (e) { |
| print(e.text); |
| return -1; |
| } |
| List<String> arguments = [ |
| 'log', |
| '--pretty=format:"%h"', |
| '--after=${options.after}', |
| if (options.before != null) '--before=${options.before}' |
| ]; |
| ProcessResult res = await Process.run('git', arguments); |
| if (res.exitCode != 0) { |
| print(res.stderr); |
| return 2; |
| } |
| int foundCount = 0; |
| List<String> commits = (res.stdout as String).split("\n"); |
| for (var c in commits) { |
| if (c.isEmpty) { |
| continue; |
| } |
| c = c.replaceAll('"', ''); |
| ProcessResult r = await Process.run( |
| 'git', ['show', '--pretty=format:""', '--name-only', c]); |
| if (r.exitCode != 0) { |
| print(r.stderr); |
| return 3; |
| } |
| List files = (r.stdout as String).split("\n"); |
| for (String f in files) { |
| if (f.isEmpty) { |
| continue; |
| } |
| if (f.endsWith(".dart")) { |
| String testName = f.substring(0, f.indexOf(".dart")); |
| var found = map[testName]; |
| if (found != null) { |
| print( |
| "Test $testName was changed in commit $c and presents in the following status files"); |
| print(found); |
| foundCount++; |
| } |
| } |
| } |
| } |
| print("$foundCount status files entries was found in ${commits.length} commits"); |
| return 0; |
| } |
| |
| class Options { |
| String? after = null; |
| String? before = null; |
| String sdk = '.'; |
| int days = 30; |
| |
| static Options read(List<String> args) { |
| Options options = new Options(); |
| if (args.length > 0) { |
| for (var arg in args) { |
| if (!_checkFormat(arg)) { |
| throw OptionsException("Unknown argument: '$arg'"); |
| } |
| List<String> pair = arg.substring(1).split("="); |
| switch (pair[0]) { |
| case "after": |
| options.after = pair[1]; |
| break; |
| case "before": |
| options.before = pair[1]; |
| break; |
| case "days": |
| try { |
| options.days = int.parse(pair[1]); |
| } on FormatException catch (_) { |
| throw new OptionsException( |
| "Unable to parse 'days' value: '${pair[0]}'"); |
| } |
| DateTime da = DateTime.now().add(Duration(days: -options.days)); |
| options.after = _formatDate(da); |
| break; |
| case "sdk": |
| options.sdk = pair[1]; |
| break; |
| default: |
| throw new OptionsException("Unknown argument: '${pair[0]}'"); |
| } |
| } |
| if (options.after == null) { |
| DateTime da = DateTime.now().add(Duration(days: -options.days)); |
| options.after = _formatDate(da); |
| } |
| return options; |
| } else { |
| print(''' |
| This tool checks if recent commits contain tests mentioned in .status files. Usage: |
| dart status_files_checker.dart <options> |
| Options: |
| -after=yyyy-MM-dd - by default current date minus ${options.days} days |
| -before=yyyy-MM-dd - current date by default |
| -days=N - check commits for N latest days (${options.days} by default) |
| -sdk=path_to_sdk - path to Dart SDK (by default it's the current directory) |
| '''); |
| throw EmptyOptionsException(); |
| } |
| } |
| |
| static bool _checkFormat(String arg) { |
| RegExp regExp = new RegExp("-.*=.*"); |
| return regExp.hasMatch(arg); |
| } |
| |
| static String _formatDate(DateTime date) { |
| return "${date.year}-${date.month < 10 ? "0" + date.month.toString() : date.month}-${date.day < 01 ? "0" + date.day.toString() : date.day}"; |
| } |
| } |
| |
| Map<String, List<String>> findAndReadStatusFiles(String sdk) { |
| Directory testsDir = new Directory(sdk + Platform.pathSeparator + "tests"); |
| if (!testsDir.existsSync()) { |
| throw StatusFilesException("Directory ${testsDir.path} doesn't exist"); |
| } |
| Map<String, List<String>> map = new Map<String, List<String>>(); |
| List<FileSystemEntity> list = testsDir.listSync(recursive: true); |
| for (FileSystemEntity fse in list) { |
| if (fse is File && fse.path.endsWith(".status")) { |
| addAll(map, readStatusFile(fse)); |
| } |
| } |
| return map; |
| } |
| |
| Map<String, List<String>> readStatusFile(File f) { |
| // get test suite prefix |
| String prefix = ""; |
| final test = Platform.pathSeparator + "tests" + Platform.pathSeparator; |
| final ind = f.path.indexOf(test); |
| final suite = f.path.substring(ind + test.length, |
| f.path.indexOf(Platform.pathSeparator, ind + test.length + 1)); |
| if (suite != "co19" && suite != "co19_2") { |
| prefix = "tests/" + suite + "/"; |
| } |
| // now read the file |
| final regExp = new RegExp(".*:(\\s)*(Skip|Crash|RuntimeError){1}(\\s|#)"); |
| List<String> strings = f.readAsLinesSync(); |
| Map<String, List<String>> map = new Map<String, List<String>>(); |
| for (String s in strings) { |
| RegExpMatch? match = regExp.firstMatch(s); |
| if (match != null) { |
| String testAndStatus = prefix + |
| s.substring(match.start, |
| match.end); // LibTest/ffi/Array/PointerArray_A01_t01: Skip |
| // Now process multitests like io/platform_resolved_executable_test/06: RuntimeError |
| testAndStatus = |
| testAndStatus.replaceFirst(new RegExp(r"\/([0-9]{2,3}|none):"), ":"); |
| List<String> parts = testAndStatus.split(":"); |
| map[parts[0]] = [f.path + parts[1]]; |
| } |
| } |
| return map; |
| } |
| |
| void addAll(Map<String, List<String>> map1, Map<String, List<String>> map2) { |
| for (String k in map2.keys) { |
| if (map1.containsKey(k)) { |
| map1[k]?.addAll(map2[k]!); |
| } else { |
| map1[k] = map2[k]!; |
| } |
| } |
| } |
| |
| class EmptyOptionsException implements Exception {} |
| |
| class OptionsException implements Exception { |
| final String text; |
| OptionsException(this.text); |
| } |
| |
| class StatusFilesException implements Exception { |
| final String text; |
| StatusFilesException(this.text); |
| } |