testBuilder function

Future testBuilder (Builder builder, Map<String, dynamic> sourceAssets, { Set<String> generateFor, bool isInput(String assetId), String rootPackage, MultiPackageAssetReader reader, RecordingAssetWriter writer, Map<String, dynamic> outputs, void onLog(LogRecord log) })

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. If outputs is omitted the only validation this method provides is that the build did not throw.

generateFor or the isInput call back 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. isInput precedent over generateFor if both 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 entire packages source 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.

Implementation

Future testBuilder(
    Builder builder, Map<String, /*String|List<int>*/ dynamic> sourceAssets,
    {Set<String> generateFor,
    bool isInput(String assetId),
    String rootPackage,
    MultiPackageAssetReader reader,
    RecordingAssetWriter writer,
    Map<String, /*String|List<int>|Matcher<String|List<int>>*/ dynamic> outputs,
    void onLog(LogRecord log)}) async {
  writer ??= InMemoryAssetWriter();
  final inMemoryReader = InMemoryAssetReader(rootPackage: rootPackage);
  if (reader != null) {
    reader = MultiAssetReader([inMemoryReader, reader]);
  } else {
    reader = inMemoryReader;
  }

  var inputIds = <AssetId>[];
  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);
    }
    inputIds.add(id);
  });

  var allPackages = inMemoryReader.assets.keys.map((a) => a.package).toSet();
  for (var pkg in allPackages) {
    for (var dir in const ['lib', 'web', 'test']) {
      var asset = AssetId(pkg, '$dir/\$$dir\$');
      inputIds.add(asset);
    }
  }

  isInput ??= generateFor?.contains ?? (_) => true;
  inputIds = inputIds.where((id) => isInput('$id')).toList();

  var writerSpy = AssetWriterSpy(writer);
  var logger = Logger('testBuilder');
  var logSubscription = logger.onRecord.listen(onLog);
  await runBuilder(builder, inputIds, reader, writerSpy, defaultResolvers,
      logger: logger);
  await logSubscription.cancel();
  var actualOutputs = writerSpy.assetsWritten;
  checkOutputs(outputs, actualOutputs, writer);
}