diff --git a/pkg/analyzer_testing/api.txt b/pkg/analyzer_testing/api.txt index 986b25be1cc..31c0b77dff7 100644 --- a/pkg/analyzer_testing/api.txt +++ b/pkg/analyzer_testing/api.txt @@ -5,8 +5,10 @@ package:analyzer_testing/analysis_rule/analysis_rule.dart: analysisRule (getter: String) assertNoPubspecDiagnostics (method: Future Function(String)) assertPubspecDiagnostics (method: Future Function(String, List)) + correctionMessage (method: String Function(List)) lint (method: ExpectedDiagnostic Function(int, int, {Pattern? correctionContains, Pattern? messageContains, String? name})) setUp (method: void Function()) + unexpectedMessage (method: String Function(List)) package:analyzer_testing/experiments/experiments.dart: experimentsForTests (static getter: List) experimentsForTests= (static setter: List) @@ -78,6 +80,8 @@ dart:core: int (referenced) package:_fe_analyzer_shared/src/base/errors.dart: DiagnosticCode (referenced) +package:analyzer/diagnostic/diagnostic.dart: + Diagnostic (referenced) package:analyzer/file_system/file_system.dart: File (referenced) Folder (referenced) diff --git a/pkg/analyzer_testing/lib/analysis_rule/analysis_rule.dart b/pkg/analyzer_testing/lib/analysis_rule/analysis_rule.dart index ca278f42960..3626b438342 100644 --- a/pkg/analyzer_testing/lib/analysis_rule/analysis_rule.dart +++ b/pkg/analyzer_testing/lib/analysis_rule/analysis_rule.dart @@ -2,6 +2,8 @@ // 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:convert' show json; + import 'package:analyzer/analysis_rule/pubspec.dart'; import 'package:analyzer/diagnostic/diagnostic.dart'; import 'package:analyzer/error/error.dart'; @@ -46,6 +48,28 @@ abstract class AnalysisRuleTest extends PubPackageResolutionTest { assertDiagnosticsIn(errors, expectedDiagnostics); } + @override + String correctionMessage(List diagnostics) { + var buffer = StringBuffer(); + diagnostics.sort((first, second) => first.offset.compareTo(second.offset)); + buffer.writeln(); + buffer.writeln('To accept the current state, expect:'); + for (var actual in diagnostics) { + if (actual.diagnosticCode is LintCode) { + buffer.write(' lint('); + } else { + buffer.write(' error(${actual.diagnosticCode}, '); + } + buffer.write('${actual.offset}, ${actual.length}'); + if (actual.diagnosticCode.name != analysisRule) { + buffer.write(", name: '${actual.diagnosticCode.name}'"); + } + buffer.writeln('),'); + } + + return buffer.toString(); + } + /// Returns an "expected diagnostic" for [analysisRule] (or [name], if given) /// at [offset] and [length]. /// @@ -79,6 +103,25 @@ abstract class AnalysisRuleTest extends PubPackageResolutionTest { ); } + @override + String unexpectedMessage(List unmatchedActual) { + var buffer = StringBuffer(); + if (buffer.isNotEmpty) { + buffer.writeln(); + } + buffer.writeln('Found but did not expect:'); + for (var actual in unmatchedActual) { + buffer.write(' $analysisRule.${actual.diagnosticCode.name} ['); + buffer.write('${actual.offset}, ${actual.length}, ${actual.message}'); + if (actual.correctionMessage case Pattern correctionMessage) { + buffer.write(', '); + buffer.write(json.encode(correctionMessage)); + } + buffer.writeln(']'); + } + return buffer.toString(); + } + Future> _analyzePubspecFile(String content) async { var path = convertPath(testPackagePubspecPath); var pubspecRules = >{}; diff --git a/pkg/analyzer_testing/lib/src/analysis_rule/pub_package_resolution.dart b/pkg/analyzer_testing/lib/src/analysis_rule/pub_package_resolution.dart index 6c60548fd8d..4e07710b53b 100644 --- a/pkg/analyzer_testing/lib/src/analysis_rule/pub_package_resolution.dart +++ b/pkg/analyzer_testing/lib/src/analysis_rule/pub_package_resolution.dart @@ -212,9 +212,7 @@ class PubPackageResolutionTest with MockPackagesMixin, ResourceProviderMixin { List diagnostics, List expectedDiagnostics, ) { - // // Match actual diagnostics to expected diagnostics. - // var unmatchedActual = diagnostics.toList(); var unmatchedExpected = expectedDiagnostics.toList(); var actualIndex = 0; @@ -236,99 +234,33 @@ class PubPackageResolutionTest with MockPackagesMixin, ResourceProviderMixin { actualIndex++; } } - // - // Write the results. - // + + // Print the results to the terminal. var buffer = StringBuffer(); if (unmatchedExpected.isNotEmpty) { - buffer.writeln('Expected but did not find:'); - for (var expected in unmatchedExpected) { - buffer.write(' '); - if (expected is ExpectedError) { - buffer.write(expected._code); - } - if (expected is ExpectedLint) { - buffer.write(expected._lintName); - } - buffer.write(' ['); - buffer.write(expected._offset); - buffer.write(', '); - buffer.write(expected._length); - if (expected._messageContains != null) { - buffer.write(', messageContains: '); - buffer.write(json.encode(expected._messageContains.toString())); - } - if (expected._correctionContains != null) { - buffer.write(', correctionContains: '); - buffer.write(json.encode(expected._correctionContains.toString())); - } - buffer.writeln(']'); - } + buffer.write(missingExpectedMessage(unmatchedExpected)); } if (unmatchedActual.isNotEmpty) { - if (buffer.isNotEmpty) { - buffer.writeln(); - } - buffer.writeln('Found but did not expect:'); - for (var actual in unmatchedActual) { - buffer.write(' '); - buffer.write(actual.diagnosticCode); - buffer.write(' ['); - buffer.write(actual.offset); - buffer.write(', '); - buffer.write(actual.length); - buffer.write(', '); - buffer.write(actual.message); - if (actual.correctionMessage != null) { - buffer.write(', '); - buffer.write(json.encode(actual.correctionMessage)); - } - buffer.writeln(']'); - } + buffer.write(unexpectedMessage(unmatchedActual)); } - if (buffer.isNotEmpty) { - diagnostics.sort( - (first, second) => first.offset.compareTo(second.offset), - ); - buffer.writeln(); - buffer.writeln('To accept the current state, expect:'); - for (var actual in diagnostics) { - late String diagnosticKind; - Object? description; - if (actual.diagnosticCode is LintCode) { - diagnosticKind = 'lint'; - } else { - diagnosticKind = 'error'; - description = actual.diagnosticCode; - } - buffer.write(' $diagnosticKind('); - if (description != null) { - buffer.write(description); - buffer.write(', '); - } - buffer.write(actual.offset); - buffer.write(', '); - buffer.write(actual.length); - buffer.writeln('),'); - } + if (unmatchedExpected.isNotEmpty || unmatchedActual.isNotEmpty) { + buffer.write(correctionMessage(diagnostics)); if (dumpAstOnFailures) { buffer.writeln(); buffer.writeln(); - try { - var astSink = StringBuffer(); + try { Spelunker( result.unit.toSource(), - sink: astSink, + sink: buffer, featureSet: result.unit.featureSet, ).spelunk(); - buffer.write(astSink); - buffer.writeln(); - // I hereby choose to catch this type. } on ArgumentError catch (_) { // Perhaps we encountered a parsing error while spelunking. } + + buffer.writeln(); } fail(buffer.toString()); @@ -371,6 +303,54 @@ class PubPackageResolutionTest with MockPackagesMixin, ResourceProviderMixin { Future assertNoDiagnosticsInFile(String path) async => assertDiagnosticsInFile(path, const []); + /// Text to display upon failure, which indicates possible corrections. + @visibleForOverriding + String correctionMessage(List diagnostics) { + var buffer = StringBuffer(); + diagnostics.sort((first, second) => first.offset.compareTo(second.offset)); + buffer.writeln(); + buffer.writeln('To accept the current state, expect:'); + for (var actual in diagnostics) { + if (actual.diagnosticCode is LintCode) { + buffer.write(' lint('); + } else { + buffer.write(' error(${actual.diagnosticCode}, '); + } + buffer.write('${actual.offset}, ${actual.length}),'); + } + + return buffer.toString(); + } + + /// Text to display upon failure, indicating that [unmatchedExpected] + /// diagnostics were expected, but not found. + @visibleForOverriding + String missingExpectedMessage(List unmatchedExpected) { + var buffer = StringBuffer(); + buffer.writeln('Expected but did not find:'); + for (var expected in unmatchedExpected) { + buffer.write(' '); + if (expected is ExpectedError) { + buffer.write(expected._code); + } + if (expected is ExpectedLint) { + buffer.write(expected._lintName); + } + buffer.write(' [${expected._offset}, '); + buffer.write(expected._length); + if (expected._messageContains case Pattern messageContains) { + buffer.write(', messageContains: '); + buffer.write(json.encode(messageContains.toString())); + } + if (expected._correctionContains case Pattern correctionContains) { + buffer.write(', correctionContains: '); + buffer.write(json.encode(correctionContains.toString())); + } + buffer.writeln(']'); + } + return buffer.toString(); + } + @override File newFile(String path, String content) { if (_analysisContextCollection != null && !path.endsWith('.dart')) { @@ -414,6 +394,27 @@ class PubPackageResolutionTest with MockPackagesMixin, ResourceProviderMixin { _analysisContextCollection = null; } + /// Text to display upon failure, indicating that [unmatchedActual] + /// diagnostics were found, but unexpected. + @visibleForOverriding + String unexpectedMessage(List unmatchedActual) { + var buffer = StringBuffer(); + if (buffer.isNotEmpty) { + buffer.writeln(); + } + buffer.writeln('Found but did not expect:'); + for (var actual in unmatchedActual) { + buffer.write(' ${actual.diagnosticCode} ['); + buffer.write('${actual.offset}, ${actual.length}, ${actual.message}'); + if (actual.correctionMessage case Pattern correctionMessage) { + buffer.write(', '); + buffer.write(json.encode(correctionMessage)); + } + buffer.writeln(']'); + } + return buffer.toString(); + } + void writePackageConfig(String path, PackageConfigFileBuilder config) { newFile(path, config.toContent(pathContext: pathContext)); }