| // Copyright (c) 2019, 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. |
| |
| /// Regenerates the static error test markers inside static error tests based |
| /// on the actual errors reported by analyzer and CFE. |
| import 'dart:io'; |
| |
| import 'package:args/args.dart'; |
| import 'package:glob/glob.dart'; |
| |
| import 'package:test_runner/src/command_output.dart'; |
| import 'package:test_runner/src/path.dart'; |
| import 'package:test_runner/src/test_file.dart'; |
| import 'package:test_runner/src/update_errors.dart'; |
| import 'package:test_runner/src/utils.dart'; |
| |
| const _usage = |
| "Usage: dart update_static_error_tests.dart [flags...] <path glob>"; |
| |
| Future<void> main(List<String> args) async { |
| var parser = ArgParser(); |
| |
| parser.addFlag("help", abbr: "h"); |
| |
| parser.addFlag("dry-run", |
| abbr: "n", |
| help: "Print result but do not overwrite any files.", |
| negatable: false); |
| |
| parser.addSeparator("Strip expectations out of the tests:"); |
| parser.addFlag("remove", |
| abbr: "r", |
| help: "Remove all existing error expectations.", |
| negatable: false); |
| parser.addFlag("remove-analyzer", |
| help: "Remove existing analyzer error expectations.", negatable: false); |
| parser.addFlag("remove-cfe", |
| help: "Remove existing CFE error expectations.", negatable: false); |
| |
| parser.addSeparator( |
| "Insert expectations in the tests based on current front end output:"); |
| parser.addFlag("insert", |
| abbr: "i", |
| help: "Insert analyzer and CFE error expectations.", |
| negatable: false); |
| parser.addFlag("insert-analyzer", |
| help: "Insert analyzer error expectations.", negatable: false); |
| parser.addFlag("insert-cfe", |
| help: "Insert CFE error expectations.", negatable: false); |
| |
| parser.addSeparator("Update combines remove and insert:"); |
| parser.addFlag("update", |
| abbr: "u", |
| help: "Replace analyzer and CFE error expectations.", |
| negatable: false); |
| parser.addFlag("update-analyzer", |
| help: "Replace analyzer error expectations.", negatable: false); |
| parser.addFlag("update-cfe", |
| help: "Replace CFE error expectations.", negatable: false); |
| |
| var results = parser.parse(args); |
| |
| if (results["help"] as bool) { |
| print("Regenerates the test markers inside static error tests."); |
| print(""); |
| print(_usage); |
| print(""); |
| print(parser.usage); |
| exit(0); |
| } |
| |
| var dryRun = results["dry-run"] as bool; |
| var removeAnalyzer = results["remove-analyzer"] as bool || |
| results["remove"] as bool || |
| results["update-analyzer"] as bool || |
| results["update"] as bool; |
| |
| var removeCfe = results["remove-cfe"] as bool || |
| results["remove"] as bool || |
| results["update-cfe"] as bool || |
| results["update"] as bool; |
| |
| var insertAnalyzer = results["insert-analyzer"] as bool || |
| results["insert"] as bool || |
| results["update-analyzer"] as bool || |
| results["update"] as bool; |
| |
| var insertCfe = results["insert-cfe"] as bool || |
| results["insert"] as bool || |
| results["update-cfe"] as bool || |
| results["update"] as bool; |
| |
| if (!removeAnalyzer && !removeCfe && !insertAnalyzer && !insertCfe) { |
| _usageError( |
| parser, "Must provide at least one flag for an operation to perform."); |
| } |
| |
| if (results.rest.length != 1) { |
| _usageError( |
| parser, "Must provide a file path or glob for which tests to update."); |
| } |
| |
| var result = results.rest.single; |
| // Allow tests to be specified without the extension for compatibility with |
| // the regular test runner syntax. |
| if (!result.endsWith(".dart")) { |
| result += ".dart"; |
| } |
| // Allow tests to be specified either relative to the "tests" directory |
| // or relative to the current directory. |
| var root = result.startsWith("tests") ? "." : "tests"; |
| var glob = Glob(result, recursive: true); |
| for (var entry in glob.listSync(root: root)) { |
| if (!entry.path.endsWith(".dart")) continue; |
| |
| if (entry is File) { |
| await _processFile(entry, |
| dryRun: dryRun, |
| removeAnalyzer: removeAnalyzer, |
| removeCfe: removeCfe, |
| insertAnalyzer: insertAnalyzer, |
| insertCfe: insertCfe); |
| } |
| } |
| } |
| |
| void _usageError(ArgParser parser, String message) { |
| stderr.writeln("Usage error: $message"); |
| stderr.writeln(); |
| stderr.writeln(_usage); |
| stderr.writeln(parser.usage); |
| exit(64); |
| } |
| |
| Future<void> _processFile(File file, |
| {bool dryRun, |
| bool removeAnalyzer, |
| bool removeCfe, |
| bool insertAnalyzer, |
| bool insertCfe}) async { |
| stdout.write("${file.path}..."); |
| var source = file.readAsStringSync(); |
| var testFile = TestFile.parse(Path("."), file.path, source); |
| |
| var errors = <StaticError>[]; |
| if (insertAnalyzer) { |
| stdout.write("\r${file.path} (Running analyzer...)"); |
| errors.addAll(await _runAnalyzer(file.path, testFile.sharedOptions)); |
| } |
| |
| if (insertCfe) { |
| // Clear the previous line. |
| stdout.write("\r${file.path} "); |
| stdout.write("\r${file.path} (Running CFE...)"); |
| errors.addAll(await _runCfe(file.path, testFile.sharedOptions)); |
| } |
| |
| errors = StaticError.simplify(errors); |
| |
| var result = updateErrorExpectations(source, errors, |
| removeAnalyzer: removeAnalyzer, removeCfe: removeCfe); |
| |
| stdout.writeln("\r${file.path} (Updated with ${errors.length} errors)"); |
| |
| if (dryRun) { |
| print(result); |
| } else { |
| await file.writeAsString(result); |
| } |
| } |
| |
| /// Invoke analyzer on [path] and gather all static errors it reports. |
| Future<List<StaticError>> _runAnalyzer( |
| String path, List<String> options) async { |
| // TODO(rnystrom): Running the analyzer command line each time is very slow. |
| // Either import the analyzer as a library, or at least invoke it in a batch |
| // mode. |
| var result = await Process.run("sdk/bin/dartanalyzer$shellScriptExtension", [ |
| ...options, |
| "--format=machine", |
| path, |
| ]); |
| var errors = <StaticError>[]; |
| AnalysisCommandOutput.parseErrors(result.stderr as String, errors); |
| return errors; |
| } |
| |
| /// Invoke CFE on [path] and gather all static errors it reports. |
| Future<List<StaticError>> _runCfe(String path, List<String> options) async { |
| // TODO(rnystrom): Running the CFE command line each time is slow and wastes |
| // time generating code, which we don't care about. Import it as a library or |
| // at least run it in batch mode. |
| var result = await Process.run("sdk/bin/dart", [ |
| "pkg/front_end/tool/_fasta/compile.dart", |
| ...options, |
| "--verify", |
| path, |
| ]); |
| |
| var errors = <StaticError>[]; |
| FastaCommandOutput.parseErrors(result.stdout as String, errors); |
| |
| // Running the above command generates a dill file next to the test, which we |
| // don't want, so delete it. |
| await File("$path.dill").delete(); |
| |
| return errors; |
| } |