diff --git a/_test_common/lib/common.dart b/_test_common/lib/common.dart index 1085f3d94..ad3b24e97 100644 --- a/_test_common/lib/common.dart +++ b/_test_common/lib/common.dart @@ -14,8 +14,7 @@ export 'package:build_test/build_test.dart' export 'assets.dart'; export 'builders.dart'; export 'descriptors.dart'; -export 'in_memory_reader.dart'; -export 'in_memory_writer.dart'; +export 'in_memory_reader_writer.dart'; export 'matchers.dart'; export 'package_graphs.dart'; export 'sdk.dart'; diff --git a/_test_common/lib/in_memory_reader.dart b/_test_common/lib/in_memory_reader.dart deleted file mode 100644 index 1b3ac171d..000000000 --- a/_test_common/lib/in_memory_reader.dart +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2016, 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 'package:build/build.dart'; -// ignore: implementation_imports -import 'package:build_runner_core/src/asset/reader.dart'; -import 'package:build_test/build_test.dart'; - -class InMemoryRunnerAssetReader extends InMemoryAssetReader - implements RunnerAssetReader { - final _onCanReadController = StreamController.broadcast(); - Stream get onCanRead => _onCanReadController.stream; - - @override - Future canRead(AssetId id) { - _onCanReadController.add(id); - return super.canRead(id); - } - - InMemoryRunnerAssetReader( - [Map? sourceAssets, String? rootPackage]) - : super(sourceAssets: sourceAssets, rootPackage: rootPackage); - - InMemoryRunnerAssetReader.shareAssetCache(super.assetCache, - {super.rootPackage}) - : super.shareAssetCache(); -} diff --git a/_test_common/lib/in_memory_writer.dart b/_test_common/lib/in_memory_reader_writer.dart similarity index 75% rename from _test_common/lib/in_memory_writer.dart rename to _test_common/lib/in_memory_reader_writer.dart index 2435845ff..bfb53d647 100644 --- a/_test_common/lib/in_memory_writer.dart +++ b/_test_common/lib/in_memory_reader_writer.dart @@ -10,10 +10,20 @@ import 'package:build_test/build_test.dart'; import 'package:path/path.dart' as p; import 'package:watcher/watcher.dart'; -class InMemoryRunnerAssetWriter extends InMemoryAssetWriter - implements RunnerAssetWriter { +class InMemoryRunnerAssetReaderWriter extends InMemoryAssetReaderWriter + implements RunnerAssetReader, RunnerAssetWriter { + final _onCanReadController = StreamController.broadcast(); + Stream get onCanRead => _onCanReadController.stream; void Function(AssetId)? onDelete; + InMemoryRunnerAssetReaderWriter({super.sourceAssets, super.rootPackage}); + + @override + Future canRead(AssetId id) { + _onCanReadController.add(id); + return super.canRead(id); + } + @override Future writeAsBytes(AssetId id, List bytes) async { var type = assets.containsKey(id) ? ChangeType.MODIFY : ChangeType.ADD; diff --git a/_test_common/lib/test_environment.dart b/_test_common/lib/test_environment.dart index 0328792c7..471faa396 100644 --- a/_test_common/lib/test_environment.dart +++ b/_test_common/lib/test_environment.dart @@ -4,29 +4,26 @@ import 'dart:async'; -// ignore: implementation_imports -import 'package:build_runner_core/src/asset/reader.dart'; -// ignore: implementation_imports -import 'package:build_runner_core/src/asset/writer.dart'; -// ignore: implementation_imports -import 'package:build_runner_core/src/environment/build_environment.dart'; +import 'package:build_runner_core/build_runner_core.dart'; import 'package:logging/logging.dart'; import 'common.dart'; +import 'in_memory_reader_writer.dart'; /// A [BuildEnvironment] for testing. /// -/// Defaults to using an [InMemoryRunnerAssetReader] and -/// [InMemoryRunnerAssetWriter]. +/// Defaults to an empty [InMemoryRunnerAssetReaderWriter]. /// /// To handle prompts you must first set `nextPromptResponse`. Alternatively /// you can set `throwOnPrompt` to `true` to emulate a /// [NonInteractiveBuildException]. class TestBuildEnvironment extends BuildEnvironment { + final InMemoryRunnerAssetReaderWriter _readerWriter; + @override - final RunnerAssetReader reader; + RunnerAssetReader get reader => _readerWriter; @override - final RunnerAssetWriter writer; + RunnerAssetWriter get writer => _readerWriter; /// If true, this will throw a [NonInteractiveBuildException] for all calls to /// [prompt]. @@ -44,11 +41,9 @@ class TestBuildEnvironment extends BuildEnvironment { int? _nextPromptResponse; TestBuildEnvironment( - {RunnerAssetReader? reader, - RunnerAssetWriter? writer, + {InMemoryRunnerAssetReaderWriter? readerWriter, this.throwOnPrompt = false}) - : reader = reader ?? InMemoryRunnerAssetReader(), - writer = writer ?? InMemoryRunnerAssetWriter(); + : _readerWriter = readerWriter ?? InMemoryRunnerAssetReaderWriter(); @override void onLog(LogRecord record) => logRecords.add(record); diff --git a/_test_common/lib/test_phases.dart b/_test_common/lib/test_phases.dart index 66b0f949b..c9d228db2 100644 --- a/_test_common/lib/test_phases.dart +++ b/_test_common/lib/test_phases.dart @@ -11,8 +11,7 @@ import 'package:build_test/build_test.dart'; import 'package:logging/logging.dart'; import 'package:test/test.dart'; -import 'in_memory_reader.dart'; -import 'in_memory_writer.dart'; +import 'in_memory_reader_writer.dart'; import 'package_graphs.dart'; Future wait(int milliseconds) => @@ -41,14 +40,13 @@ void _printOnFailure(LogRecord record) { /// For example `$$myapp|lib/utils.copy.dart` will check that the generated /// output was written to the build cache. /// +/// [resumeFrom] reuses the `readerWriter` from a previous [BuildResult]. +/// /// [packageGraph] supplies the root package into which the outputs are to be /// written. /// /// [status] optionally indicates the desired outcome. /// -/// [writer] can optionally be provided to capture assets written by the -/// builders (e.g. when [outputs] is not sufficient). -/// /// [logLevel] sets the builder log level and [onLog] can optionally capture /// build log messages. /// @@ -67,15 +65,14 @@ void _printOnFailure(LogRecord record) { /// }); /// } /// -Future testBuilders( +Future testBuilders( List builders, Map*/ Object> inputs, { + TestBuildersResult? resumeFrom, Map*/ Object>? outputs, PackageGraph? packageGraph, BuildStatus status = BuildStatus.success, Map? overrideBuildConfig, - InMemoryRunnerAssetReader? reader, - InMemoryRunnerAssetWriter? writer, Level? logLevel, // A better way to "silence" logging than setting logLevel to OFF. void Function(LogRecord record) onLog = _printOnFailure, @@ -88,14 +85,16 @@ Future testBuilders( Set buildFilters = const {}, String? logPerformanceDir, String expectedGeneratedDir = 'generated', + void Function(AssetId id)? onDelete, }) async { packageGraph ??= buildPackageGraph({rootPackage('a'): []}); - writer ??= InMemoryRunnerAssetWriter(); - reader ??= InMemoryRunnerAssetReader.shareAssetCache(writer.assets, - rootPackage: packageGraph.root.name); + final readerWriter = resumeFrom == null + ? InMemoryRunnerAssetReaderWriter(rootPackage: packageGraph.root.name) + : resumeFrom.readerWriter; + readerWriter.onDelete = onDelete; var pkgConfigId = AssetId(packageGraph.root.name, '.dart_tool/package_config.json'); - if (!await reader.canRead(pkgConfigId)) { + if (!await readerWriter.canRead(pkgConfigId)) { var packageConfig = { 'configVersion': 2, 'packages': [ @@ -108,21 +107,21 @@ Future testBuilders( }, ], }; - await writer.writeAsString(pkgConfigId, jsonEncode(packageConfig)); + await readerWriter.writeAsString(pkgConfigId, jsonEncode(packageConfig)); } inputs.forEach((serializedId, contents) { var id = makeAssetId(serializedId); if (contents is String) { - reader!.cacheStringAsset(id, contents); + readerWriter.cacheStringAsset(id, contents); } else if (contents is List) { - reader!.cacheBytesAsset(id, contents); + readerWriter.cacheBytesAsset(id, contents); } }); builderConfigOverrides ??= const {}; var environment = OverrideableEnvironment(IOEnvironment(packageGraph), - reader: reader, writer: writer, onLog: onLog); + reader: readerWriter, writer: readerWriter, onLog: onLog); var logSubscription = LogSubscription(environment, verbose: verbose, logLevel: logLevel); var options = await BuildOptions.create(logSubscription, @@ -149,12 +148,13 @@ Future testBuilders( if (checkBuildStatus) { checkBuild(result, outputs: outputs, - writer: writer, + writer: readerWriter, status: status, rootPackage: packageGraph.root.name, expectedGeneratedDir: expectedGeneratedDir); } - return result; + + return TestBuildersResult(buildResult: result, readerWriter: readerWriter); } /// Translates expected outptus which start with `$$` to the build cache and @@ -190,3 +190,10 @@ void checkBuild(BuildResult result, mapAssetIds: (id) => mapHidden(id, expectedGeneratedDir)); } } + +class TestBuildersResult { + final BuildResult buildResult; + final InMemoryRunnerAssetReaderWriter readerWriter; + + TestBuildersResult({required this.buildResult, required this.readerWriter}); +} diff --git a/build/CHANGELOG.md b/build/CHANGELOG.md index 92d8529d5..4e802e719 100644 --- a/build/CHANGELOG.md +++ b/build/CHANGELOG.md @@ -2,6 +2,7 @@ - Add `package:build/src/internal.dart` for use by `build_resolvers`, `build_runner_core` and `build_test`. +- Use `build_test` 3.0.0. ## 2.4.2 diff --git a/build/pubspec.yaml b/build/pubspec.yaml index f1d8ab97d..22f4e9cb9 100644 --- a/build/pubspec.yaml +++ b/build/pubspec.yaml @@ -20,7 +20,7 @@ dependencies: dev_dependencies: build_resolvers: ^2.4.0 - build_test: ^2.0.0 + build_test: ^3.0.0-wip dart_flutter_team_lints: ^3.1.0 test: ^1.16.0 diff --git a/build/test/builder/build_step_impl_test.dart b/build/test/builder/build_step_impl_test.dart index 315f6f140..3ab3ccb95 100644 --- a/build/test/builder/build_step_impl_test.dart +++ b/build/test/builder/build_step_impl_test.dart @@ -78,8 +78,7 @@ void main() { late InMemoryAssetReader reader; setUp(() { - writer = InMemoryAssetWriter(); - reader = InMemoryAssetReader.shareAssetCache(writer.assets); + writer = reader = InMemoryAssetReaderWriter(); }); test('tracks outputs created by a builder', () async { diff --git a/build/test/generate/run_builder_test.dart b/build/test/generate/run_builder_test.dart index 90747ba54..b9821447a 100644 --- a/build/test/generate/run_builder_test.dart +++ b/build/test/generate/run_builder_test.dart @@ -29,8 +29,7 @@ void main() { }); builder = TestBuilder( extraWork: (buildStep, __) => buildStep.fetchResource(resource)); - writer = InMemoryAssetWriter(); - reader = InMemoryAssetReader.shareAssetCache(writer.assets); + writer = reader = InMemoryAssetReaderWriter(); addAssets(inputs, writer); }); diff --git a/build_modules/CHANGELOG.md b/build_modules/CHANGELOG.md index 5bdf3d00e..7c2bbc51c 100644 --- a/build_modules/CHANGELOG.md +++ b/build_modules/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.0.12-wip + +- Use `build_test` 3.0.0. + ## 5.0.11 - Support 3.8.0 pre-release sdks. diff --git a/build_modules/pubspec.yaml b/build_modules/pubspec.yaml index 085527608..5e7818c8d 100644 --- a/build_modules/pubspec.yaml +++ b/build_modules/pubspec.yaml @@ -1,5 +1,5 @@ name: build_modules -version: 5.0.11 +version: 5.0.12-wip description: >- Builders to analyze and split Dart code into individually compilable modules based on imports. @@ -32,7 +32,7 @@ dev_dependencies: path: test/fixtures/b # Used inside tests build_runner: ^2.0.0 - build_test: ^2.0.0 + build_test: ^3.0.0-wip json_serializable: ^6.0.0 test: ^1.16.0 diff --git a/build_modules/test/util.dart b/build_modules/test/util.dart index 85b17d2cc..04cd06eb9 100644 --- a/build_modules/test/util.dart +++ b/build_modules/test/util.dart @@ -17,15 +17,13 @@ Future testBuilderAndCollectAssets( void Function(LogRecord log)? onLog, void Function(AssetId, Iterable)? reportUnusedAssetsForInput}) async { - var writer = InMemoryAssetWriter(); onLog ??= (log) => printOnFailure('${log.level}: ${log.message}'); - await testBuilder(builder, assets, + final result = await testBuilder(builder, assets, generateFor: generateFor, outputs: outputs, onLog: onLog, - reportUnusedAssetsForInput: reportUnusedAssetsForInput, - writer: writer); - writer.assets.forEach((id, value) { + reportUnusedAssetsForInput: reportUnusedAssetsForInput); + result.readerWriter.assets.forEach((id, value) { assets['${id.package}|${id.path}'] = value; }); } diff --git a/build_resolvers/CHANGELOG.md b/build_resolvers/CHANGELOG.md index f346777dc..b8399fd20 100644 --- a/build_resolvers/CHANGELOG.md +++ b/build_resolvers/CHANGELOG.md @@ -3,6 +3,7 @@ - Start using `package:build/src/internal.dart`. - Switch `BuildAssetUriResolver` dependency crawl to an iterative algorithm, preventing stack overflows. +- Use `build_test` 3.0.0. ## 2.4.4 diff --git a/build_resolvers/pubspec.yaml b/build_resolvers/pubspec.yaml index 31948e7d5..5db1bad0a 100644 --- a/build_resolvers/pubspec.yaml +++ b/build_resolvers/pubspec.yaml @@ -24,7 +24,7 @@ dependencies: yaml: ^3.0.0 dev_dependencies: - build_test: ^2.0.0 + build_test: ^3.0.0-wip dart_flutter_team_lints: ^3.1.0 test: ^1.16.0 diff --git a/build_resolvers/test/resolver_test.dart b/build_resolvers/test/resolver_test.dart index 0849fc424..a913e2334 100644 --- a/build_resolvers/test/resolver_test.dart +++ b/build_resolvers/test/resolver_test.dart @@ -975,16 +975,15 @@ int? get x => 1; }, ); - final writer = InMemoryAssetWriter(); - final reader = InMemoryAssetReader.shareAssetCache(writer.assets); + final readerWriter = InMemoryAssetReaderWriter(); - writer.assets[makeAssetId('a|lib/a.dart')] = utf8.encode(''); - await runBuilder( - builder, [makeAssetId('a|lib/a.dart')], reader, writer, resolvers); + readerWriter.assets[makeAssetId('a|lib/a.dart')] = utf8.encode(''); + await runBuilder(builder, [makeAssetId('a|lib/a.dart')], readerWriter, + readerWriter, resolvers); - writer.assets[makeAssetId('a|lib/b.dart')] = utf8.encode(''); - await runBuilder( - builder, [makeAssetId('a|lib/b.dart')], reader, writer, resolvers); + readerWriter.assets[makeAssetId('a|lib/b.dart')] = utf8.encode(''); + await runBuilder(builder, [makeAssetId('a|lib/b.dart')], readerWriter, + readerWriter, resolvers); }); group('The ${isFlutter ? 'flutter' : 'dart'} sdk', () { @@ -1018,10 +1017,9 @@ int? get x => 1; }); test('generated part files are not considered libraries', () async { - var writer = InMemoryAssetWriter(); - var reader = InMemoryAssetReader.shareAssetCache(writer.assets); + var readerWriter = InMemoryAssetReaderWriter(); var input = AssetId('a', 'lib/input.dart'); - writer.assets[input] = utf8.encode("part 'input.a.dart';"); + readerWriter.assets[input] = utf8.encode("part 'input.a.dart';"); var builder = TestBuilder( buildExtensions: { @@ -1042,17 +1040,16 @@ int? get x => 1; } }); var resolvers = createResolvers(); - await runBuilder(builder, [input], reader, writer, resolvers); + await runBuilder(builder, [input], readerWriter, readerWriter, resolvers); - await runBuilder( - builder, [input.changeExtension('.a.dart')], reader, writer, resolvers); + await runBuilder(builder, [input.changeExtension('.a.dart')], readerWriter, + readerWriter, resolvers); }); test('missing files are not considered libraries', () async { - var writer = InMemoryAssetWriter(); - var reader = InMemoryAssetReader.shareAssetCache(writer.assets); + var readerWriter = InMemoryAssetReaderWriter(); var input = AssetId('a', 'lib/input.dart'); - writer.assets[input] = utf8.encode('void doStuff() {}'); + readerWriter.assets[input] = utf8.encode('void doStuff() {}'); var builder = TestBuilder( buildExtensions: { @@ -1065,18 +1062,17 @@ int? get x => 1; false); })); var resolvers = createResolvers(); - await runBuilder(builder, [input], reader, writer, resolvers); + await runBuilder(builder, [input], readerWriter, readerWriter, resolvers); }); test('assets with extensions other than `.dart` are not considered libraries', () async { - var writer = InMemoryAssetWriter(); - var reader = InMemoryAssetReader.shareAssetCache(writer.assets); + var readerWriter = InMemoryAssetReaderWriter(); var input = AssetId('a', 'lib/input.dart'); - writer.assets[input] = utf8.encode('void doStuff() {}'); + readerWriter.assets[input] = utf8.encode('void doStuff() {}'); var otherFile = AssetId('a', 'lib/input.notdart'); - writer.assets[otherFile] = utf8.encode('Not a Dart file'); + readerWriter.assets[otherFile] = utf8.encode('Not a Dart file'); var builder = TestBuilder( buildExtensions: { @@ -1088,7 +1084,7 @@ int? get x => 1; expect(await buildStep.resolver.isLibrary(other), false); })); var resolvers = createResolvers(); - await runBuilder(builder, [input], reader, writer, resolvers); + await runBuilder(builder, [input], readerWriter, readerWriter, resolvers); }); group('compilationUnitFor', () { diff --git a/build_runner/CHANGELOG.md b/build_runner/CHANGELOG.md index 2a7a34398..78cfaa648 100644 --- a/build_runner/CHANGELOG.md +++ b/build_runner/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.16-wip + +- Use `build_test` 3.0.0. + ## 2.4.15 - Update to package:web and dart:js_interop. diff --git a/build_runner/pubspec.yaml b/build_runner/pubspec.yaml index 3f08b722c..41ded8636 100644 --- a/build_runner/pubspec.yaml +++ b/build_runner/pubspec.yaml @@ -1,5 +1,5 @@ name: build_runner -version: 2.4.15 +version: 2.4.16-wip description: A build system for Dart code generation and modular compilation. repository: https://github.com/dart-lang/build/tree/master/build_runner resolution: workspace @@ -53,7 +53,7 @@ dependencies: dev_dependencies: _test_common: path: ../_test_common - build_test: ^2.0.0 + build_test: ^3.0.0-wip build_web_compilers: ^4.0.0 dart_flutter_team_lints: ^3.1.0 stream_channel: ^2.0.0 diff --git a/build_runner/test/generate/build_test.dart b/build_runner/test/generate/build_test.dart index 1c75c50d9..2906b3309 100644 --- a/build_runner/test/generate/build_test.dart +++ b/build_runner/test/generate/build_test.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:convert'; import 'package:_test_common/common.dart'; +import 'package:build/build.dart'; import 'package:build_runner/src/generate/build.dart' as build_impl; import 'package:build_runner_core/build_runner_core.dart'; import 'package:logging/logging.dart'; @@ -17,12 +18,6 @@ void main() { final copyABuildApplication = applyToRoot( TestBuilder(buildExtensions: appendExtension('.copy', from: '.txt'))); final packageConfigId = makeAssetId('a|.dart_tool/package_config.json'); - late InMemoryRunnerAssetWriter writer; - - setUp(() async { - writer = InMemoryRunnerAssetWriter(); - await writer.writeAsString(packageConfigId, jsonEncode(_packageConfig)); - }); group('--config', () { test('warns override config defines builders', () async { @@ -41,7 +36,8 @@ builders: builder_factories: ["myFactory"] build_extensions: {"a": ["b"]} ''' - }, writer, + }, + packageConfigId: packageConfigId, configKey: 'cool', logLevel: Level.WARNING, onLog: logs.add, @@ -55,26 +51,28 @@ builders: }); } -Future _doBuild(List builders, - Map inputs, InMemoryRunnerAssetWriter writer, - {PackageGraph? packageGraph, +Future _doBuild( + List builders, Map inputs, + {required AssetId packageConfigId, + PackageGraph? packageGraph, void Function(LogRecord)? onLog, Level? logLevel, String? configKey}) async { onLog ??= (_) {}; - inputs.forEach((serializedId, contents) { - writer.writeAsString(makeAssetId(serializedId), contents); - }); packageGraph ??= buildPackageGraph({rootPackage('a', path: path.absolute('a')): []}); - final reader = InMemoryRunnerAssetReader.shareAssetCache(writer.assets, - rootPackage: packageGraph.root.name); + final readerWriter = + InMemoryRunnerAssetReaderWriter(rootPackage: packageGraph.root.name); + inputs.forEach((serializedId, contents) { + readerWriter.writeAsString(makeAssetId(serializedId), contents); + }); + await readerWriter.writeAsString(packageConfigId, jsonEncode(_packageConfig)); return await build_impl.build(builders, configKey: configKey, deleteFilesByDefault: true, - reader: reader, - writer: writer, + reader: readerWriter, + writer: readerWriter, packageGraph: packageGraph, logLevel: logLevel, onLog: onLog, diff --git a/build_runner/test/generate/serve_test.dart b/build_runner/test/generate/serve_test.dart index 80db38259..ee9dfd29f 100644 --- a/build_runner/test/generate/serve_test.dart +++ b/build_runner/test/generate/serve_test.dart @@ -17,12 +17,15 @@ import 'package:test/test.dart'; void main() { group('ServeHandler', () { - late InMemoryRunnerAssetWriter writer; + final packageGraph = + buildPackageGraph({rootPackage('a', path: path.absolute('a')): []}); + late InMemoryRunnerAssetReaderWriter readerWriter; setUp(() async { _terminateServeController = StreamController(); - writer = InMemoryRunnerAssetWriter(); - await writer.writeAsString( + readerWriter = + InMemoryRunnerAssetReaderWriter(rootPackage: packageGraph.root.name); + await readerWriter.writeAsString( makeAssetId('a|.dart_tool/package_config.json'), jsonEncode({ 'configVersion': 2, @@ -42,16 +45,18 @@ void main() { }); test('does basic builds', () async { - var handler = await createHandler( - [applyToRoot(TestBuilder())], {'a|web/a.txt': 'a'}, writer); + var handler = await createHandler([applyToRoot(TestBuilder())], + {'a|web/a.txt': 'a'}, packageGraph, readerWriter); var results = StreamQueue(handler.buildResults); var result = await results.next; - checkBuild(result, outputs: {'a|web/a.txt.copy': 'a'}, writer: writer); + checkBuild(result, + outputs: {'a|web/a.txt.copy': 'a'}, writer: readerWriter); - await writer.writeAsString(makeAssetId('a|web/a.txt'), 'b'); + await readerWriter.writeAsString(makeAssetId('a|web/a.txt'), 'b'); result = await results.next; - checkBuild(result, outputs: {'a|web/a.txt.copy': 'b'}, writer: writer); + checkBuild(result, + outputs: {'a|web/a.txt.copy': 'b'}, writer: readerWriter); }); test('blocks serving files until the build is done', () async { @@ -61,7 +66,8 @@ void main() { var handler = await createHandler( [applyToRoot(TestBuilder(extraWork: (_, __) => nextBuildBlocker))], {'a|web/a.txt': 'a'}, - writer); + packageGraph, + readerWriter); var webHandler = handler.handlerFor('web'); var results = StreamQueue(handler.buildResults); // Give the build enough time to get started. @@ -76,7 +82,8 @@ void main() { await wait(250); buildBlocker1.complete(); var result = await results.next; - checkBuild(result, outputs: {'a|web/a.txt.copy': 'a'}, writer: writer); + checkBuild(result, + outputs: {'a|web/a.txt.copy': 'a'}, writer: readerWriter); /// Next request completes right away. var buildBlocker2 = Completer(); @@ -88,7 +95,7 @@ void main() { /// Make an edit to force another build, and we should block again. nextBuildBlocker = buildBlocker2.future; - await writer.writeAsString(makeAssetId('a|web/a.txt'), 'b'); + await readerWriter.writeAsString(makeAssetId('a|web/a.txt'), 'b'); // Give the build enough time to get started. await wait(500); var done = Completer(); @@ -101,7 +108,8 @@ void main() { await wait(250); buildBlocker2.complete(); result = await results.next; - checkBuild(result, outputs: {'a|web/a.txt.copy': 'b'}, writer: writer); + checkBuild(result, + outputs: {'a|web/a.txt.copy': 'b'}, writer: readerWriter); /// Make sure we actually see the final request finish. return done.future; @@ -113,24 +121,23 @@ final _debounceDelay = const Duration(milliseconds: 10); StreamController? _terminateServeController; /// Start serving files and running builds. -Future createHandler(List builders, - Map inputs, InMemoryRunnerAssetWriter writer) async { +Future createHandler( + List builders, + Map inputs, + PackageGraph packageGraph, + InMemoryRunnerAssetReaderWriter readerWriter) async { await Future.wait(inputs.keys.map((serializedId) async { - await writer.writeAsString( + await readerWriter.writeAsString( makeAssetId(serializedId), inputs[serializedId]!); })); - final packageGraph = - buildPackageGraph({rootPackage('a', path: path.absolute('a')): []}); - final reader = InMemoryRunnerAssetReader.shareAssetCache(writer.assets, - rootPackage: packageGraph.root.name); FakeWatcher watcherFactory(String path) => FakeWatcher(path); return watch_impl.watch(builders, deleteFilesByDefault: true, debounceDelay: _debounceDelay, directoryWatcherFactory: watcherFactory, - reader: reader, - writer: writer, + reader: readerWriter, + writer: readerWriter, packageGraph: packageGraph, terminateEventStream: _terminateServeController!.stream, logLevel: Level.OFF, diff --git a/build_runner/test/generate/watch_test.dart b/build_runner/test/generate/watch_test.dart index b80d6d30d..099d5befe 100644 --- a/build_runner/test/generate/watch_test.dart +++ b/build_runner/test/generate/watch_test.dart @@ -26,11 +26,15 @@ void main() { TestBuilder(buildExtensions: appendExtension('.copy', from: '.txt'))); final defaultBuilderOptions = const BuilderOptions({}); final packageConfigId = makeAssetId('a|.dart_tool/package_config.json'); - late InMemoryRunnerAssetWriter writer; + final packageGraph = + buildPackageGraph({rootPackage('a', path: path.absolute('a')): []}); + late InMemoryRunnerAssetReaderWriter readerWriter; setUp(() async { - writer = InMemoryRunnerAssetWriter(); - await writer.writeAsString(packageConfigId, jsonEncode(_packageConfig)); + readerWriter = + InMemoryRunnerAssetReaderWriter(rootPackage: packageGraph.root.name); + await readerWriter.writeAsString( + packageConfigId, jsonEncode(_packageConfig)); }); group('watch', () { @@ -46,16 +50,19 @@ void main() { group('simple', () { test('rebuilds once on file updates', () async { var buildState = await startWatch( - [copyABuildApplication], {'a|web/a.txt': 'a'}, writer); + [copyABuildApplication], {'a|web/a.txt': 'a'}, readerWriter, + packageGraph: packageGraph); var results = StreamQueue(buildState.buildResults); var result = await results.next; - checkBuild(result, outputs: {'a|web/a.txt.copy': 'a'}, writer: writer); + checkBuild(result, + outputs: {'a|web/a.txt.copy': 'a'}, writer: readerWriter); - await writer.writeAsString(makeAssetId('a|web/a.txt'), 'b'); + await readerWriter.writeAsString(makeAssetId('a|web/a.txt'), 'b'); result = await results.next; - checkBuild(result, outputs: {'a|web/a.txt.copy': 'b'}, writer: writer); + checkBuild(result, + outputs: {'a|web/a.txt.copy': 'b'}, writer: readerWriter); // Wait for the `_debounceDelay` before terminating. await Future.delayed(_debounceDelay); @@ -69,7 +76,8 @@ void main() { var buildState = await startWatch( [], {'a|web/a.txt.copy': 'a'}, - writer, + readerWriter, + packageGraph: packageGraph, onLog: logs.add, logLevel: Level.SEVERE, ); @@ -83,7 +91,8 @@ void main() { test('rebuilds on file updates outside hardcoded sources', () async { var buildState = await startWatch( - [copyABuildApplication], {'a|test_files/a.txt': 'a'}, writer, + [copyABuildApplication], {'a|test_files/a.txt': 'a'}, readerWriter, + packageGraph: packageGraph, overrideBuildConfig: parseBuildConfigs({ 'a': { 'targets': { @@ -97,35 +106,43 @@ void main() { var result = await results.next; checkBuild(result, - outputs: {'a|test_files/a.txt.copy': 'a'}, writer: writer); + outputs: {'a|test_files/a.txt.copy': 'a'}, writer: readerWriter); - await writer.writeAsString(makeAssetId('a|test_files/a.txt'), 'b'); + await readerWriter.writeAsString( + makeAssetId('a|test_files/a.txt'), 'b'); result = await results.next; checkBuild(result, - outputs: {'a|test_files/a.txt.copy': 'b'}, writer: writer); + outputs: {'a|test_files/a.txt.copy': 'b'}, writer: readerWriter); }); test('rebuilds on new files', () async { var buildState = await startWatch( - [copyABuildApplication], {'a|web/a.txt': 'a'}, writer); + [copyABuildApplication], + {'a|web/a.txt': 'a'}, + readerWriter, + packageGraph: packageGraph, + ); var results = StreamQueue(buildState.buildResults); var result = await results.next; - checkBuild(result, outputs: {'a|web/a.txt.copy': 'a'}, writer: writer); + checkBuild(result, + outputs: {'a|web/a.txt.copy': 'a'}, writer: readerWriter); - await writer.writeAsString(makeAssetId('a|web/b.txt'), 'b'); + await readerWriter.writeAsString(makeAssetId('a|web/b.txt'), 'b'); result = await results.next; - checkBuild(result, outputs: {'a|web/b.txt.copy': 'b'}, writer: writer); + checkBuild(result, + outputs: {'a|web/b.txt.copy': 'b'}, writer: readerWriter); // Previous outputs should still exist. - expect(writer.assets[makeAssetId('a|web/a.txt.copy')], + expect(readerWriter.assets[makeAssetId('a|web/a.txt.copy')], decodedMatches('a')); }); test('rebuilds on new files outside hardcoded sources', () async { var buildState = await startWatch( - [copyABuildApplication], {'a|test_files/a.txt': 'a'}, writer, + [copyABuildApplication], {'a|test_files/a.txt': 'a'}, readerWriter, + packageGraph: packageGraph, overrideBuildConfig: parseBuildConfigs({ 'a': { 'targets': { @@ -139,46 +156,50 @@ void main() { var result = await results.next; checkBuild(result, - outputs: {'a|test_files/a.txt.copy': 'a'}, writer: writer); + outputs: {'a|test_files/a.txt.copy': 'a'}, writer: readerWriter); - await writer.writeAsString(makeAssetId('a|test_files/b.txt'), 'b'); + await readerWriter.writeAsString( + makeAssetId('a|test_files/b.txt'), 'b'); result = await results.next; checkBuild(result, - outputs: {'a|test_files/b.txt.copy': 'b'}, writer: writer); + outputs: {'a|test_files/b.txt.copy': 'b'}, writer: readerWriter); // Previous outputs should still exist. - expect(writer.assets[makeAssetId('a|test_files/a.txt.copy')], + expect(readerWriter.assets[makeAssetId('a|test_files/a.txt.copy')], decodedMatches('a')); }); test('rebuilds on deleted files', () async { - var buildState = await startWatch([ - copyABuildApplication - ], { - 'a|web/a.txt': 'a', - 'a|web/b.txt': 'b', - }, writer); + var buildState = await startWatch( + [copyABuildApplication], + { + 'a|web/a.txt': 'a', + 'a|web/b.txt': 'b', + }, + readerWriter, + packageGraph: packageGraph, + ); var results = StreamQueue(buildState.buildResults); var result = await results.next; checkBuild(result, outputs: {'a|web/a.txt.copy': 'a', 'a|web/b.txt.copy': 'b'}, - writer: writer); + writer: readerWriter); // Don't call writer.delete, that has side effects. - writer.assets.remove(makeAssetId('a|web/a.txt')); + readerWriter.assets.remove(makeAssetId('a|web/a.txt')); FakeWatcher.notifyWatchers( WatchEvent(ChangeType.REMOVE, path.absolute('a', 'web', 'a.txt'))); result = await results.next; // Shouldn't rebuild anything, no outputs. - checkBuild(result, outputs: {}, writer: writer); + checkBuild(result, outputs: {}, writer: readerWriter); // The old output file should no longer exist either. - expect(writer.assets[makeAssetId('a|web/a.txt.copy')], isNull); + expect(readerWriter.assets[makeAssetId('a|web/a.txt.copy')], isNull); // Previous outputs should still exist. - expect(writer.assets[makeAssetId('a|web/b.txt.copy')], + expect(readerWriter.assets[makeAssetId('a|web/b.txt.copy')], decodedMatches('b')); }); @@ -188,7 +209,8 @@ void main() { ], { 'a|test_files/a.txt': 'a', 'a|test_files/b.txt': 'b', - }, writer, + }, readerWriter, + packageGraph: packageGraph, overrideBuildConfig: parseBuildConfigs({ 'a': { 'targets': { @@ -206,58 +228,63 @@ void main() { 'a|test_files/a.txt.copy': 'a', 'a|test_files/b.txt.copy': 'b' }, - writer: writer); + writer: readerWriter); // Don't call writer.delete, that has side effects. - writer.assets.remove(makeAssetId('a|test_files/a.txt')); + readerWriter.assets.remove(makeAssetId('a|test_files/a.txt')); FakeWatcher.notifyWatchers(WatchEvent( ChangeType.REMOVE, path.absolute('a', 'test_files', 'a.txt'))); result = await results.next; // Shouldn't rebuild anything, no outputs. - checkBuild(result, outputs: {}, writer: writer); + checkBuild(result, outputs: {}, writer: readerWriter); // The old output file should no longer exist either. - expect(writer.assets[makeAssetId('a|test_files/a.txt.copy')], isNull); + expect(readerWriter.assets[makeAssetId('a|test_files/a.txt.copy')], + isNull); // Previous outputs should still exist. - expect(writer.assets[makeAssetId('a|test_files/b.txt.copy')], + expect(readerWriter.assets[makeAssetId('a|test_files/b.txt.copy')], decodedMatches('b')); }); test('rebuilds properly update asset_graph.json', () async { - var buildState = await startWatch([copyABuildApplication], - {'a|web/a.txt': 'a', 'a|web/b.txt': 'b'}, writer); + var buildState = await startWatch( + [copyABuildApplication], + {'a|web/a.txt': 'a', 'a|web/b.txt': 'b'}, + readerWriter, + packageGraph: packageGraph, + ); var results = StreamQueue(buildState.buildResults); var result = await results.next; checkBuild(result, outputs: {'a|web/a.txt.copy': 'a', 'a|web/b.txt.copy': 'b'}, - writer: writer); + writer: readerWriter); - await writer.writeAsString(makeAssetId('a|web/c.txt'), 'c'); + await readerWriter.writeAsString(makeAssetId('a|web/c.txt'), 'c'); - await writer.writeAsString(makeAssetId('a|web/b.txt'), 'b2'); + await readerWriter.writeAsString(makeAssetId('a|web/b.txt'), 'b2'); // Don't call writer.delete, that has side effects. - writer.assets.remove(makeAssetId('a|web/a.txt')); + readerWriter.assets.remove(makeAssetId('a|web/a.txt')); FakeWatcher.notifyWatchers( WatchEvent(ChangeType.REMOVE, path.absolute('a', 'web', 'a.txt'))); result = await results.next; checkBuild(result, outputs: {'a|web/b.txt.copy': 'b2', 'a|web/c.txt.copy': 'c'}, - writer: writer); + writer: readerWriter); var cachedGraph = AssetGraph.deserialize( - writer.assets[makeAssetId('a|$assetGraphPath')]!); + readerWriter.assets[makeAssetId('a|$assetGraphPath')]!); var expectedGraph = await AssetGraph.build( [], {}, {packageConfigId}, buildPackageGraph({rootPackage('a'): []}), - InMemoryAssetReader(sourceAssets: writer.assets)); + InMemoryAssetReader(sourceAssets: readerWriter.assets)); var builderOptionsId = makeAssetId('a|Phase0.builderOptions'); var builderOptionsNode = BuilderOptionsAssetNode(builderOptionsId, @@ -317,16 +344,17 @@ void main() { ], { 'a|web/a.txt': 'a', 'b|web/b.txt': 'b' - }, writer, packageGraph: packageGraph); + }, readerWriter, packageGraph: packageGraph); var results = StreamQueue(buildState.buildResults); var result = await results.next; // Should ignore the files under the `b` package, even though they // match the input set. - checkBuild(result, outputs: {'a|web/a.txt.copy': 'a'}, writer: writer); + checkBuild(result, + outputs: {'a|web/a.txt.copy': 'a'}, writer: readerWriter); - await writer.writeAsString(makeAssetId('a|web/a.txt'), 'b'); - await writer.writeAsString(makeAssetId('b|web/b.txt'), 'c'); + await readerWriter.writeAsString(makeAssetId('a|web/a.txt'), 'b'); + await readerWriter.writeAsString(makeAssetId('b|web/b.txt'), 'c'); // Have to manually notify here since the path isn't standard. FakeWatcher.notifyWatchers(WatchEvent( ChangeType.MODIFY, path.absolute('a', 'b', 'web', 'a.txt'))); @@ -334,15 +362,20 @@ void main() { result = await results.next; // Ignores the modification under the `b` package, even though it // matches the input set. - checkBuild(result, outputs: {'a|web/a.txt.copy': 'b'}, writer: writer); + checkBuild(result, + outputs: {'a|web/a.txt.copy': 'b'}, writer: readerWriter); }); test('rebuilds on file updates during first build', () async { var blocker = Completer(); var buildAction = applyToRoot(TestBuilder(extraWork: (_, __) => blocker.future)); - var buildState = - await startWatch([buildAction], {'a|web/a.txt': 'a'}, writer); + var buildState = await startWatch( + [buildAction], + {'a|web/a.txt': 'a'}, + readerWriter, + packageGraph: packageGraph, + ); var results = StreamQueue(buildState.buildResults); FakeWatcher.notifyWatchers( @@ -352,12 +385,14 @@ void main() { var result = await results.next; // TODO: Move this up above the call to notifyWatchers once // https://github.com/dart-lang/build/issues/526 is fixed. - await writer.writeAsString(makeAssetId('a|web/a.txt'), 'b'); + await readerWriter.writeAsString(makeAssetId('a|web/a.txt'), 'b'); - checkBuild(result, outputs: {'a|web/a.txt.copy': 'a'}, writer: writer); + checkBuild(result, + outputs: {'a|web/a.txt.copy': 'a'}, writer: readerWriter); result = await results.next; - checkBuild(result, outputs: {'a|web/a.txt.copy': 'b'}, writer: writer); + checkBuild(result, + outputs: {'a|web/a.txt.copy': 'b'}, writer: readerWriter); }); test( @@ -365,16 +400,20 @@ void main() { 'and ask you to restart', () async { var logs = []; var buildState = await startWatch( - [copyABuildApplication], {'a|web/a.txt': 'a'}, writer, - logLevel: Level.SEVERE, onLog: logs.add); + [copyABuildApplication], {'a|web/a.txt': 'a'}, readerWriter, + packageGraph: packageGraph, + logLevel: Level.SEVERE, + onLog: logs.add); var results = StreamQueue(buildState.buildResults); var result = await results.next; - checkBuild(result, outputs: {'a|web/a.txt.copy': 'a'}, writer: writer); + checkBuild(result, + outputs: {'a|web/a.txt.copy': 'a'}, writer: readerWriter); var newConfig = Map.of(_packageConfig); newConfig['extra'] = 'stuff'; - await writer.writeAsString(packageConfigId, jsonEncode(newConfig)); + await readerWriter.writeAsString( + packageConfigId, jsonEncode(newConfig)); expect(await results.hasNext, isFalse); expect(logs, hasLength(1)); @@ -388,17 +427,20 @@ void main() { () async { var logs = []; var buildState = await startWatch( - [copyABuildApplication], {'a|web/a.txt': 'a'}, writer, - logLevel: Level.SEVERE, onLog: logs.add); + [copyABuildApplication], {'a|web/a.txt': 'a'}, readerWriter, + packageGraph: packageGraph, + logLevel: Level.SEVERE, + onLog: logs.add); buildState.buildResults .handleError((Object e, StackTrace s) => print('$e\n$s')); buildState.buildResults.listen(print); var results = StreamQueue(buildState.buildResults); var result = await results.next; - checkBuild(result, outputs: {'a|web/a.txt.copy': 'a'}, writer: writer); + checkBuild(result, + outputs: {'a|web/a.txt.copy': 'a'}, writer: readerWriter); - await writer.delete(packageConfigId); + await readerWriter.delete(packageConfigId); // Wait for it to try reading the file twice to ensure it will retry. await _readerForState[buildState]! @@ -409,7 +451,8 @@ void main() { var newConfig = Map.of(_packageConfig); newConfig['extra'] = 'stuff'; - await writer.writeAsString(packageConfigId, jsonEncode(newConfig)); + await readerWriter.writeAsString( + packageConfigId, jsonEncode(newConfig)); expect(await results.hasNext, isFalse); expect(logs, hasLength(1)); @@ -431,7 +474,7 @@ void main() { setUp(() async { logs = []; var buildState = await startWatch( - [copyABuildApplication], {}, writer, + [copyABuildApplication], {}, readerWriter, logLevel: Level.SEVERE, onLog: logs.add, packageGraph: packageGraph); @@ -440,7 +483,7 @@ void main() { }); test('to the root package', () async { - await writer.writeAsString( + await readerWriter.writeAsString( AssetId('a', 'build.yaml'), '# New build.yaml file'); expect(await results.hasNext, isTrue); var next = await results.next; @@ -452,7 +495,7 @@ void main() { }); test('to a dependency', () async { - await writer.writeAsString( + await readerWriter.writeAsString( AssetId('b', 'build.yaml'), '# New build.yaml file'); expect(await results.hasNext, isTrue); @@ -465,7 +508,7 @@ void main() { }); test('.build.yaml', () async { - await writer.writeAsString( + await readerWriter.writeAsString( AssetId('a', 'b.build.yaml'), '# New b.build.yaml file'); expect(await results.hasNext, isTrue); var next = await results.next; @@ -481,7 +524,7 @@ void main() { setUp(() async { logs = []; var buildState = await startWatch([copyABuildApplication], - {'a|build.yaml': '', 'b|build.yaml': ''}, writer, + {'a|build.yaml': '', 'b|build.yaml': ''}, readerWriter, logLevel: Level.SEVERE, onLog: logs.add, packageGraph: packageGraph); @@ -490,7 +533,7 @@ void main() { }); test('in the root package', () async { - await writer.writeAsString( + await readerWriter.writeAsString( AssetId('a', 'build.yaml'), '# Edited build.yaml file'); expect(await results.hasNext, isTrue); @@ -503,7 +546,7 @@ void main() { }); test('in a dependency', () async { - await writer.writeAsString( + await readerWriter.writeAsString( AssetId('b', 'build.yaml'), '# Edited build.yaml file'); expect(await results.hasNext, isTrue); @@ -520,7 +563,7 @@ void main() { setUp(() async { logs = []; var buildState = await startWatch([copyABuildApplication], - {'a|build.yaml': '', 'a|build.cool.yaml': ''}, writer, + {'a|build.yaml': '', 'a|build.cool.yaml': ''}, readerWriter, configKey: 'cool', logLevel: Level.SEVERE, onLog: logs.add, @@ -533,7 +576,7 @@ void main() { }); test('original is edited', () async { - await writer.writeAsString( + await readerWriter.writeAsString( AssetId('a', 'build.yaml'), '# Edited build.yaml file'); expect(await results.hasNext, isTrue); @@ -546,7 +589,7 @@ void main() { }); test('build..yaml in dependencies are ignored', () async { - await writer.writeAsString( + await readerWriter.writeAsString( AssetId('b', 'build.cool.yaml'), '# New build.yaml file'); await Future.delayed(_debounceDelay); @@ -556,7 +599,7 @@ void main() { }); test('build..yaml is edited', () async { - await writer.writeAsString(AssetId('a', 'build.cool.yaml'), + await readerWriter.writeAsString(AssetId('a', 'build.cool.yaml'), '# Edited build.cool.yaml file'); expect(await results.hasNext, isTrue); @@ -574,25 +617,29 @@ void main() { group('file updates to same contents', () { test('does not rebuild', () async { var runCount = 0; - var buildState = await startWatch([ - applyToRoot(TestBuilder( - buildExtensions: appendExtension('.copy', from: '.txt'), - build: (buildStep, _) { - runCount++; - buildStep.writeAsString(buildStep.inputId.addExtension('.copy'), - buildStep.readAsString(buildStep.inputId)); - throw StateError('Fail'); - })) - ], { - 'a|web/a.txt': 'a' - }, writer); + var buildState = await startWatch( + [ + applyToRoot(TestBuilder( + buildExtensions: appendExtension('.copy', from: '.txt'), + build: (buildStep, _) { + runCount++; + buildStep.writeAsString( + buildStep.inputId.addExtension('.copy'), + buildStep.readAsString(buildStep.inputId)); + throw StateError('Fail'); + })) + ], + {'a|web/a.txt': 'a'}, + readerWriter, + packageGraph: packageGraph, + ); var results = StreamQueue(buildState.buildResults); var result = await results.next; expect(runCount, 1); - checkBuild(result, status: BuildStatus.failure, writer: writer); + checkBuild(result, status: BuildStatus.failure, writer: readerWriter); - await writer.writeAsString(makeAssetId('a|web/a.txt'), 'a'); + await readerWriter.writeAsString(makeAssetId('a|web/a.txt'), 'a'); // Wait for the `_debounceDelay * 4` before terminating to // give it a chance to pick up the change. @@ -611,21 +658,25 @@ void main() { buildExtensions: appendExtension('.copy', from: '.copy'))) ]; - var buildState = - await startWatch(buildActions, {'a|web/a.txt': 'a'}, writer); + var buildState = await startWatch( + buildActions, + {'a|web/a.txt': 'a'}, + readerWriter, + packageGraph: packageGraph, + ); var results = StreamQueue(buildState.buildResults); var result = await results.next; checkBuild(result, outputs: {'a|web/a.txt.copy': 'a', 'a|web/a.txt.copy.copy': 'a'}, - writer: writer); + writer: readerWriter); - await writer.writeAsString(makeAssetId('a|web/a.txt'), 'b'); + await readerWriter.writeAsString(makeAssetId('a|web/a.txt'), 'b'); result = await results.next; checkBuild(result, outputs: {'a|web/a.txt.copy': 'b', 'a|web/a.txt.copy.copy': 'b'}, - writer: writer); + writer: readerWriter); }); test('adds propagate through all phases', () async { @@ -635,25 +686,29 @@ void main() { buildExtensions: appendExtension('.copy', from: '.copy'))) ]; - var buildState = - await startWatch(buildActions, {'a|web/a.txt': 'a'}, writer); + var buildState = await startWatch( + buildActions, + {'a|web/a.txt': 'a'}, + readerWriter, + packageGraph: packageGraph, + ); var results = StreamQueue(buildState.buildResults); var result = await results.next; checkBuild(result, outputs: {'a|web/a.txt.copy': 'a', 'a|web/a.txt.copy.copy': 'a'}, - writer: writer); + writer: readerWriter); - await writer.writeAsString(makeAssetId('a|web/b.txt'), 'b'); + await readerWriter.writeAsString(makeAssetId('a|web/b.txt'), 'b'); result = await results.next; checkBuild(result, outputs: {'a|web/b.txt.copy': 'b', 'a|web/b.txt.copy.copy': 'b'}, - writer: writer); + writer: readerWriter); // Previous outputs should still exist. - expect(writer.assets[makeAssetId('a|web/a.txt.copy')], + expect(readerWriter.assets[makeAssetId('a|web/a.txt.copy')], decodedMatches('a')); - expect(writer.assets[makeAssetId('a|web/a.txt.copy.copy')], + expect(readerWriter.assets[makeAssetId('a|web/a.txt.copy.copy')], decodedMatches('a')); }); @@ -665,7 +720,11 @@ void main() { ]; var buildState = await startWatch( - buildActions, {'a|web/a.txt': 'a', 'a|web/b.txt': 'b'}, writer); + buildActions, + {'a|web/a.txt': 'a', 'a|web/b.txt': 'b'}, + readerWriter, + packageGraph: packageGraph, + ); var results = StreamQueue(buildState.buildResults); var result = await results.next; @@ -676,25 +735,26 @@ void main() { 'a|web/b.txt.copy': 'b', 'a|web/b.txt.copy.copy': 'b' }, - writer: writer); + writer: readerWriter); // Don't call writer.delete, that has side effects. - writer.assets.remove(makeAssetId('a|web/a.txt')); + readerWriter.assets.remove(makeAssetId('a|web/a.txt')); FakeWatcher.notifyWatchers( WatchEvent(ChangeType.REMOVE, path.absolute('a', 'web', 'a.txt'))); result = await results.next; // Shouldn't rebuild anything, no outputs. - checkBuild(result, outputs: {}, writer: writer); + checkBuild(result, outputs: {}, writer: readerWriter); // Derived outputs should no longer exist. - expect(writer.assets[makeAssetId('a|web/a.txt.copy')], isNull); - expect(writer.assets[makeAssetId('a|web/a.txt.copy.copy')], isNull); + expect(readerWriter.assets[makeAssetId('a|web/a.txt.copy')], isNull); + expect( + readerWriter.assets[makeAssetId('a|web/a.txt.copy.copy')], isNull); // Other outputs should still exist. - expect(writer.assets[makeAssetId('a|web/b.txt.copy')], + expect(readerWriter.assets[makeAssetId('a|web/b.txt.copy')], decodedMatches('b')); - expect(writer.assets[makeAssetId('a|web/b.txt.copy.copy')], + expect(readerWriter.assets[makeAssetId('a|web/b.txt.copy.copy')], decodedMatches('b')); }); @@ -705,8 +765,12 @@ void main() { buildExtensions: appendExtension('.copy', from: '.copy'))) ]; - var buildState = - await startWatch(buildActions, {'a|web/a.txt': 'a'}, writer); + var buildState = await startWatch( + buildActions, + {'a|web/a.txt': 'a'}, + readerWriter, + packageGraph: packageGraph, + ); var results = StreamQueue(buildState.buildResults); var result = await results.next; @@ -715,10 +779,10 @@ void main() { 'a|web/a.txt.copy': 'a', 'a|web/a.txt.copy.copy': 'a', }, - writer: writer); + writer: readerWriter); // Don't call writer.delete, that has side effects. - writer.assets.remove(makeAssetId('a|web/a.txt.copy')); + readerWriter.assets.remove(makeAssetId('a|web/a.txt.copy')); FakeWatcher.notifyWatchers(WatchEvent( ChangeType.REMOVE, path.absolute('a', 'web', 'a.txt.copy'))); @@ -729,7 +793,7 @@ void main() { outputs: { 'a|web/a.txt.copy': 'a', }, - writer: writer); + writer: readerWriter); }); }); @@ -743,16 +807,22 @@ void main() { ]; var buildState = await startWatch( - buildActions, {'a|web/file.a': 'a', 'a|web/file.b': 'b'}, writer); + buildActions, + {'a|web/file.a': 'a', 'a|web/file.b': 'b'}, + readerWriter, + packageGraph: packageGraph, + ); var results = StreamQueue(buildState.buildResults); var result = await results.next; - checkBuild(result, outputs: {'a|web/file.a.copy': 'b'}, writer: writer); + checkBuild(result, + outputs: {'a|web/file.a.copy': 'b'}, writer: readerWriter); - await writer.writeAsString(makeAssetId('a|web/file.b'), 'c'); + await readerWriter.writeAsString(makeAssetId('a|web/file.b'), 'c'); result = await results.next; - checkBuild(result, outputs: {'a|web/file.a.copy': 'c'}, writer: writer); + checkBuild(result, + outputs: {'a|web/file.a.copy': 'c'}, writer: readerWriter); }); test( @@ -767,19 +837,23 @@ void main() { ]; var buildState = await startWatch( - buildActions, {'a|web/file.a': 'a', 'a|web/file.b': 'b'}, writer); + buildActions, + {'a|web/file.a': 'a', 'a|web/file.b': 'b'}, + readerWriter, + packageGraph: packageGraph, + ); var results = StreamQueue(buildState.buildResults); var result = await results.next; checkBuild(result, outputs: {'a|web/file.a.copy': 'a', 'a|web/file.a.copy.copy': 'b'}, - writer: writer); + writer: readerWriter); - await writer.writeAsString(makeAssetId('a|web/file.b'), 'c'); + await readerWriter.writeAsString(makeAssetId('a|web/file.b'), 'c'); result = await results.next; checkBuild(result, - outputs: {'a|web/file.a.copy.copy': 'c'}, writer: writer); + outputs: {'a|web/file.a.copy.copy': 'c'}, writer: readerWriter); }); }); }); @@ -790,20 +864,16 @@ StreamController? _terminateWatchController; /// Start watching files and running builds. Future startWatch(List builders, - Map inputs, InMemoryRunnerAssetWriter writer, - {PackageGraph? packageGraph, + Map inputs, InMemoryRunnerAssetReaderWriter readerWriter, + {required PackageGraph packageGraph, Map overrideBuildConfig = const {}, void Function(LogRecord)? onLog, Level logLevel = Level.OFF, String? configKey}) async { onLog ??= (_) {}; inputs.forEach((serializedId, contents) { - writer.writeAsString(makeAssetId(serializedId), contents); + readerWriter.writeAsString(makeAssetId(serializedId), contents); }); - packageGraph ??= - buildPackageGraph({rootPackage('a', path: path.absolute('a')): []}); - final reader = InMemoryRunnerAssetReader.shareAssetCache(writer.assets, - rootPackage: packageGraph.root.name); FakeWatcher watcherFactory(String path) => FakeWatcher(path); var state = await watch_impl.watch(builders, @@ -812,15 +882,15 @@ Future startWatch(List builders, debounceDelay: _debounceDelay, directoryWatcherFactory: watcherFactory, overrideBuildConfig: overrideBuildConfig, - reader: reader, - writer: writer, + reader: readerWriter, + writer: readerWriter, packageGraph: packageGraph, terminateEventStream: _terminateWatchController!.stream, logLevel: logLevel, onLog: onLog, skipBuildScriptCheck: true); // Some tests need access to `reader` so we expose it through an expando. - _readerForState[state] = reader; + _readerForState[state] = readerWriter; return state; } @@ -844,4 +914,4 @@ const _packageConfig = { /// Store the private in memory asset reader for a given [BuildState] object /// here so we can get access to it. -final _readerForState = Expando(); +final _readerForState = Expando(); diff --git a/build_runner/test/server/asset_handler_test.dart b/build_runner/test/server/asset_handler_test.dart index 55754c50f..579859534 100644 --- a/build_runner/test/server/asset_handler_test.dart +++ b/build_runner/test/server/asset_handler_test.dart @@ -19,13 +19,13 @@ import 'package:test/test.dart'; void main() { late AssetHandler handler; late FinalizedReader reader; - late InMemoryRunnerAssetReader delegate; + late InMemoryRunnerAssetReaderWriter delegate; late AssetGraph graph; setUp(() async { graph = await AssetGraph.build([], {}, {}, buildPackageGraph({rootPackage('a'): []}), FakeAssetReader()); - delegate = InMemoryRunnerAssetReader(); + delegate = InMemoryRunnerAssetReaderWriter(); final packageGraph = buildPackageGraph({rootPackage('a'): []}); reader = FinalizedReader( delegate, diff --git a/build_runner/test/server/serve_handler_test.dart b/build_runner/test/server/serve_handler_test.dart index fb3b97658..7822ccf64 100644 --- a/build_runner/test/server/serve_handler_test.dart +++ b/build_runner/test/server/serve_handler_test.dart @@ -97,18 +97,19 @@ class FakeWebSocketChannel extends StreamChannelMixin void main() { late ServeHandler serveHandler; - late InMemoryRunnerAssetReader reader; + late InMemoryRunnerAssetReaderWriter readerWriter; late MockWatchImpl watchImpl; late AssetGraph assetGraph; setUp(() async { - reader = InMemoryRunnerAssetReader(); final packageGraph = buildPackageGraph({rootPackage('a'): []}); + readerWriter = + InMemoryRunnerAssetReaderWriter(rootPackage: packageGraph.root.name); assetGraph = await AssetGraph.build( - [], {}, {}, packageGraph, reader); + [], {}, {}, packageGraph, readerWriter); watchImpl = MockWatchImpl( Future.value(FinalizedReader( - reader, + readerWriter, assetGraph, await TargetGraph.forPackageGraph(packageGraph, defaultRootPackageSources: defaultRootPackageSources), @@ -127,7 +128,7 @@ void main() { node.deletedBy.add(node.id.addExtension('.post_anchor.1')); } assetGraph.add(node); - reader.cacheStringAsset(node.id, content); + readerWriter.cacheStringAsset(node.id, content); } test('can get handlers for a subdirectory', () async { diff --git a/build_runner/test/server/serve_integration_test.dart b/build_runner/test/server/serve_integration_test.dart index 5f13d6568..36b651916 100644 --- a/build_runner/test/server/serve_integration_test.dart +++ b/build_runner/test/server/serve_integration_test.dart @@ -21,8 +21,7 @@ import 'package:watcher/watcher.dart'; void main() { late FutureOr Function(Request) handler; - late InMemoryRunnerAssetReader reader; - late InMemoryRunnerAssetWriter writer; + late InMemoryRunnerAssetReaderWriter readerWriter; late StreamSubscription subscription; late Completer nextBuild; late StreamController terminateController; @@ -31,9 +30,7 @@ void main() { setUp(() async { final graph = buildPackageGraph({rootPackage('example', path: path): []}); - writer = InMemoryRunnerAssetWriter(); - reader = InMemoryRunnerAssetReader.shareAssetCache(writer.assets, - rootPackage: 'example') + readerWriter = InMemoryRunnerAssetReaderWriter(rootPackage: 'example') ..cacheStringAsset(AssetId('example', 'web/initial.txt'), 'initial') ..cacheStringAsset(AssetId('example', 'web/large.txt'), List.filled(10000, 'large').join('')) @@ -54,8 +51,8 @@ void main() { final server = await watch_impl.watch( [applyToRoot(const UppercaseBuilder())], packageGraph: graph, - reader: reader, - writer: writer, + reader: readerWriter, + writer: readerWriter, logLevel: Level.ALL, onLog: (record) => printOnFailure('[${record.level}] ' '${record.loggerName}: ${record.message}'), @@ -99,7 +96,8 @@ void main() { test('should serve built files', () async { final getHello = Uri.parse('http://localhost/initial.g.txt'); - reader.cacheStringAsset(AssetId('example', 'web/initial.g.txt'), 'INITIAL'); + readerWriter.cacheStringAsset( + AssetId('example', 'web/initial.g.txt'), 'INITIAL'); final response = await handler(Request('GET', getHello)); expect(await response.readAsString(), 'INITIAL'); }); @@ -112,7 +110,7 @@ void main() { test('should serve newly added files', () async { final getNew = Uri.parse('http://localhost/new.txt'); - reader.cacheStringAsset(AssetId('example', 'web/new.txt'), 'New'); + readerWriter.cacheStringAsset(AssetId('example', 'web/new.txt'), 'New'); await Future.value(); FakeWatcher.notifyWatchers( WatchEvent(ChangeType.ADD, '$path/web/new.txt'), @@ -124,7 +122,7 @@ void main() { test('should serve built newly added files', () async { final getNew = Uri.parse('http://localhost/new.g.txt'); - reader.cacheStringAsset(AssetId('example', 'web/new.txt'), 'New'); + readerWriter.cacheStringAsset(AssetId('example', 'web/new.txt'), 'New'); await Future.value(); FakeWatcher.notifyWatchers( WatchEvent(ChangeType.ADD, '$path/web/new.txt'), diff --git a/build_runner_core/CHANGELOG.md b/build_runner_core/CHANGELOG.md index 8528da5ce..132711b87 100644 --- a/build_runner_core/CHANGELOG.md +++ b/build_runner_core/CHANGELOG.md @@ -2,6 +2,7 @@ - Fix crash when running on assets ending in a dot. - Start using `package:build/src/internal.dart'. +- Use `build_test` 3.0.0. ## 8.0.0 diff --git a/build_runner_core/pubspec.yaml b/build_runner_core/pubspec.yaml index dc8b508fa..fcee2dde0 100644 --- a/build_runner_core/pubspec.yaml +++ b/build_runner_core/pubspec.yaml @@ -40,7 +40,7 @@ dev_dependencies: path: ../_test_common analyzer: '>=6.9.0 <8.0.0' build_runner: ^2.0.0 - build_test: ^2.0.0 + build_test: ^3.0.0-wip dart_flutter_team_lints: ^3.1.0 json_serializable: ^6.0.0 test: ^1.16.0 diff --git a/build_runner_core/test/asset/cache_test.dart b/build_runner_core/test/asset/cache_test.dart index 0a827afb6..93f193746 100644 --- a/build_runner_core/test/asset/cache_test.dart +++ b/build_runner_core/test/asset/cache_test.dart @@ -15,11 +15,11 @@ void main() { var assets = { fooTxt: 'bar', }; - late InMemoryRunnerAssetReader delegate; + late InMemoryRunnerAssetReaderWriter delegate; late CachingAssetReader reader; setUp(() { - delegate = InMemoryRunnerAssetReader(assets); + delegate = InMemoryRunnerAssetReaderWriter(sourceAssets: assets); reader = CachingAssetReader(delegate); }); diff --git a/build_runner_core/test/environment/create_merged_dir_test.dart b/build_runner_core/test/environment/create_merged_dir_test.dart index 06242523d..9b3e4d171 100644 --- a/build_runner_core/test/environment/create_merged_dir_test.dart +++ b/build_runner_core/test/environment/create_merged_dir_test.dart @@ -61,15 +61,15 @@ void main() { late Directory tmpDir; late Directory anotherTmpDir; late TestBuildEnvironment environment; - late InMemoryRunnerAssetReader assetReader; + late InMemoryRunnerAssetReaderWriter readerWriter; late OptionalOutputTracker optionalOutputTracker; late FinalizedAssetsView finalizedAssetsView; setUp(() async { - assetReader = InMemoryRunnerAssetReader(sources); - environment = TestBuildEnvironment(reader: assetReader); - graph = await AssetGraph.build( - phases, sources.keys.toSet(), {}, packageGraph, assetReader); + readerWriter = InMemoryRunnerAssetReaderWriter(sourceAssets: sources); + environment = TestBuildEnvironment(readerWriter: readerWriter); + graph = await AssetGraph.build(phases, sources.keys.toSet(), {}, + packageGraph, readerWriter); targetGraph = await TargetGraph.forPackageGraph(packageGraph, defaultRootPackageSources: defaultNonRootVisibleAssets); optionalOutputTracker = @@ -81,7 +81,7 @@ void main() { ..state = NodeState.upToDate ..wasOutput = true ..isFailure = false; - assetReader.cacheStringAsset(id, sources[node.primaryInput]!); + readerWriter.cacheStringAsset(id, sources[node.primaryInput]!); } tmpDir = await Directory.systemTemp.createTemp('build_tests'); anotherTmpDir = await Directory.systemTemp.createTemp('build_tests'); @@ -96,7 +96,7 @@ void main() { {BuildDirectory('', outputLocation: OutputLocation(tmpDir.path))}, packageGraph, environment, - assetReader, + readerWriter, finalizedAssetsView, false); expect(success, isTrue); @@ -113,7 +113,7 @@ void main() { {BuildDirectory('', outputLocation: OutputLocation(tmpDir.path))}, packageGraph, environment, - assetReader, + readerWriter, finalizedAssetsView, false); expect(success, isTrue); @@ -131,7 +131,7 @@ void main() { var success = await createMergedOutputDirectories({ BuildDirectory('', outputLocation: OutputLocation(tmpDir.path)), BuildDirectory('', outputLocation: OutputLocation(anotherTmpDir.path)) - }, packageGraph, environment, assetReader, finalizedAssetsView, false); + }, packageGraph, environment, readerWriter, finalizedAssetsView, false); expect(success, isTrue); _expectAllFiles(tmpDir); @@ -142,7 +142,7 @@ void main() { var success = await createMergedOutputDirectories({ BuildDirectory('web', outputLocation: OutputLocation(tmpDir.path)), BuildDirectory('foo', outputLocation: OutputLocation(tmpDir.path)) - }, packageGraph, environment, assetReader, finalizedAssetsView, false); + }, packageGraph, environment, readerWriter, finalizedAssetsView, false); expect(success, isFalse); expect(Directory(tmpDir.path).listSync(), isEmpty); }); @@ -152,7 +152,7 @@ void main() { {BuildDirectory('web'), BuildDirectory('foo')}, packageGraph, environment, - assetReader, + readerWriter, finalizedAssetsView, false); expect(success, isTrue); @@ -163,7 +163,7 @@ void main() { {BuildDirectory('web', outputLocation: OutputLocation(tmpDir.path))}, packageGraph, environment, - assetReader, + readerWriter, finalizedAssetsView, false); expect(success, isTrue); @@ -180,7 +180,7 @@ void main() { var success = await createMergedOutputDirectories({ BuildDirectory('no_assets_here', outputLocation: OutputLocation(tmpDir.path)) - }, packageGraph, environment, assetReader, finalizedAssetsView, false); + }, packageGraph, environment, readerWriter, finalizedAssetsView, false); expect(success, isFalse); expect(Directory(tmpDir.path).listSync(), isEmpty); }); @@ -190,7 +190,7 @@ void main() { {BuildDirectory('web', outputLocation: OutputLocation(tmpDir.path))}, packageGraph, environment, - assetReader, + readerWriter, finalizedAssetsView, false); expect(success, isTrue); @@ -203,7 +203,7 @@ void main() { BuildDirectory('web', outputLocation: OutputLocation(tmpDir.path)), BuildDirectory('foo', outputLocation: OutputLocation(anotherTmpDir.path)) - }, packageGraph, environment, assetReader, finalizedAssetsView, false); + }, packageGraph, environment, readerWriter, finalizedAssetsView, false); expect(success, isTrue); var webFiles = { @@ -223,7 +223,7 @@ void main() { {BuildDirectory('', outputLocation: OutputLocation(tmpDir.path))}, packageGraph, environment, - assetReader, + readerWriter, finalizedAssetsView, false); expect(success, isTrue); @@ -235,7 +235,7 @@ void main() { BuildDirectory('web', outputLocation: OutputLocation(tmpDir.path)), BuildDirectory('foo', outputLocation: OutputLocation(anotherTmpDir.path)) - }, packageGraph, environment, assetReader, finalizedAssetsView, false); + }, packageGraph, environment, readerWriter, finalizedAssetsView, false); expect(success, isTrue); var webFiles = { @@ -267,7 +267,7 @@ void main() { {BuildDirectory('', outputLocation: OutputLocation(tmpDir.path))}, packageGraph, environment, - assetReader, + readerWriter, finalizedAssetsView, false); expect(success, isTrue); @@ -285,7 +285,7 @@ void main() { {BuildDirectory('', outputLocation: OutputLocation(tmpDir.path))}, packageGraph, environment, - assetReader, + readerWriter, finalizedAssetsView, false); expect(success, isTrue); @@ -313,13 +313,13 @@ void main() { }); test('fails in non-interactive mode', () async { - environment = - TestBuildEnvironment(reader: assetReader, throwOnPrompt: true); + environment = TestBuildEnvironment( + readerWriter: readerWriter, throwOnPrompt: true); var success = await createMergedOutputDirectories( {BuildDirectory('', outputLocation: OutputLocation(tmpDir.path))}, packageGraph, environment, - assetReader, + readerWriter, finalizedAssetsView, false); expect(success, isFalse); @@ -331,7 +331,7 @@ void main() { {BuildDirectory('', outputLocation: OutputLocation(tmpDir.path))}, packageGraph, environment, - assetReader, + readerWriter, finalizedAssetsView, false); expect(success, isFalse, @@ -351,7 +351,7 @@ void main() { {BuildDirectory('', outputLocation: OutputLocation(tmpDir.path))}, packageGraph, environment, - assetReader, + readerWriter, finalizedAssetsView, false); expect(success, isTrue); @@ -367,7 +367,7 @@ void main() { {BuildDirectory('', outputLocation: OutputLocation(tmpDir.path))}, packageGraph, environment, - assetReader, + readerWriter, finalizedAssetsView, false); expect(success, isTrue); @@ -379,7 +379,7 @@ void main() { environment.nextPromptResponse = 1; var success = await createMergedOutputDirectories({ BuildDirectory('../', outputLocation: OutputLocation(tmpDir.path)) - }, packageGraph, environment, assetReader, finalizedAssetsView, false); + }, packageGraph, environment, readerWriter, finalizedAssetsView, false); expect(success, isFalse); }); @@ -389,7 +389,7 @@ void main() { {BuildDirectory('', outputLocation: OutputLocation(tmpDir.path))}, packageGraph, environment, - assetReader, + readerWriter, finalizedAssetsView, false); expect(success, isTrue); @@ -408,7 +408,7 @@ void main() { {BuildDirectory('', outputLocation: OutputLocation(tmpDir.path))}, packageGraph, environment, - assetReader, + readerWriter, finalizedAssetsView, false); expect(success, isTrue); @@ -423,7 +423,7 @@ void main() { {BuildDirectory('', outputLocation: OutputLocation(tmpDir.path))}, packageGraph, environment, - assetReader, + readerWriter, finalizedAssetsView, false); expect(success, isTrue); diff --git a/build_runner_core/test/generate/build_error_test.dart b/build_runner_core/test/generate/build_error_test.dart index 2fdd85397..d33ba0124 100644 --- a/build_runner_core/test/generate/build_error_test.dart +++ b/build_runner_core/test/generate/build_error_test.dart @@ -45,12 +45,9 @@ void main() { test('should fail if a severe was logged on a previous build', () async { var packageGraph = buildPackageGraph({rootPackage('a'): []}); - var writer = InMemoryRunnerAssetWriter(); - var reader = InMemoryRunnerAssetReader.shareAssetCache(writer.assets, - rootPackage: packageGraph.root.name); var builder = _LoggingBuilder(Level.SEVERE); var builders = [applyToRoot(builder)]; - await testBuilders( + final result = await testBuilders( builders, { 'a|lib/a.dart': '', @@ -60,27 +57,21 @@ void main() { status: BuildStatus.failure, outputs: { 'a|lib/a.dart.empty': '', - }, - reader: reader, - writer: writer); + }); await testBuilders(builders, {}, + resumeFrom: result, packageGraph: packageGraph, checkBuildStatus: true, status: BuildStatus.failure, - outputs: {}, - reader: reader, - writer: writer); + outputs: {}); }); test('should succeed if a severe log is fixed on a subsequent build', () async { var packageGraph = buildPackageGraph({rootPackage('a'): []}); - var writer = InMemoryRunnerAssetWriter(); - var reader = InMemoryRunnerAssetReader.shareAssetCache(writer.assets, - rootPackage: packageGraph.root.name); var builder = _LoggingBuilder(Level.SEVERE); var builders = [applyToRoot(builder)]; - await testBuilders( + final result = await testBuilders( builders, { 'a|lib/a.dart': '', @@ -90,23 +81,20 @@ void main() { status: BuildStatus.failure, outputs: { 'a|lib/a.dart.empty': '', - }, - reader: reader, - writer: writer); + }); builder.level = Level.WARNING; await testBuilders( builders, { 'a|lib/a.dart': 'changed', }, + resumeFrom: result, packageGraph: packageGraph, checkBuildStatus: true, status: BuildStatus.success, outputs: { 'a|lib/a.dart.empty': '', - }, - reader: reader, - writer: writer); + }); }); test('should fail if an exception is thrown', () async { diff --git a/build_runner_core/test/generate/build_test.dart b/build_runner_core/test/generate/build_test.dart index 560ff7c39..f55efc627 100644 --- a/build_runner_core/test/generate/build_test.dart +++ b/build_runner_core/test/generate/build_test.dart @@ -441,9 +441,6 @@ void main() { var firstBuilder = TestBuilder( buildExtensions: appendExtension('.exists', from: '.a'), build: writeCanRead(aTxtId)); - var writer = InMemoryRunnerAssetWriter(); - var reader = InMemoryRunnerAssetReader.shareAssetCache(writer.assets, - rootPackage: 'a'); var builders = [ applyToRoot(firstBuilder), applyToRoot(TestBuilder( @@ -453,10 +450,15 @@ void main() { )), ]; + // Do an first build so a reader is created. + final result = await testBuilders(builders, { + 'unused|lib/unused.a': '', + }); + // After the first builder runs, delete the asset from the reader and // allow the 2nd builder to run. - unawaited(firstBuilder.buildsCompleted.first.then((_) { - reader.assets.remove(aTxtId); + unawaited(firstBuilder.buildsCompleted.first.then((id) { + result.readerWriter.assets.remove(aTxtId); ready.complete(); })); @@ -466,17 +468,15 @@ void main() { 'a|lib/file.a': '', 'a|lib/file.b': '', }, + resumeFrom: result, outputs: { 'a|lib/file.a.exists': 'true', 'a|lib/file.b.exists': 'true', - }, - reader: reader, - writer: writer); + }); }); test('pre-existing outputs', () async { - var writer = InMemoryRunnerAssetWriter(); - await testBuilders([ + final result = await testBuilders([ copyABuilderApplication, applyToRoot(TestBuilder( buildExtensions: appendExtension('.clone', from: '.copy'))) @@ -486,11 +486,12 @@ void main() { }, outputs: { 'a|web/a.txt.copy': 'a', 'a|web/a.txt.copy.clone': 'a' - }, writer: writer, deleteFilesByDefault: true); + }, deleteFilesByDefault: true); var graphId = makeAssetId('a|$assetGraphPath'); - expect(writer.assets, contains(graphId)); - var cachedGraph = AssetGraph.deserialize(writer.assets[graphId]!); + expect(result.readerWriter.assets, contains(graphId)); + var cachedGraph = + AssetGraph.deserialize(result.readerWriter.assets[graphId]!); expect( cachedGraph.allNodes.map((node) => node.id), unorderedEquals([ @@ -519,26 +520,26 @@ void main() { }); test('previous outputs are cleaned up', () async { - final writer = InMemoryRunnerAssetWriter(); - await testBuilders([copyABuilderApplication], {'a|web/a.txt': 'a'}, - outputs: {'a|web/a.txt.copy': 'a'}, writer: writer); + final result = await testBuilders( + [copyABuilderApplication], {'a|web/a.txt': 'a'}, + outputs: {'a|web/a.txt.copy': 'a'}); var blockingCompleter = Completer(); var builder = TestBuilder( buildExtensions: appendExtension('.copy', from: '.txt'), extraWork: (_, __) => blockingCompleter.future); var done = testBuilders([applyToRoot(builder)], {'a|web/a.txt': 'b'}, - outputs: {'a|web/a.txt.copy': 'b'}, writer: writer); + resumeFrom: result, outputs: {'a|web/a.txt.copy': 'b'}); // Before the build starts we should still see the asset, we haven't // actually deleted it yet. var copyId = makeAssetId('a|web/a.txt.copy'); - expect(writer.assets, contains(copyId)); + expect(result.readerWriter.assets, contains(copyId)); // But we should delete it before actually running the builder. var inputId = makeAssetId('a|web/a.txt'); await builder.buildInputs.firstWhere((id) => id == inputId); - expect(writer.assets, isNot(contains(copyId))); + expect(result.readerWriter.assets, isNot(contains(copyId))); // Now let the build finish. blockingCompleter.complete(); @@ -546,27 +547,23 @@ void main() { }); test('does not build hidden non-lib assets by default', () async { - final writer = InMemoryRunnerAssetWriter(); final result = await testBuilders( [applyToRoot(testBuilder, hideOutput: true)], {'a|example/a.txt': 'a', 'a|lib/b.txt': 'b'}, checkBuildStatus: false, buildDirs: {BuildDirectory('web')}, - writer: writer, ); - checkBuild(result, - writer: writer, outputs: {r'$$a|lib/b.txt.copy': 'b'}); + checkBuild(result.buildResult, + writer: result.readerWriter, outputs: {r'$$a|lib/b.txt.copy': 'b'}); }); test('builds hidden asset forming a custom public source', () async { - final writer = InMemoryRunnerAssetWriter(); final result = await testBuilders( [applyToRoot(testBuilder, hideOutput: true)], {'a|include/a.txt': 'a', 'a|lib/b.txt': 'b'}, checkBuildStatus: false, buildDirs: {BuildDirectory('web')}, - writer: writer, overrideBuildConfig: { 'a': BuildConfig.fromMap('a', const [], { 'additional_public_assets': ['include/**'] @@ -575,8 +572,8 @@ void main() { ); checkBuild( - result, - writer: writer, + result.buildResult, + writer: result.readerWriter, outputs: { r'$$a|include/a.txt.copy': 'a', r'$$a|lib/b.txt.copy': 'b', @@ -773,13 +770,6 @@ void main() { }); test('Will not delete from non-root packages', () async { - var writer = InMemoryRunnerAssetWriter() - ..onDelete = (AssetId assetId) { - if (assetId.package != 'a') { - throw StateError('Should not delete outside of package:a, ' - 'tried to delete $assetId'); - } - }; await testBuilders( [ apply('', [(_) => TestBuilder()], toPackage('b'), @@ -790,8 +780,12 @@ void main() { 'a|.dart_tool/build/generated/b/lib/b.txt.copy': 'b' }, packageGraph: packageGraph, - writer: writer, - outputs: {r'$$b|lib/b.txt.copy': 'b'}); + outputs: {r'$$b|lib/b.txt.copy': 'b'}, onDelete: (AssetId assetId) { + if (assetId.package != 'a') { + throw StateError('Should not delete outside of package:a, ' + 'tried to delete $assetId'); + } + }); }); }); @@ -972,24 +966,19 @@ void main() { }); test('can output performance logs', () async { - var writer = InMemoryRunnerAssetWriter(); - var reader = InMemoryRunnerAssetReader.shareAssetCache(writer.assets, - rootPackage: 'a'); - await testBuilders( + final result = await testBuilders( [ apply('test_builder', [(_) => TestBuilder()], toRoot(), isOptional: false, hideOutput: false), ], {'a|web/a.txt': 'a'}, outputs: {'a|web/a.txt.copy': 'a'}, - writer: writer, - reader: reader, logPerformanceDir: 'perf', ); - var logs = await reader.findAssets(Glob('perf/**')).toList(); + var logs = await result.readerWriter.findAssets(Glob('perf/**')).toList(); expect(logs.length, 1); var perf = BuildPerformance.fromJson( - jsonDecode(await reader.readAsString(logs.first)) + jsonDecode(await result.readerWriter.readAsString(logs.first)) as Map); expect(perf.phases.length, 1); expect(perf.phases.first.builderKeys, equals(['test_builder'])); @@ -1077,8 +1066,7 @@ void main() { }); test('tracks dependency graph in a asset_graph.json file', () async { - final writer = InMemoryRunnerAssetWriter(); - await testBuilders([ + final result = await testBuilders([ requiresPostProcessBuilderApplication, postCopyABuilderApplication, ], { @@ -1089,18 +1077,19 @@ void main() { 'a|lib/b.txt.copy': 'b', r'$$a|web/a.txt.post': 'a', r'$$a|lib/b.txt.post': 'b', - }, writer: writer); + }); var graphId = makeAssetId('a|$assetGraphPath'); - expect(writer.assets, contains(graphId)); - var cachedGraph = AssetGraph.deserialize(writer.assets[graphId]!); + expect(result.readerWriter.assets, contains(graphId)); + var cachedGraph = + AssetGraph.deserialize(result.readerWriter.assets[graphId]!); var expectedGraph = await AssetGraph.build( [], {}, {makeAssetId('a|.dart_tool/package_config.json')}, buildPackageGraph({rootPackage('a'): []}), - InMemoryAssetReader(sourceAssets: writer.assets)); + InMemoryAssetReader(sourceAssets: result.readerWriter.assets)); // Source nodes var aSourceNode = makeAssetNode( @@ -1207,9 +1196,7 @@ void main() { test("builders reading their output don't cause self-referential nodes", () async { - final writer = InMemoryRunnerAssetWriter(); - - await testBuilders([ + final result = await testBuilders([ apply( '', [ @@ -1231,10 +1218,11 @@ void main() { 'a|lib/a.txt': 'a', }, outputs: { 'a|lib/a.txt.out': 'a' - }, writer: writer); + }); final graphId = makeAssetId('a|$assetGraphPath'); - final cachedGraph = AssetGraph.deserialize(writer.assets[graphId]!); + final cachedGraph = + AssetGraph.deserialize(result.readerWriter.assets[graphId]!); final outputId = AssetId('a', 'lib/a.txt.out'); final outputNode = cachedGraph.get(outputId) as GeneratedAssetNode; @@ -1243,38 +1231,36 @@ void main() { test('outputs from previous full builds shouldn\'t be inputs to later ones', () async { - final writer = InMemoryRunnerAssetWriter(); var inputs = {'a|web/a.txt': 'a', 'a|lib/b.txt': 'b'}; var outputs = { 'a|web/a.txt.copy': 'a', 'a|lib/b.txt.copy': 'b' }; // First run, nothing special. - await testBuilders([copyABuilderApplication], inputs, - outputs: outputs, writer: writer); + final result = + await testBuilders([copyABuilderApplication], inputs, outputs: outputs); // Second run, should have no outputs. await testBuilders([copyABuilderApplication], inputs, - outputs: {}, writer: writer); + outputs: {}, resumeFrom: result); }); test('can recover from a deleted asset_graph.json cache', () async { - final writer = InMemoryRunnerAssetWriter(); var inputs = {'a|web/a.txt': 'a', 'a|lib/b.txt': 'b'}; var outputs = { 'a|web/a.txt.copy': 'a', 'a|lib/b.txt.copy': 'b' }; // First run, nothing special. - await testBuilders([copyABuilderApplication], inputs, - outputs: outputs, writer: writer); + final result = + await testBuilders([copyABuilderApplication], inputs, outputs: outputs); // Delete the `asset_graph.json` file! var outputId = makeAssetId('a|$assetGraphPath'); - await writer.delete(outputId); + await result.readerWriter.delete(outputId); // Second run, should have no extra outputs. var done = testBuilders([copyABuilderApplication], inputs, - outputs: outputs, writer: writer); + outputs: outputs, resumeFrom: result); // Should block on user input. await Future.delayed(const Duration(seconds: 1)); // Now it should complete! @@ -1286,22 +1272,18 @@ void main() { var builders = [copyABuilderApplication]; // Initial build. - var writer = InMemoryRunnerAssetWriter(); - await testBuilders( - builders, - { - 'a|web/a.txt': 'a', - 'a|lib/b.txt': 'b', - }, - outputs: { - 'a|web/a.txt.copy': 'a', - 'a|lib/b.txt.copy': 'b', - }, - writer: writer); + final result = await testBuilders(builders, { + 'a|web/a.txt': 'a', + 'a|lib/b.txt': 'b', + }, outputs: { + 'a|web/a.txt.copy': 'a', + 'a|lib/b.txt.copy': 'b', + }); // Followup build with modified inputs. - var serializedGraph = writer.assets[makeAssetId('a|$assetGraphPath')]!; - writer.assets.clear(); + var serializedGraph = + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!; + result.readerWriter.assets.clear(); await testBuilders( builders, { @@ -1316,7 +1298,7 @@ void main() { 'a|web/a.txt.copy': 'a2', 'a|lib/c.txt.copy': 'c', }, - writer: writer); + resumeFrom: result); }); test('deleting only the second output of a builder causes it to rerun', @@ -1328,21 +1310,17 @@ void main() { ]; // Initial build. - var writer = InMemoryRunnerAssetWriter(); - await testBuilders( - builders, - { - 'a|lib/a.txt': 'a', - }, - outputs: { - 'a|lib/a.txt.1': 'a', - 'a|lib/a.txt.2': 'a', - }, - writer: writer); + final result = await testBuilders(builders, { + 'a|lib/a.txt': 'a', + }, outputs: { + 'a|lib/a.txt.1': 'a', + 'a|lib/a.txt.2': 'a', + }); // Followup build with the 2nd output missing. - var serializedGraph = writer.assets[makeAssetId('a|$assetGraphPath')]!; - writer.assets.clear(); + var serializedGraph = + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!; + result.readerWriter.assets.clear(); await testBuilders( builders, { @@ -1354,7 +1332,7 @@ void main() { 'a|lib/a.txt.1': 'a', 'a|lib/a.txt.2': 'a', }, - writer: writer); + resumeFrom: result); }); group('reportUnusedAssets', () { @@ -1378,22 +1356,18 @@ void main() { var builders = [applyToRoot(builder)]; // Initial build. - var writer = InMemoryRunnerAssetWriter(); - await testBuilders( - builders, - { - 'a|lib/a.txt': 'a', - 'a|lib/a.txt.used': 'b', - 'a|lib/a.txt.unused': 'c', - }, - outputs: { - 'a|lib/a.txt.copy': 'ab', - }, - writer: writer); + final result = await testBuilders(builders, { + 'a|lib/a.txt': 'a', + 'a|lib/a.txt.used': 'b', + 'a|lib/a.txt.unused': 'c', + }, outputs: { + 'a|lib/a.txt.copy': 'ab', + }); // Followup build with modified unused inputs should have no outputs. - var serializedGraph = writer.assets[makeAssetId('a|$assetGraphPath')]!; - writer.assets.clear(); + var serializedGraph = + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!; + result.readerWriter.assets.clear(); await testBuilders( builders, { @@ -1404,11 +1378,12 @@ void main() { 'a|$assetGraphPath': serializedGraph, }, outputs: {}, - writer: writer); + resumeFrom: result); // And now modify a real input. - serializedGraph = writer.assets[makeAssetId('a|$assetGraphPath')]!; - writer.assets.clear(); + serializedGraph = + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!; + result.readerWriter.assets.clear(); await testBuilders( builders, { @@ -1421,11 +1396,12 @@ void main() { outputs: { 'a|lib/a.txt.copy': 'ae', }, - writer: writer); + resumeFrom: result); // Finally modify the primary input. - serializedGraph = writer.assets[makeAssetId('a|$assetGraphPath')]!; - writer.assets.clear(); + serializedGraph = + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!; + result.readerWriter.assets.clear(); await testBuilders( builders, { @@ -1438,7 +1414,7 @@ void main() { outputs: { 'a|lib/a.txt.copy': 'fe', }, - writer: writer); + resumeFrom: result); }); test('allows marking the primary input as unused', () async { @@ -1454,21 +1430,17 @@ void main() { var builders = [applyToRoot(builder)]; // Initial build. - var writer = InMemoryRunnerAssetWriter(); - await testBuilders( - builders, - { - 'a|lib/a.txt': 'a', - 'a|lib/a.txt.used': '', - }, - outputs: { - 'a|lib/a.txt.copy': 'a', - }, - writer: writer); + final result = await testBuilders(builders, { + 'a|lib/a.txt': 'a', + 'a|lib/a.txt.used': '', + }, outputs: { + 'a|lib/a.txt.copy': 'a', + }); // Followup build with modified primary input should have no outputs. - var serializedGraph = writer.assets[makeAssetId('a|$assetGraphPath')]!; - writer.assets.clear(); + var serializedGraph = + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!; + result.readerWriter.assets.clear(); await testBuilders( builders, { @@ -1478,11 +1450,12 @@ void main() { 'a|$assetGraphPath': serializedGraph, }, outputs: {}, - writer: writer); + resumeFrom: result); // But modifying other inputs still causes a rebuild. - serializedGraph = writer.assets[makeAssetId('a|$assetGraphPath')]!; - writer.assets.clear(); + serializedGraph = + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!; + result.readerWriter.assets.clear(); await testBuilders( builders, { @@ -1494,7 +1467,7 @@ void main() { outputs: { 'a|lib/a.txt.copy': 'b', }, - writer: writer); + resumeFrom: result); }); test('marking the primary input as unused still tracks if it is deleted', @@ -1509,20 +1482,16 @@ void main() { var builders = [applyToRoot(builder)]; // Initial build. - var writer = InMemoryRunnerAssetWriter(); - await testBuilders( - builders, - { - 'a|lib/a.txt': 'a', - }, - outputs: { - 'a|lib/a.txt.copy': 'a', - }, - writer: writer); + final result = await testBuilders(builders, { + 'a|lib/a.txt': 'a', + }, outputs: { + 'a|lib/a.txt.copy': 'a', + }); // Delete the primary input, the output shoud still be deleted - var serializedGraph = writer.assets[makeAssetId('a|$assetGraphPath')]!; - writer.assets.clear(); + var serializedGraph = + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!; + result.readerWriter.assets.clear(); await testBuilders( builders, { @@ -1530,10 +1499,10 @@ void main() { 'a|$assetGraphPath': serializedGraph, }, outputs: {}, - writer: writer); + resumeFrom: result); var graph = AssetGraph.deserialize( - writer.assets[makeAssetId('a|$assetGraphPath')]!); + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!); expect(graph.get(makeAssetId('a|lib/a.txt.copy')), isNull); }); }); @@ -1546,21 +1515,17 @@ void main() { ]; // Initial build. - var writer = InMemoryRunnerAssetWriter(); - await testBuilders( - builders, - { - 'a|lib/a.txt': 'a', - }, - outputs: { - 'a|lib/a.txt.copy': 'a', - 'a|lib/a.txt.clone': 'a', - }, - writer: writer); + final result = await testBuilders(builders, { + 'a|lib/a.txt': 'a', + }, outputs: { + 'a|lib/a.txt.copy': 'a', + 'a|lib/a.txt.clone': 'a', + }); // Followup build with deleted input + cached graph. - var serializedGraph = writer.assets[makeAssetId('a|$assetGraphPath')]!; - writer.assets.clear(); + var serializedGraph = + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!; + result.readerWriter.assets.clear(); await testBuilders( builders, { @@ -1569,32 +1534,32 @@ void main() { 'a|$assetGraphPath': serializedGraph, }, outputs: {}, - writer: writer); + resumeFrom: result); /// Should be deleted using the writer, and removed from the new graph. var newGraph = AssetGraph.deserialize( - writer.assets[makeAssetId('a|$assetGraphPath')]!); + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!); var aNodeId = makeAssetId('a|lib/a.txt'); var aCopyNodeId = makeAssetId('a|lib/a.txt.copy'); var aCloneNodeId = makeAssetId('a|lib/a.txt.copy.clone'); expect(newGraph.contains(aNodeId), isFalse); expect(newGraph.contains(aCopyNodeId), isFalse); expect(newGraph.contains(aCloneNodeId), isFalse); - expect(writer.assets.containsKey(aNodeId), isFalse); - expect(writer.assets.containsKey(aCopyNodeId), isFalse); - expect(writer.assets.containsKey(aCloneNodeId), isFalse); + expect(result.readerWriter.assets.containsKey(aNodeId), isFalse); + expect(result.readerWriter.assets.containsKey(aCopyNodeId), isFalse); + expect(result.readerWriter.assets.containsKey(aCloneNodeId), isFalse); }); test('no outputs if no changed sources', () async { var builders = [copyABuilderApplication]; // Initial build. - var writer = InMemoryRunnerAssetWriter(); - await testBuilders(builders, {'a|web/a.txt': 'a'}, - outputs: {'a|web/a.txt.copy': 'a'}, writer: writer); + final result = await testBuilders(builders, {'a|web/a.txt': 'a'}, + outputs: {'a|web/a.txt.copy': 'a'}); // Followup build with same sources + cached graph. - var serializedGraph = writer.assets[makeAssetId('a|$assetGraphPath')]!; + var serializedGraph = + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!; await testBuilders(builders, { 'a|web/a.txt': 'a', 'a|web/a.txt.copy': 'a', @@ -1608,15 +1573,14 @@ void main() { ]; // Initial build. - var writer = InMemoryRunnerAssetWriter(); - await testBuilders(builders, {'a|web/a.txt': 'a'}, + final result = await testBuilders(builders, {'a|web/a.txt': 'a'}, // Note that `testBuilders` converts generated cache dir paths to the // original ones for matching. - outputs: {r'$$a|web/a.txt.copy': 'a'}, - writer: writer); + outputs: {r'$$a|web/a.txt.copy': 'a'}); // Followup build with same sources + cached graph. - var serializedGraph = writer.assets[makeAssetId('a|$assetGraphPath')]!; + var serializedGraph = + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!; await testBuilders(builders, { 'a|web/a.txt': 'a', 'a|web/a.txt.copy': 'a', @@ -1626,8 +1590,7 @@ void main() { test('inputs/outputs are updated if they change', () async { // Initial build. - var writer = InMemoryRunnerAssetWriter(); - await testBuilders([ + final result = await testBuilders([ applyToRoot(TestBuilder( buildExtensions: appendExtension('.copy', from: '.a'), build: copyFrom(makeAssetId('a|lib/file.b')))), @@ -1637,12 +1600,13 @@ void main() { 'a|lib/file.c': 'c', }, outputs: { 'a|lib/file.a.copy': 'b', - }, writer: writer); + }); // Followup build with same sources + cached graph, but configure the // builder to read a different file. - var serializedGraph = writer.assets[makeAssetId('a|$assetGraphPath')]!; - writer.assets.clear(); + var serializedGraph = + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!; + result.readerWriter.assets.clear(); await testBuilders([ applyToRoot(TestBuilder( @@ -1658,11 +1622,11 @@ void main() { 'a|$assetGraphPath': serializedGraph, }, outputs: { 'a|lib/file.a.copy': 'c', - }, writer: writer); + }, resumeFrom: result); // Read cached graph and validate. var graph = AssetGraph.deserialize( - writer.assets[makeAssetId('a|$assetGraphPath')]!); + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!); var outputNode = graph.get(makeAssetId('a|lib/file.a.copy')) as GeneratedAssetNode; var fileANode = graph.get(makeAssetId('a|lib/file.a'))!; @@ -1684,23 +1648,19 @@ void main() { ]; // Initial build. - var writer = InMemoryRunnerAssetWriter(); - await testBuilders( - builders, - { - 'a|lib/file.a': 'a', - 'a|lib/file.b': 'b', - }, - outputs: { - 'a|lib/file.a.copy': 'b', - 'a|lib/file.a.copy.copy': 'b', - }, - writer: writer); + final result = await testBuilders(builders, { + 'a|lib/file.a': 'a', + 'a|lib/file.b': 'b', + }, outputs: { + 'a|lib/file.a.copy': 'b', + 'a|lib/file.a.copy.copy': 'b', + }); // Modify the primary input of `file.a.copy`, but its output doesn't // change so `file.a.copy.copy` shouldn't be rebuilt. - var serializedGraph = writer.assets[makeAssetId('a|$assetGraphPath')]!; - writer.assets.clear(); + var serializedGraph = + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!; + result.readerWriter.assets.clear(); await testBuilders( builders, { @@ -1713,17 +1673,16 @@ void main() { outputs: { 'a|lib/file.a.copy': 'b', }, - writer: writer); + resumeFrom: result); }); test('no implicit dependency on primary input contents', () async { var builders = [applyToRoot(SiblingCopyBuilder())]; // Initial build. - var writer = InMemoryRunnerAssetWriter(); - await testBuilders( + final result = await testBuilders( builders, {'a|web/a.txt': 'a', 'a|web/a.txt.sibling': 'sibling'}, - outputs: {'a|web/a.txt.new': 'sibling'}, writer: writer); + outputs: {'a|web/a.txt.new': 'sibling'}); // Followup build with cached graph and a changed primary input, but the // actual file that was read has not changed. @@ -1731,7 +1690,8 @@ void main() { 'a|web/a.txt': 'b', 'a|web/a.txt.sibling': 'sibling', 'a|web/a.txt.new': 'sibling', - 'a|$assetGraphPath': writer.assets[makeAssetId('a|$assetGraphPath')]!, + 'a|$assetGraphPath': + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!, }, outputs: {}); // And now try modifying the sibling to make sure that still works. @@ -1739,7 +1699,8 @@ void main() { 'a|web/a.txt': 'b', 'a|web/a.txt.sibling': 'new!', 'a|web/a.txt.new': 'sibling', - 'a|$assetGraphPath': writer.assets[makeAssetId('a|$assetGraphPath')]!, + 'a|$assetGraphPath': + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!, }, outputs: { 'a|web/a.txt.new': 'new!', }); @@ -1766,16 +1727,15 @@ void main() { throw StateError('Fails always'); })), ]; - var writer = InMemoryRunnerAssetWriter(); - await testBuilders( + final result = await testBuilders( builders, {'a|lib/a.source': 'true'}, status: BuildStatus.failure, - writer: writer, ); - var serializedGraph = writer.assets[makeAssetId('a|$assetGraphPath')]!; - writer.assets.clear(); + var serializedGraph = + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!; + result.readerWriter.assets.clear(); await testBuilders( builders, @@ -1784,7 +1744,7 @@ void main() { 'a|$assetGraphPath': serializedGraph, }, outputs: {}, - writer: writer); + resumeFrom: result); }); test('the entrypoint cannot be read by a builder', () async { @@ -1841,16 +1801,15 @@ void main() { buildStep.inputId.changeExtension('.g3'), ''); })), ]; - var writer = InMemoryRunnerAssetWriter(); - await testBuilders( + final result = await testBuilders( builders, {'a|web/a.source': 'true'}, status: BuildStatus.failure, - writer: writer, ); - var serializedGraph = writer.assets[makeAssetId('a|$assetGraphPath')]!; - writer.assets.clear(); + var serializedGraph = + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!; + result.readerWriter.assets.clear(); await testBuilders( builders, @@ -1863,10 +1822,11 @@ void main() { 'a|web/a.g2': '', 'a|web/a.g3': '', }, - writer: writer); + resumeFrom: result); - serializedGraph = writer.assets[makeAssetId('a|$assetGraphPath')]!; - writer.assets.clear(); + serializedGraph = + result.readerWriter.assets[makeAssetId('a|$assetGraphPath')]!; + result.readerWriter.assets.clear(); // Make sure if we mark the original node as a failure again, that we // also mark all its primary outputs as failures. @@ -1878,10 +1838,10 @@ void main() { }, outputs: {}, status: BuildStatus.failure, - writer: writer); + resumeFrom: result); - var finalGraph = - AssetGraph.deserialize(writer.assets[AssetId('a', assetGraphPath)]!); + var finalGraph = AssetGraph.deserialize( + result.readerWriter.assets[AssetId('a', assetGraphPath)]!); for (var i = 1; i < 4; i++) { var node = finalGraph.get(AssetId('a', 'web/a.g$i')) as GeneratedAssetNode; diff --git a/build_runner_core/test/generate/custom_generated_dir_test.dart b/build_runner_core/test/generate/custom_generated_dir_test.dart index e6a38f7c7..7e7d74d99 100644 --- a/build_runner_core/test/generate/custom_generated_dir_test.dart +++ b/build_runner_core/test/generate/custom_generated_dir_test.dart @@ -20,8 +20,7 @@ void main() { }); test('can output files to a custom generated dir', () async { - var writer = InMemoryRunnerAssetWriter(); - await testBuilders( + final result = await testBuilders( [ applyToRoot( TestBuilder( @@ -33,10 +32,9 @@ void main() { outputs: { r'$$a|lib/a.txt.copy': 'a', }, - expectedGeneratedDir: customGeneratedDir, - writer: writer); + expectedGeneratedDir: customGeneratedDir); expect( - writer.assets[AssetId( + result.readerWriter.assets[AssetId( 'a', '.dart_tool/build/$customGeneratedDir/a/lib/a.txt.copy')], isNotNull); }); diff --git a/build_test/CHANGELOG.md b/build_test/CHANGELOG.md index 5656821b3..3a33f57f3 100644 --- a/build_test/CHANGELOG.md +++ b/build_test/CHANGELOG.md @@ -1,5 +1,11 @@ -## 2.2.4-wip - +## 3.0.0-wip + +- `InMemoryAssetReader` and `InMemoryAssetWriter` implementations are merged + into `InMemoryAssetReaderWriter` with shared state; it implements both + `InMemoryAssetReader` and `InMemoryAssetWriter`. +- Breaking change: `testBuilder` no longer accepts a `reader` and a `writer`. + Instead it returns a `TestBuilderResult` with the `InMemoryAssetReaderWriter` + that was used. - Support checks on reader state after a build action in `resolveSources`. - Start using `package:build/src/internal.dart`. diff --git a/build_test/lib/src/in_memory_reader.dart b/build_test/lib/src/in_memory_reader.dart index 73c49511e..19c5cbbde 100644 --- a/build_test/lib/src/in_memory_reader.dart +++ b/build_test/lib/src/in_memory_reader.dart @@ -10,12 +10,19 @@ import 'package:glob/glob.dart'; /// An [AssetReader] that records which assets have been read to [assetsRead]. abstract class RecordingAssetReader implements AssetReader { - Iterable get assetsRead; + Set get assetsRead; } -/// An implementation of [AssetReader] with primed in-memory assets. -class InMemoryAssetReader extends AssetReader - implements MultiPackageAssetReader, RecordingAssetReader, AssetReaderState { +/// An implementation of [AssetWriter] that records outputs to [assets]. +abstract class RecordingAssetWriter implements AssetWriter { + Map> get assets; +} + +/// An implementation of [AssetReader] and [AssetWriter] with primed in-memory +/// assets. +class InMemoryAssetReaderWriter extends AssetReader + implements InMemoryAssetReader, InMemoryAssetWriter { + @override final Map> assets; final String? rootPackage; @@ -31,11 +38,12 @@ class InMemoryAssetReader extends AssetReader /// bytes. /// /// May optionally define a [rootPackage], which is required for some APIs. - InMemoryAssetReader({Map? sourceAssets, this.rootPackage}) + InMemoryAssetReaderWriter( + {Map? sourceAssets, this.rootPackage}) : assets = _assetsAsBytes(sourceAssets); /// Create a new asset reader backed by [assets]. - InMemoryAssetReader.shareAssetCache(this.assets, {this.rootPackage}); + InMemoryAssetReaderWriter.shareAssetCache(this.assets, {this.rootPackage}); static Map> _assetsAsBytes(Map? assets) { if (assets == null || assets.isEmpty) { @@ -86,12 +94,69 @@ class InMemoryAssetReader extends AssetReader .where((id) => id.package == package && glob.matches(id.path))); } + @override void cacheBytesAsset(AssetId id, List bytes) { assets[id] = bytes; } + @override void cacheStringAsset(AssetId id, String contents, {Encoding? encoding}) { encoding ??= utf8; assets[id] = encoding.encode(contents); } + + @override + Future writeAsBytes(AssetId id, List bytes) async { + assets[id] = bytes; + } + + @override + Future writeAsString(AssetId id, String contents, + {Encoding encoding = utf8}) async { + assets[id] = encoding.encode(contents); + } +} + +/// An implementation of [AssetReader] with primed in-memory assets. +abstract class InMemoryAssetReader + implements + AssetReader, + MultiPackageAssetReader, + RecordingAssetReader, + AssetReaderState { + abstract final Map> assets; + + /// Create a new asset reader that contains [sourceAssets]. + /// + /// Any strings in [sourceAssets] will be converted into a `List` of + /// bytes. + /// + /// May optionally define a [rootPackage], which is required for some APIs. + factory InMemoryAssetReader( + {Map? sourceAssets, String? rootPackage}) => + InMemoryAssetReaderWriter( + sourceAssets: sourceAssets, rootPackage: rootPackage); + + /// Create a new asset reader backed by [assets]. + // InMemoryAssetReader.shareAssetCache(this.assets, {this.rootPackage}); + + void cacheBytesAsset(AssetId id, List bytes); + + void cacheStringAsset(AssetId id, String contents, {Encoding? encoding}); +} + +/// An implementation of [AssetWriter] that writes outputs to memory. +abstract class InMemoryAssetWriter implements RecordingAssetWriter { + factory InMemoryAssetWriter() => InMemoryAssetReaderWriter(); + + @override + Future writeAsBytes(AssetId id, List bytes) async { + assets[id] = bytes; + } + + @override + Future writeAsString(AssetId id, String contents, + {Encoding encoding = utf8}) async { + assets[id] = encoding.encode(contents); + } } diff --git a/build_test/lib/src/in_memory_writer.dart b/build_test/lib/src/in_memory_writer.dart index 3fb234bff..92046e5fb 100644 --- a/build_test/lib/src/in_memory_writer.dart +++ b/build_test/lib/src/in_memory_writer.dart @@ -1,30 +1,5 @@ // Copyright (c) 2016, 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:convert'; -import 'package:build/build.dart'; - -/// An implementation of [AssetWriter] that records outputs to [assets]. -abstract class RecordingAssetWriter implements AssetWriter { - Map> get assets; -} - -/// An implementation of [AssetWriter] that writes outputs to memory. -class InMemoryAssetWriter implements RecordingAssetWriter { - @override - final Map> assets = {}; - - InMemoryAssetWriter(); - - @override - Future writeAsBytes(AssetId id, List bytes) async { - assets[id] = bytes; - } - - @override - Future writeAsString(AssetId id, String contents, - {Encoding encoding = utf8}) async { - assets[id] = encoding.encode(contents); - } -} +export 'in_memory_reader.dart' show InMemoryAssetWriter, RecordingAssetWriter; diff --git a/build_test/lib/src/resolve_source.dart b/build_test/lib/src/resolve_source.dart index 3dc132e20..1e1bded09 100644 --- a/build_test/lib/src/resolve_source.dart +++ b/build_test/lib/src/resolve_source.dart @@ -11,7 +11,6 @@ import 'package:build_resolvers/build_resolvers.dart'; import 'package:package_config/package_config.dart'; import 'in_memory_reader.dart'; -import 'in_memory_writer.dart'; import 'multi_asset_reader.dart'; import 'package_reader.dart'; @@ -195,7 +194,7 @@ Future _resolveAssets( } inputAssets[assetId] = assetValue; })); - final inMemory = InMemoryAssetReader( + final inMemory = InMemoryAssetReaderWriter( sourceAssets: inputAssets, rootPackage: rootPackage, ); @@ -218,7 +217,7 @@ Future _resolveAssets( resolveBuilder, inputAssets.keys, MultiAssetReader([inMemory, assetReader]), - InMemoryAssetWriter(), + inMemory, resolvers, ).catchError((_) {})); final result = await resolveBuilder.onDone.future; diff --git a/build_test/lib/src/test_builder.dart b/build_test/lib/src/test_builder.dart index 054d1173b..c654d47e3 100644 --- a/build_test/lib/src/test_builder.dart +++ b/build_test/lib/src/test_builder.dart @@ -12,8 +12,6 @@ import 'package:test/test.dart'; import 'assets.dart'; import 'in_memory_reader.dart'; -import 'in_memory_writer.dart'; -import 'multi_asset_reader.dart'; import 'written_asset_reader.dart'; AssetId _passThrough(AssetId id) => id; @@ -100,20 +98,6 @@ void checkOutputs( /// is the path to a file relative to the package. `PATH_WITHIN_PACKAGE` must /// include `lib`, `web`, `bin` or `test`. Example: "myapp|lib/utils.dart". /// -/// If a [reader] is provided, then any asset not in [sourceAssets] will be -/// read from the provided reader. This allows you to more easily provide -/// sources of entire packages to the test, instead of mocking them out, for -/// example, this exposes all assets available to the test itself: -/// -/// -/// ```dart -/// testBuilder(yourBuilder, {}/* test assets here */, -/// reader: await PackageAssetReader.currentIsolate()); -/// ``` -/// -/// Callers may optionally provide a [writer] to stub different behavior or do -/// more complex validation than what is possible with [outputs]. -/// /// Callers may optionally provide an [onLog] callback to do validaiton on the /// logging output of the builder. /// @@ -123,19 +107,19 @@ void checkOutputs( /// /// Enabling of language experiments is supported through the /// `withEnabledExperiments` method from package:build. -Future testBuilder( +/// +/// Returns a [TestBuilderResult] with the [InMemoryAssetReaderWriter] used for +/// the build, which can be used for further checks. +Future testBuilder( Builder builder, Map*/ Object> sourceAssets, {Set? generateFor, bool Function(String assetId)? isInput, String? rootPackage, - MultiPackageAssetReader? reader, - RecordingAssetWriter? writer, Map|Matcher>*/ Object>? outputs, void Function(LogRecord log)? onLog, void Function(AssetId, Iterable)? reportUnusedAssetsForInput, PackageConfig? packageConfig}) async { onLog ??= (log) => printOnFailure('$log'); - writer ??= InMemoryAssetWriter(); var inputIds = { for (var descriptor in sourceAssets.keys) makeAssetId(descriptor) @@ -152,36 +136,35 @@ Future testBuilder( ] ]); - final inMemoryReader = InMemoryAssetReader(rootPackage: rootPackage); + final readerWriter = InMemoryAssetReaderWriter(rootPackage: rootPackage); sourceAssets.forEach((serializedId, contents) { var id = makeAssetId(serializedId); if (contents is String) { - inMemoryReader.cacheStringAsset(id, contents); + readerWriter.cacheStringAsset(id, contents); } else if (contents is List) { - inMemoryReader.cacheBytesAsset(id, contents); + readerWriter.cacheBytesAsset(id, contents); } }); final inputFilter = isInput ?? generateFor?.contains ?? (_) => true; inputIds.retainWhere((id) => inputFilter('$id')); - var writerSpy = AssetWriterSpy(writer); + var writerSpy = AssetWriterSpy(readerWriter); var logger = Logger('testBuilder'); var logSubscription = logger.onRecord.listen(onLog); var resolvers = packageConfig == null && enabledExperiments.isEmpty ? AnalyzerResolvers.sharedInstance : AnalyzerResolvers.custom(packageConfig: packageConfig); + final startingFiles = readerWriter.assets.keys.toList(); for (var input in inputIds) { - // create another writer spy and reader for each input. This prevents writes - // from a previous input being readable when processing the current input. + // Create a reader that can read initial files plus anything written by the + // builder during the step; outputs by other builders during the step are + // not readable. final spyForStep = AssetWriterSpy(writerSpy); - final readerForStep = MultiAssetReader([ - inMemoryReader, - if (reader != null) reader, - WrittenAssetReader(writer, spyForStep), - ]); + final readerForStep = WrittenAssetReader(readerWriter, spyForStep) + ..allowReadingAll(startingFiles); await runBuilder(builder, {input}, readerForStep, spyForStep, resolvers, logger: logger, reportUnusedAssetsForInput: reportUnusedAssetsForInput); @@ -189,5 +172,12 @@ Future testBuilder( await logSubscription.cancel(); var actualOutputs = writerSpy.assetsWritten; - checkOutputs(outputs, actualOutputs, writer); + checkOutputs(outputs, actualOutputs, readerWriter); + return TestBuilderResult(readerWriter: readerWriter); +} + +class TestBuilderResult { + final InMemoryAssetReaderWriter readerWriter; + + TestBuilderResult({required this.readerWriter}); } diff --git a/build_test/lib/src/written_asset_reader.dart b/build_test/lib/src/written_asset_reader.dart index 52db3fbe1..5e304dd4d 100644 --- a/build_test/lib/src/written_asset_reader.dart +++ b/build_test/lib/src/written_asset_reader.dart @@ -15,17 +15,30 @@ class WrittenAssetReader extends MultiPackageAssetReader { /// An optional [AssetWriterSpy] to limit what's readable through this reader. /// - /// Only assets reported as written trough this [AssetWriterSpy] can be read + /// Only assets reported as written through this [AssetWriterSpy] can be read /// from this reader. When null, all assets from [source] are available. final AssetWriterSpy? filterSpy; + /// Assets allowed to be read because of a call to [allowReadingAll]. + final Set _additionallyAllowed = {}; + WrittenAssetReader(this.source, [this.filterSpy]); + /// Marks [assets] as allowed to be read. + /// + /// They are then readable regardless of whether they were written through + /// [filterSpy]. + void allowReadingAll(Iterable assets) { + _additionallyAllowed.addAll(assets); + } + @override Future canRead(AssetId id) { var canRead = source.assets.containsKey(id); if (filterSpy != null) { - canRead = canRead && filterSpy!.assetsWritten.contains(id); + canRead = canRead && + (_additionallyAllowed.contains(id) || + filterSpy!.assetsWritten.contains(id)); } return Future.value(canRead); @@ -35,7 +48,8 @@ class WrittenAssetReader extends MultiPackageAssetReader { Stream findAssets(Glob glob, {String? package}) async* { var available = source.assets.keys.toSet(); if (filterSpy != null) { - available = available.intersection(filterSpy!.assetsWritten.toSet()); + available = available.intersection( + filterSpy!.assetsWritten.toSet().union(_additionallyAllowed)); } for (var asset in available) { diff --git a/build_test/pubspec.yaml b/build_test/pubspec.yaml index cf98bd535..447f1aac5 100644 --- a/build_test/pubspec.yaml +++ b/build_test/pubspec.yaml @@ -1,6 +1,6 @@ name: build_test description: Utilities for writing unit tests of Builders. -version: 2.2.4-wip +version: 3.0.0-wip repository: https://github.com/dart-lang/build/tree/master/build_test resolution: workspace diff --git a/build_test/test/test_builder_test.dart b/build_test/test/test_builder_test.dart index 53b8c4dcb..99765936d 100644 --- a/build_test/test/test_builder_test.dart +++ b/build_test/test/test_builder_test.dart @@ -75,23 +75,6 @@ void main() { } }); - test('can pass a custom reader', () async { - var reader = - await PackageAssetReader.currentIsolate(rootPackage: 'build_test'); - var builder = TestBuilder( - buildExtensions: { - '.txt': ['.txt.copy'] - }, - build: (buildStep, _) async { - await buildStep.writeAsString( - buildStep.inputId.addExtension('.copy'), - buildStep - .readAsString(AssetId('build_test', 'test/data/hello.txt'))); - }); - await testBuilder(builder, {'build_test|data/1.txt': ''}, - outputs: {'build_test|data/1.txt.copy': 'hello world'}, reader: reader); - }); - test('can capture reportUnusedAssets calls', () async { var unusedInput = AssetId('a', 'lib/unused.txt'); var recorded = >{}; diff --git a/build_web_compilers/CHANGELOG.md b/build_web_compilers/CHANGELOG.md index eed23eab7..9a071d215 100644 --- a/build_web_compilers/CHANGELOG.md +++ b/build_web_compilers/CHANGELOG.md @@ -1,6 +1,7 @@ ## 4.1.2-wip - Use support-detection scripts emitted by `dart2wasm`. +- Use `build_test` 3.0.0. ## 4.1.1 diff --git a/build_web_compilers/pubspec.yaml b/build_web_compilers/pubspec.yaml index 1e24cd00f..159bff223 100644 --- a/build_web_compilers/pubspec.yaml +++ b/build_web_compilers/pubspec.yaml @@ -31,7 +31,7 @@ dev_dependencies: b: path: ../build_modules/test/fixtures/b build_runner: ^2.0.0 - build_test: ^2.0.0 + build_test: ^3.0.0-wip c: path: test/fixtures/c d: diff --git a/build_web_compilers/test/util.dart b/build_web_compilers/test/util.dart index 5ce59bf10..41b862b28 100644 --- a/build_web_compilers/test/util.dart +++ b/build_web_compilers/test/util.dart @@ -11,11 +11,9 @@ import 'package:test/test.dart'; /// Forwards to [testBuilder], and adds all output assets to [assets]. Future testBuilderAndCollectAssets( Builder builder, Map assets) async { - var writer = InMemoryAssetWriter(); - await testBuilder(builder, assets, - writer: writer, + final result = await testBuilder(builder, assets, onLog: (log) => printOnFailure('${log.level}: ${log.message}')); - writer.assets.forEach((id, value) { + result.readerWriter.assets.forEach((id, value) { assets['${id.package}|${id.path}'] = value; }); } diff --git a/scratch_space/CHANGELOG.md b/scratch_space/CHANGELOG.md index 474594d93..f44d281b0 100644 --- a/scratch_space/CHANGELOG.md +++ b/scratch_space/CHANGELOG.md @@ -1,6 +1,7 @@ ## 1.0.3-wip - Bump the min sdk to 3.6.0. +- Use `build_test` 3.0.0. ## 1.0.2 diff --git a/scratch_space/pubspec.yaml b/scratch_space/pubspec.yaml index 964ea2db2..5f65e9720 100644 --- a/scratch_space/pubspec.yaml +++ b/scratch_space/pubspec.yaml @@ -19,5 +19,5 @@ dependencies: dev_dependencies: build_runner: ^2.0.0 - build_test: ^2.0.0 + build_test: ^3.0.0-wip test: ^1.16.0