testBuilder function
- Builder builder,
- Map<
String, Object> sourceAssets, { - Set<
String> ? generateFor, - bool isInput(
- String assetId
- String? rootPackage,
- MultiPackageAssetReader? reader,
- RecordingAssetWriter? writer,
- Map<
String, Object> ? outputs, - void onLog(
- LogRecord log
- void reportUnusedAssetsForInput(
- AssetId,
- Iterable<
AssetId>
- PackageConfig? packageConfig,
Runs builder
in a test environment.
The test environment supplies in-memory build sourceAssets
to the builders
under test.
outputs
may be optionally provided to verify that the builders
produce the expected output, see checkOutputs for a full description of
the outputs
map and how to use it. If outputs
is omitted the only
validation this method provides is that the build did not throw
.
Either generateFor
or the isInput
callback can specify which assets
should be given as inputs to the builder. These can be omitted if every
asset in sourceAssets
should be considered an input. generateFor
is
ignored if both isInput
and generateFor
are provided.
The keys in sourceAssets
and outputs
are paths to file assets and the
values are file contents. The paths must use the following format:
PACKAGE_NAME|PATH_WITHIN_PACKAGE
Where PACKAGE_NAME
is the name of the package, and PATH_WITHIN_PACKAGE
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:
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.
An optional packageConfig
may be supplied to set the language versions of
certain packages. It will only be used for this purpose and not for reading
of files or converting uris.
Enabling of language experiments is supported through the
withEnabledExperiments
method from package:build.
Implementation
Future testBuilder(
Builder builder, Map<String, /*String|List<int>*/ Object> sourceAssets,
{Set<String>? generateFor,
bool Function(String assetId)? isInput,
String? rootPackage,
MultiPackageAssetReader? reader,
RecordingAssetWriter? writer,
Map<String, /*String|List<int>|Matcher<List<int>>*/ Object>? outputs,
void Function(LogRecord log)? onLog,
void Function(AssetId, Iterable<AssetId>)? reportUnusedAssetsForInput,
PackageConfig? packageConfig}) async {
onLog ??= (log) => printOnFailure('$log');
writer ??= InMemoryAssetWriter();
var inputIds = {
for (var descriptor in sourceAssets.keys) makeAssetId(descriptor)
};
var allPackages = {for (var id in inputIds) id.package};
if (allPackages.length == 1) rootPackage ??= allPackages.first;
inputIds.addAll([
for (var package in allPackages) AssetId(package, r'lib/$lib$'),
if (rootPackage != null) ...[
AssetId(rootPackage, r'$package$'),
AssetId(rootPackage, r'test/$test$'),
AssetId(rootPackage, r'web/$web$'),
]
]);
final inMemoryReader = InMemoryAssetReader(rootPackage: rootPackage);
sourceAssets.forEach((serializedId, contents) {
var id = makeAssetId(serializedId);
if (contents is String) {
inMemoryReader.cacheStringAsset(id, contents);
} else if (contents is List<int>) {
inMemoryReader.cacheBytesAsset(id, contents);
}
});
final inputFilter = isInput ?? generateFor?.contains ?? (_) => true;
inputIds.retainWhere((id) => inputFilter('$id'));
var writerSpy = AssetWriterSpy(writer);
var logger = Logger('testBuilder');
var logSubscription = logger.onRecord.listen(onLog);
var resolvers = packageConfig == null && enabledExperiments.isEmpty
? AnalyzerResolvers.sharedInstance
: AnalyzerResolvers.custom(packageConfig: packageConfig);
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.
final spyForStep = AssetWriterSpy(writerSpy);
final readerForStep = MultiAssetReader([
inMemoryReader,
if (reader != null) reader,
WrittenAssetReader(writer, spyForStep),
]);
await runBuilder(builder, {input}, readerForStep, spyForStep, resolvers,
logger: logger, reportUnusedAssetsForInput: reportUnusedAssetsForInput);
}
await logSubscription.cancel();
var actualOutputs = writerSpy.assetsWritten;
checkOutputs(outputs, actualOutputs, writer);
}