diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart index 0bd3d3a2d13..fffa7deb1b9 100644 --- a/pkg/analyzer/lib/src/dart/analysis/driver.dart +++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart @@ -17,6 +17,7 @@ import 'package:analyzer/src/context/packages.dart'; import 'package:analyzer/src/dart/analysis/analysis_options_map.dart'; import 'package:analyzer/src/dart/analysis/byte_store.dart'; import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart'; +import 'package:analyzer/src/dart/analysis/driver_event.dart' as events; import 'package:analyzer/src/dart/analysis/feature_set_provider.dart'; import 'package:analyzer/src/dart/analysis/file_content_cache.dart'; import 'package:analyzer/src/dart/analysis/file_state.dart'; @@ -1115,9 +1116,8 @@ class AnalysisDriver implements AnalysisDriverGeneric { if (_errorsRequestedFiles.isNotEmpty) { var path = _errorsRequestedFiles.keys.first; var completers = _errorsRequestedFiles.remove(path)!; - var result = await _computeErrors( - path: path, - ); + var analysisResult = await _computeAnalysisResult(path, withUnit: false); + var result = analysisResult.errorsResult!; for (var completer in completers) { completer.complete(result); } @@ -1340,7 +1340,15 @@ class AnalysisDriver implements AnalysisDriverGeneric { if (!withUnit) { var bytes = _byteStore.get(key); if (bytes != null) { - return _getAnalysisResultFromBytes(file, signature, bytes); + final unit = AnalysisDriverResolvedUnit.fromBuffer(bytes); + final errors = _getErrorsFromSerialized(file, unit.errors); + _updateHasErrorOrWarningFlag(file, errors); + final index = unit.index!; + final errorsResult = _createErrorsResultImpl( + file: file, + errors: errors, + ); + return AnalysisResult.errors(signature, errorsResult, index); } } @@ -1349,6 +1357,12 @@ class AnalysisDriver implements AnalysisDriverGeneric { _logger.writeln('Work in $name'); try { testView?.numOfAnalyzedLibraries++; + _resultController.add( + events.ComputeAnalysis( + file: file, + library: library, + ), + ); if (!_hasLibraryByUri('dart:core')) { return _newMissingDartLibraryResult(file, 'dart:core'); @@ -1446,13 +1460,6 @@ class AnalysisDriver implements AnalysisDriverGeneric { }); } - Future _computeErrors({ - required String path, - }) async { - var analysisResult = await _computeAnalysisResult(path, withUnit: false); - return analysisResult.errorsResult!; - } - Future _computeIndex(String path) async { var analysisResult = await _computeAnalysisResult(path, withUnit: false); return analysisResult._index!; @@ -1476,6 +1483,12 @@ class AnalysisDriver implements AnalysisDriverGeneric { performance: OperationPerformanceImpl(''), ); + _resultController.add( + events.ComputeResolvedLibrary( + library: library, + ), + ); + var analysisOptions = libraryContext.analysisContext .getAnalysisOptionsForFile(library.file.resource); @@ -1689,38 +1702,6 @@ class AnalysisDriver implements AnalysisDriverGeneric { _saltForUnlinked = buffer.toUint32List(); } - /// Load the [AnalysisResult] for the given [file] from the [bytes]. Set - /// optional [content] and [resolvedUnit]. - AnalysisResult _getAnalysisResultFromBytes( - FileState file, - String signature, - Uint8List bytes, { - String? content, - CompilationUnit? resolvedUnit, - List? errors, - }) { - var unit = AnalysisDriverResolvedUnit.fromBuffer(bytes); - errors ??= _getErrorsFromSerialized(file, unit.errors); - _updateHasErrorOrWarningFlag(file, errors); - var index = unit.index!; - if (content != null && resolvedUnit != null) { - var resolvedUnitResult = ResolvedUnitResultImpl( - session: currentSession, - fileState: file, - content: content, - unit: resolvedUnit, - errors: errors, - ); - return AnalysisResult.unit(signature, resolvedUnitResult, index); - } else { - var errorsResult = _createErrorsResultImpl( - file: file, - errors: errors, - ); - return AnalysisResult.errors(signature, errorsResult, index); - } - } - /// Return [AnalysisError]s for the given [serialized] errors. List _getErrorsFromSerialized( FileState file, List serialized) { diff --git a/pkg/analyzer/lib/src/dart/analysis/driver_event.dart b/pkg/analyzer/lib/src/dart/analysis/driver_event.dart new file mode 100644 index 00000000000..d0c90aea027 --- /dev/null +++ b/pkg/analyzer/lib/src/dart/analysis/driver_event.dart @@ -0,0 +1,27 @@ +// Copyright (c) 2024, 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 'package:analyzer/src/dart/analysis/driver.dart'; +import 'package:analyzer/src/dart/analysis/file_state.dart'; + +/// An event that happened inside the [AnalysisDriver]. +sealed class AnalysisDriverEvent {} + +final class ComputeAnalysis extends AnalysisDriverEvent { + final FileState file; + final LibraryFileKind library; + + ComputeAnalysis({ + required this.file, + required this.library, + }); +} + +final class ComputeResolvedLibrary extends AnalysisDriverEvent { + final LibraryFileKind library; + + ComputeResolvedLibrary({ + required this.library, + }); +} diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart index 5e0cac6a547..71964329ec6 100644 --- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart +++ b/pkg/analyzer/test/src/dart/analysis/driver_test.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:async'; + import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; @@ -34,6 +36,7 @@ import '../../../util/element_type_matchers.dart'; import '../../../utils.dart'; import '../resolution/context_collection_resolution.dart'; import 'base.dart'; +import 'result_printer.dart'; main() { defineReflectiveSuite(() { @@ -213,17 +216,92 @@ part of 'b.dart'; ); } + test_getResolvedLibraryByUri_library_pending_getResolvedUnit() async { + final a = newFile('$testPackageLibPath/a.dart', r''' +part 'b.dart'; +'''); + + final b = newFile('$testPackageLibPath/b.dart', r''' +part of 'a.dart'; +'''); + + final driver = driverFor(a); + + final collector = DriverEventCollector(driver); + collector.getResolvedUnit('a1', a); + collector.getResolvedUnit('b1', b); + + final uri = Uri.parse('package:test/a.dart'); + collector.getResolvedLibraryByUri('a2', uri); + + await pumpEventQueue(); + + // Note, that the `get` events are reported before `stream` events. + // TODO(scheglov): The current state is not optimal. + // We resolve `a.dart` separately as `analysisId: 0`. + // And then again `b.dart` as `analysisId: 1`. + // But actually we always resolve the whole library `a.dart`. + // So, we resolved it twice. + // Even worse, for `getResolvedLibraryByUri` we resolve it again. + // Theoretically we could have just one resolution overall. + assertDriverEventsText(collector.events, r''' +[operation] computeAnalysisResult + file: /home/test/lib/a.dart + library: /home/test/lib/a.dart +[future] getResolvedUnit + name: a1 + ResolvedUnitResult #0 + path: /home/test/lib/a.dart + uri: package:test/a.dart + flags: exists isLibrary +[stream] + ResolvedUnitResult #0 +[operation] computeAnalysisResult + file: /home/test/lib/b.dart + library: /home/test/lib/a.dart +[future] getResolvedUnit + name: b1 + ResolvedUnitResult #1 + path: /home/test/lib/b.dart + uri: package:test/b.dart + flags: exists isPart +[stream] + ResolvedUnitResult #1 +[operation] computeResolvedLibrary + library: /home/test/lib/a.dart +[future] getResolvedLibraryByUri + name: a2 + ResolvedLibraryResult + element: package:test/a.dart + units + ResolvedUnitResult #2 + path: /home/test/lib/a.dart + uri: package:test/a.dart + flags: exists isLibrary + ResolvedUnitResult #3 + path: /home/test/lib/b.dart + uri: package:test/b.dart + flags: exists isPart +'''); + } + test_getResolvedLibraryByUri_notLibrary_augmentation() async { final a = newFile('$testPackageLibPath/a.dart', r''' library augment 'b.dart'; '''); final driver = driverFor(a); + final collector = DriverEventCollector(driver); + final uri = Uri.parse('package:test/a.dart'); - expect( - await driver.getResolvedLibraryByUri(uri), - isA(), - ); + collector.getResolvedLibraryByUri('a1', uri); + + await pumpEventQueue(); + assertDriverEventsText(collector.events, r''' +[future] getResolvedLibraryByUri + name: a1 + NotLibraryButAugmentationResult +'''); } test_getResolvedLibraryByUri_notLibrary_part() async { @@ -1050,7 +1128,7 @@ var A = B; // We have a result only for "a". await waitForIdleWithoutExceptions(); - expect(allResults, hasLength(1)); + expect(allResults, hasLength(2)); { ResolvedUnitResult ar = allResults .whereType() @@ -1069,7 +1147,7 @@ var A = B; // While "b" is not analyzed explicitly, it is analyzed implicitly. // The change causes "a" to be reanalyzed. await waitForIdleWithoutExceptions(); - expect(allResults, hasLength(1)); + expect(allResults, hasLength(2)); { ResolvedUnitResult ar = allResults .whereType() @@ -1286,7 +1364,7 @@ var B1 = A1; await waitForIdleWithoutExceptions(); // We have results for both "a" and "b". - expect(allResults, hasLength(2)); + expect(allResults, hasLength(4)); { ResolvedUnitResult ar = allResults .whereType() @@ -1313,7 +1391,7 @@ var A2 = B1; // We again get results for both "a" and "b". // The results are consistent. await waitForIdleWithoutExceptions(); - expect(allResults, hasLength(2)); + expect(allResults, hasLength(4)); { ResolvedUnitResult ar = allResults .whereType() @@ -2118,7 +2196,7 @@ part of 'a.dart'; // The same result is also received through the stream. await waitForIdleWithoutExceptions(); - expect(allResults.toList(), [result]); + expect(allResults.whereType().toList(), [result]); } test_getResult_constants_defaultParameterValue_localFunction() async { @@ -3426,7 +3504,7 @@ var A = B; // We have results for both "a" and "b". await waitForIdleWithoutExceptions(); - expect(allResults, hasLength(2)); + expect(allResults, hasLength(4)); { ResolvedUnitResult ar = allResults .whereType() @@ -3450,7 +3528,7 @@ var A = B; // We don't get a result for "b". // But the change causes "a" to be reanalyzed. await waitForIdleWithoutExceptions(); - expect(allResults, hasLength(1)); + expect(allResults, hasLength(2)); { ResolvedUnitResult ar = allResults .whereType() @@ -3609,8 +3687,8 @@ class F extends X {} await waitForIdleWithoutExceptions(); - expect(allResults, hasLength(1)); - var result = allResults.single as ResolvedUnitResult; + expect(allResults, hasLength(2)); + var result = allResults.whereType().single; expect(result.path, testFile); expect(result.uri.toString(), 'package:test/test.dart'); expect(result.content, content); @@ -3636,8 +3714,8 @@ class F extends X {} driver.priorityFiles = [b]; await waitForIdleWithoutExceptions(); - expect(allResults, hasLength(3)); - var result = allResults.first as ResolvedUnitResult; + expect(allResults, hasLength(6)); + var result = allResults.whereType().first; expect(result.path, b); expect(result.unit, isNotNull); expect(result.errors, hasLength(0)); @@ -3648,8 +3726,8 @@ class F extends X {} addTestFile(content); await waitForIdleWithoutExceptions(); - expect(allResults, hasLength(1)); - var result = allResults.single as ErrorsResult; + expect(allResults, hasLength(2)); + var result = allResults.whereType().single; expect(result.path, testFile); expect(result.uri.toString(), 'package:test/test.dart'); expect(result.errors, hasLength(0)); @@ -3687,7 +3765,7 @@ var v = 0 driver.addFile(b); await waitForIdleWithoutExceptions(); - expect(allResults, hasLength(2)); + expect(allResults, hasLength(4)); allResults.clear(); // Update a.dart and notify. @@ -3696,7 +3774,7 @@ var v = 0 // Only result for a.dart should be produced, b.dart is not affected. await waitForIdleWithoutExceptions(); - expect(allResults, hasLength(1)); + expect(allResults, hasLength(2)); } test_results_status() async { @@ -3718,10 +3796,10 @@ var v = 0 expect(allResults, isEmpty); // scheduler.waitForIdle should wait for the analysis. await waitForIdleWithoutExceptions(); - expect(allResults, hasLength(1)); + expect(allResults, hasLength(2)); // Make sure there is no more analysis pending. await waitForIdleWithoutExceptions(); - expect(allResults, hasLength(1)); + expect(allResults, hasLength(2)); } Future waitForIdleWithoutExceptions() async { @@ -3828,6 +3906,48 @@ var v = 0 } } +/// Tracks events reported into the `results` stream, and results of `getXyz` +/// requests. We are interested in relative orders, identity of the objects, +/// absence of duplicate events, etc. +class DriverEventCollector { + final AnalysisDriver driver; + final List events = []; + + DriverEventCollector(this.driver) { + driver.results.listen((object) { + events.add( + ResultStreamEvent( + object: object, + ), + ); + }); + } + + void getResolvedLibraryByUri(String name, Uri uri) { + final future = driver.getResolvedLibraryByUri(uri); + unawaited(future.then((value) { + events.add( + GetResolvedLibraryByUriEvent( + name: name, + result: value, + ), + ); + })); + } + + void getResolvedUnit(String name, File file) { + final future = driver.getResult(file.path); + unawaited(future.then((value) { + events.add( + GetResolvedUnitEvent( + name: name, + result: value, + ), + ); + })); + } +} + class _SourceMock implements Source { @override final String fullName; diff --git a/pkg/analyzer/test/src/dart/analysis/result_printer.dart b/pkg/analyzer/test/src/dart/analysis/result_printer.dart index 70a148f1960..03ff8305789 100644 --- a/pkg/analyzer/test/src/dart/analysis/result_printer.dart +++ b/pkg/analyzer/test/src/dart/analysis/result_printer.dart @@ -6,6 +6,8 @@ import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/error/error.dart'; +import 'package:analyzer/src/dart/analysis/driver_event.dart' as events; +import 'package:analyzer/src/dart/analysis/results.dart'; import 'package:analyzer/src/utilities/extensions/file_system.dart'; import 'package:test/test.dart'; @@ -13,10 +15,151 @@ import '../../../util/element_printer.dart'; import '../../../util/tree_string_sink.dart'; import '../../summary/resolved_ast_printer.dart'; +sealed class DriverEvent {} + +class DriverEventsPrinter { + final ResolvedLibraryResultPrinterConfiguration configuration; + final TreeStringSink sink; + final ElementPrinter elementPrinter; + final IdProvider idProvider; + + DriverEventsPrinter({ + required this.configuration, + required this.sink, + required this.elementPrinter, + required this.idProvider, + }); + + void write(List events) { + for (final event in events) { + _writeEvent(event); + } + } + + void _writeEvent(DriverEvent event) { + switch (event) { + case GetResolvedLibraryByUriEvent(): + _writeGetResolvedLibraryByUri(event); + case GetResolvedUnitEvent(): + _writeGetResolvedUnit(event); + case ResultStreamEvent(): + _writeResultStreamEvent(event); + default: + throw UnimplementedError('${event.runtimeType}'); + } + } + + void _writeGetResolvedLibraryByUri(GetResolvedLibraryByUriEvent event) { + sink.writelnWithIndent('[future] getResolvedLibraryByUri'); + sink.withIndent(() { + sink.writelnWithIndent('name: ${event.name}'); + final result = event.result; + switch (result) { + case NotLibraryButAugmentationResult(): + sink.writelnWithIndent('NotLibraryButAugmentationResult'); + case ResolvedLibraryResult(): + ResolvedLibraryResultPrinter( + configuration: configuration, + sink: sink, + idProvider: idProvider, + elementPrinter: ElementPrinter( + sink: sink, + configuration: ElementPrinterConfiguration(), + selfUriStr: null, + ), + ).write(result); + default: + throw UnimplementedError('${result.runtimeType}'); + } + }); + } + + void _writeGetResolvedUnit(GetResolvedUnitEvent event) { + sink.writelnWithIndent('[future] getResolvedUnit'); + sink.withIndent(() { + sink.writelnWithIndent('name: ${event.name}'); + _writeResolvedUnitResult(event.result); + }); + } + + void _writeResolvedUnitResult(SomeResolvedUnitResult result) { + ResolvedUnitResultPrinter( + configuration: configuration.unitConfiguration, + sink: sink, + elementPrinter: elementPrinter, + idProvider: idProvider, + libraryElement: null, + ).write(result); + } + + void _writeResultStreamEvent(ResultStreamEvent event) { + final object = event.object; + switch (object) { + case events.ComputeAnalysis(): + sink.writelnWithIndent('[operation] computeAnalysisResult'); + sink.withIndent(() { + final file = object.file.resource; + sink.writelnWithIndent('file: ${file.posixPath}'); + final libraryFile = object.library.file.resource; + sink.writelnWithIndent('library: ${libraryFile.posixPath}'); + }); + case events.ComputeResolvedLibrary(): + sink.writelnWithIndent('[operation] computeResolvedLibrary'); + sink.withIndent(() { + final fileState = object.library.file; + final file = fileState.resource; + sink.writelnWithIndent('library: ${file.posixPath}'); + }); + case ResolvedUnitResult(): + sink.writelnWithIndent('[stream]'); + sink.withIndent(() { + _writeResolvedUnitResult(object); + }); + default: + throw UnimplementedError('${object.runtimeType}'); + } + } +} + +/// The result of `getResolvedLibraryByUri`. +final class GetResolvedLibraryByUriEvent extends DriverEvent { + final String name; + final SomeResolvedLibraryResult result; + + GetResolvedLibraryByUriEvent({ + required this.name, + required this.result, + }); +} + +/// The result of `getResolvedUnit`. +final class GetResolvedUnitEvent extends DriverEvent { + final String name; + final SomeResolvedUnitResult result; + + GetResolvedUnitEvent({ + required this.name, + required this.result, + }); +} + +class IdProvider { + final Map _map = Map.identity(); + + String operator [](Object object) { + return _map[object] ??= '#${_map.length}'; + } + + String? existing(Object object) { + return _map[object]; + } +} + class ResolvedLibraryResultPrinter { final ResolvedLibraryResultPrinterConfiguration configuration; final TreeStringSink sink; final ElementPrinter elementPrinter; + final IdProvider idProvider; late final LibraryElement _libraryElement; @@ -24,6 +167,7 @@ class ResolvedLibraryResultPrinter { required this.configuration, required this.sink, required this.elementPrinter, + required this.idProvider, }); void write(SomeResolvedLibraryResult result) { @@ -38,7 +182,7 @@ class ResolvedLibraryResultPrinter { void _writeResolvedLibraryResult(ResolvedLibraryResult result) { _libraryElement = result.element; - sink.writeln('ResolvedLibraryResult'); + sink.writelnWithIndent('ResolvedLibraryResult'); sink.withIndent(() { elementPrinter.writeNamedElement('element', result.element); sink.writeElements('units', result.units, _writeResolvedUnitResult); @@ -51,6 +195,7 @@ class ResolvedLibraryResultPrinter { sink: sink, elementPrinter: elementPrinter, libraryElement: _libraryElement, + idProvider: idProvider, ).write(result); } } @@ -63,18 +208,20 @@ class ResolvedUnitResultPrinter { final ResolvedUnitResultPrinterConfiguration configuration; final TreeStringSink sink; final ElementPrinter elementPrinter; - final LibraryElement libraryElement; + final LibraryElement? libraryElement; + final IdProvider idProvider; ResolvedUnitResultPrinter({ required this.configuration, required this.sink, required this.elementPrinter, required this.libraryElement, + required this.idProvider, }); void write(SomeResolvedUnitResult result) { switch (result) { - case ResolvedUnitResult(): + case ResolvedUnitResultImpl(): _writeResolvedUnitResult(result); default: throw UnimplementedError('${result.runtimeType}'); @@ -85,14 +232,26 @@ class ResolvedUnitResultPrinter { sink.writelnWithIndent('${e.offset} +${e.length} ${e.errorCode.name}'); } - void _writeResolvedUnitResult(ResolvedUnitResult result) { - sink.writelnWithIndent(result.file.posixPath); - expect(result.path, result.file.path); + void _writeResolvedUnitResult(ResolvedUnitResultImpl result) { + if (idProvider.existing(result) case final id?) { + sink.writelnWithIndent('ResolvedUnitResult $id'); + return; + } - // Don't write, just check. - expect(result.libraryElement, same(libraryElement)); + final id = idProvider[result]; + sink.writelnWithIndent('ResolvedUnitResult $id'); sink.withIndent(() { + sink.writelnWithIndent('path: ${result.file.posixPath}'); + expect(result.path, result.file.path); + + sink.writelnWithIndent('uri: ${result.uri}'); + + // Don't write, just check. + if (libraryElement != null) { + expect(result.libraryElement, same(libraryElement)); + } + sink.writeFlags({ 'exists': result.exists, 'isAugmentation': result.isAugmentation, @@ -100,7 +259,6 @@ class ResolvedUnitResultPrinter { 'isMacroAugmentation': result.isMacroAugmentation, 'isPart': result.isPart, }); - sink.writelnWithIndent('uri: ${result.uri}'); if (configuration.withContentPredicate(result)) { sink.writelnWithIndent('content'); @@ -131,3 +289,12 @@ class ResolvedUnitResultPrinterConfiguration { AstNode? Function(ResolvedUnitResult) nodeSelector = (_) => null; bool Function(ResolvedUnitResult) withContentPredicate = (_) => false; } + +/// The event of received an object into the `results` stream. +final class ResultStreamEvent extends DriverEvent { + final Object object; + + ResultStreamEvent({ + required this.object, + }); +} diff --git a/pkg/analyzer/test/src/dart/resolution/macro_test.dart b/pkg/analyzer/test/src/dart/resolution/macro_test.dart index 6236ab3f71f..b68185d60b7 100644 --- a/pkg/analyzer/test/src/dart/resolution/macro_test.dart +++ b/pkg/analyzer/test/src/dart/resolution/macro_test.dart @@ -320,12 +320,14 @@ class A {} ResolvedLibraryResult element: package:test/test.dart units - /home/test/lib/test.dart - flags: exists isLibrary + ResolvedUnitResult #0 + path: /home/test/lib/test.dart uri: package:test/test.dart - /home/test/lib/test.macro.dart - flags: exists isAugmentation isMacroAugmentation + flags: exists isLibrary + ResolvedUnitResult #1 + path: /home/test/lib/test.macro.dart uri: package:test/test.macro.dart + flags: exists isAugmentation isMacroAugmentation content --- library augment 'test.dart'; @@ -379,9 +381,10 @@ void f() { ResolvedLibraryResult element: package:test/test.dart units - /home/test/lib/test.dart - flags: exists isLibrary + ResolvedUnitResult #0 + path: /home/test/lib/test.dart uri: package:test/test.dart + flags: exists isLibrary selectedNode: Block leftBracket: { statements @@ -392,9 +395,10 @@ ResolvedLibraryResult staticType: int semicolon: ; rightBracket: } - /home/test/lib/test.macro.dart - flags: exists isAugmentation isMacroAugmentation + ResolvedUnitResult #1 + path: /home/test/lib/test.macro.dart uri: package:test/test.macro.dart + flags: exists isAugmentation isMacroAugmentation content --- library augment 'test.dart'; diff --git a/pkg/analyzer/test/src/dart/resolution/resolution.dart b/pkg/analyzer/test/src/dart/resolution/resolution.dart index 859d630908c..170c7d2e7e8 100644 --- a/pkg/analyzer/test/src/dart/resolution/resolution.dart +++ b/pkg/analyzer/test/src/dart/resolution/resolution.dart @@ -134,6 +134,40 @@ mixin ResolutionTest implements ResourceProviderMixin { expect(actual, expected); } + void assertDriverEventsText( + List events, + String expected, { + void Function(ResolvedLibraryResultPrinterConfiguration)? configure, + }) { + final configuration = ResolvedLibraryResultPrinterConfiguration(); + configure?.call(configuration); + + final buffer = StringBuffer(); + final sink = TreeStringSink(sink: buffer, indent: ''); + final idProvider = IdProvider(); + + final elementPrinter = ElementPrinter( + sink: sink, + configuration: ElementPrinterConfiguration(), + selfUriStr: null, + ); + + DriverEventsPrinter( + configuration: configuration, + sink: sink, + elementPrinter: elementPrinter, + idProvider: idProvider, + ).write(events); + + final actual = buffer.toString(); + if (actual != expected) { + print('-------- Actual --------'); + print('$actual------------------------'); + NodeTextExpectationsCollector.add(actual); + } + expect(actual, expected); + } + void assertElement(Object? nodeOrElement, Object? elementOrMatcher) { Element? element; if (nodeOrElement is AstNode) { @@ -307,9 +341,11 @@ mixin ResolutionTest implements ResourceProviderMixin { final buffer = StringBuffer(); final sink = TreeStringSink(sink: buffer, indent: ''); + final idProvider = IdProvider(); ResolvedLibraryResultPrinter( configuration: configuration, sink: sink, + idProvider: idProvider, elementPrinter: ElementPrinter( sink: sink, configuration: ElementPrinterConfiguration(),