Revert "Speed up first asset load by encoding asset manifest in binary rather than JSON (#113637)" (#116662) (#116672)
This reverts commit 56cad89b1e8d79b75b88116a5db9cb70f91c4986.
Co-authored-by: Casey Hillers <chillers@google.com>
diff --git a/dev/benchmarks/microbenchmarks/lib/foundation/decode_and_parse_asset_manifest.dart b/dev/benchmarks/microbenchmarks/lib/foundation/decode_and_parse_asset_manifest.dart
index d6b0f7c..b64c153 100644
--- a/dev/benchmarks/microbenchmarks/lib/foundation/decode_and_parse_asset_manifest.dart
+++ b/dev/benchmarks/microbenchmarks/lib/foundation/decode_and_parse_asset_manifest.dart
@@ -3,9 +3,9 @@
// found in the LICENSE file.
import 'dart:convert';
-import 'dart:typed_data';
-import 'package:flutter/services.dart' show PlatformAssetBundle, StandardMessageCodec;
+import 'package:flutter/foundation.dart';
+import 'package:flutter/services.dart' show PlatformAssetBundle;
import 'package:flutter/widgets.dart';
import '../common.dart';
@@ -18,14 +18,16 @@
final BenchmarkResultPrinter printer = BenchmarkResultPrinter();
WidgetsFlutterBinding.ensureInitialized();
final Stopwatch watch = Stopwatch();
+ final PlatformAssetBundle bundle = PlatformAssetBundle();
- final ByteData assetManifest = await loadAssetManifest();
-
+ final ByteData assetManifestBytes = await bundle.load('money_asset_manifest.json');
watch.start();
for (int i = 0; i < _kNumIterations; i++) {
- // This is effectively a test.
+ bundle.clear();
+ final String json = utf8.decode(assetManifestBytes.buffer.asUint8List());
+ // This is a test, so we don't need to worry about this rule.
// ignore: invalid_use_of_visible_for_testing_member
- AssetImage.parseAssetManifest(assetManifest);
+ await AssetImage.manifestParser(json);
}
watch.stop();
@@ -38,49 +40,3 @@
printer.printToStdout();
}
-
-final RegExp _extractRatioRegExp = RegExp(r'/?(\d+(\.\d*)?)x$');
-
-Future<ByteData> loadAssetManifest() async {
- double parseScale(String key) {
- final Uri assetUri = Uri.parse(key);
- String directoryPath = '';
- if (assetUri.pathSegments.length > 1) {
- directoryPath = assetUri.pathSegments[assetUri.pathSegments.length - 2];
- }
- final Match? match = _extractRatioRegExp.firstMatch(directoryPath);
- if (match != null && match.groupCount > 0) {
- return double.parse(match.group(1)!);
- }
- return 1.0;
- }
-
- final Map<String, dynamic> result = <String, dynamic>{};
- final PlatformAssetBundle bundle = PlatformAssetBundle();
-
- // For the benchmark, we use the older JSON format and then convert it to the modern binary format.
- final ByteData jsonAssetManifestBytes = await bundle.load('money_asset_manifest.json');
- final String jsonAssetManifest = utf8.decode(jsonAssetManifestBytes.buffer.asUint8List());
-
- final Map<String, dynamic> assetManifest = json.decode(jsonAssetManifest) as Map<String, dynamic>;
-
- for (final MapEntry<String, dynamic> manifestEntry in assetManifest.entries) {
- final List<dynamic> resultVariants = <dynamic>[];
- final List<String> entries = (manifestEntry.value as List<dynamic>).cast<String>();
- for (final String variant in entries) {
- if (variant == manifestEntry.key) {
- // With the newer binary format, don't include the main asset in it's
- // list of variants. This reduces parsing time at runtime.
- continue;
- }
- final Map<String, dynamic> resultVariant = <String, dynamic>{};
- final double variantDevicePixelRatio = parseScale(variant);
- resultVariant['asset'] = variant;
- resultVariant['dpr'] = variantDevicePixelRatio;
- resultVariants.add(resultVariant);
- }
- result[manifestEntry.key] = resultVariants;
- }
-
- return const StandardMessageCodec().encodeMessage(result)!;
-}
diff --git a/dev/integration_tests/flutter_gallery/test/example_code_parser_test.dart b/dev/integration_tests/flutter_gallery/test/example_code_parser_test.dart
index a1baa46..94276fb 100644
--- a/dev/integration_tests/flutter_gallery/test/example_code_parser_test.dart
+++ b/dev/integration_tests/flutter_gallery/test/example_code_parser_test.dart
@@ -2,8 +2,6 @@
// 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:flutter/services.dart';
import 'package:flutter_gallery/gallery/example_code_parser.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -60,9 +58,4 @@
@override
String toString() => '$runtimeType@$hashCode()';
-
- @override
- Future<T> loadStructuredBinaryData<T>(String key, FutureOr<T> Function(ByteData data) parser) async {
- return parser(await load(key));
- }
}
diff --git a/packages/flutter/lib/src/painting/image_resolution.dart b/packages/flutter/lib/src/painting/image_resolution.dart
index f978dff..1e42774 100644
--- a/packages/flutter/lib/src/painting/image_resolution.dart
+++ b/packages/flutter/lib/src/painting/image_resolution.dart
@@ -11,8 +11,7 @@
import 'image_provider.dart';
-const String _kLegacyAssetManifestFilename = 'AssetManifest.json';
-const String _kAssetManifestFilename = 'AssetManifest.bin';
+const String _kAssetManifestFileName = 'AssetManifest.json';
/// A screen with a device-pixel ratio strictly less than this value is
/// considered a low-resolution screen (typically entry-level to mid-range
@@ -285,45 +284,18 @@
Completer<AssetBundleImageKey>? completer;
Future<AssetBundleImageKey>? result;
- Future<_AssetManifest> loadJsonAssetManifest() {
- Future<_AssetManifest> parseJson(String data) {
- final _AssetManifest parsed = _LegacyAssetManifest.fromJsonString(data);
- return SynchronousFuture<_AssetManifest>(parsed);
- }
- return chosenBundle.loadStructuredData(_kLegacyAssetManifestFilename, parseJson);
- }
-
- // TODO(andrewkolos): Once google3 and google-fonts-flutter are migrated
- // away from using AssetManifest.json, remove all references to it.
- // See https://github.com/flutter/flutter/issues/114913.
- Future<_AssetManifest>? manifest;
-
- // Since AssetBundle load calls can be synchronous (e.g. in the case of tests),
- // it is not sufficient to only use catchError/onError or the onError parameter
- // of Future.then--we also have to use a synchronous try/catch. Once google3
- // tooling starts producing AssetManifest.bin, this block can be removed.
- try {
- manifest = chosenBundle.loadStructuredBinaryData(_kAssetManifestFilename, _AssetManifestBin.fromStandardMessageCodecMessage);
- } catch (error) {
- manifest = loadJsonAssetManifest();
- }
-
- manifest
- // To understand why we use this no-op `then` instead of `catchError`/`onError`,
- // see https://github.com/flutter/flutter/issues/115601
- .then((_AssetManifest manifest) => manifest,
- onError: (Object? error, StackTrace? stack) => loadJsonAssetManifest())
- .then((_AssetManifest manifest) {
- final List<_AssetVariant> candidateVariants = manifest.getVariants(keyName);
- final _AssetVariant chosenVariant = _chooseVariant(
+ chosenBundle.loadStructuredData<Map<String, List<String>>?>(_kAssetManifestFileName, manifestParser).then<void>(
+ (Map<String, List<String>>? manifest) {
+ final String chosenName = _chooseVariant(
keyName,
configuration,
- candidateVariants,
- );
+ manifest == null ? null : manifest[keyName],
+ )!;
+ final double chosenScale = _parseScale(chosenName);
final AssetBundleImageKey key = AssetBundleImageKey(
bundle: chosenBundle,
- name: chosenVariant.asset,
- scale: chosenVariant.devicePixelRatio,
+ name: chosenName,
+ scale: chosenScale,
);
if (completer != null) {
// We already returned from this function, which means we are in the
@@ -337,15 +309,14 @@
// ourselves.
result = SynchronousFuture<AssetBundleImageKey>(key);
}
- })
- .onError((Object error, StackTrace stack) {
- // We had an error. (This guarantees we weren't called synchronously.)
- // Forward the error to the caller.
- assert(completer != null);
- assert(result == null);
- completer!.completeError(error, stack);
- });
-
+ },
+ ).catchError((Object error, StackTrace stack) {
+ // We had an error. (This guarantees we weren't called synchronously.)
+ // Forward the error to the caller.
+ assert(completer != null);
+ assert(result == null);
+ completer!.completeError(error, stack);
+ });
if (result != null) {
// The code above ran synchronously, and came up with an answer.
// Return the SynchronousFuture that we created above.
@@ -357,29 +328,35 @@
return completer.future;
}
- /// Parses the asset manifest's file contents into it's Dart representation.
+ /// Parses the asset manifest string into a strongly-typed map.
@visibleForTesting
- // Return type is set to Object?, because the specific type is private.
- static Object? parseAssetManifest(ByteData bytes) {
- return _AssetManifestBin.fromStandardMessageCodecMessage(bytes);
+ static Future<Map<String, List<String>>?> manifestParser(String? jsonData) {
+ if (jsonData == null) {
+ return SynchronousFuture<Map<String, List<String>>?>(null);
+ }
+ // TODO(ianh): JSON decoding really shouldn't be on the main thread.
+ final Map<String, dynamic> parsedJson = json.decode(jsonData) as Map<String, dynamic>;
+ final Iterable<String> keys = parsedJson.keys;
+ final Map<String, List<String>> parsedManifest = <String, List<String>> {
+ for (final String key in keys) key: List<String>.from(parsedJson[key] as List<dynamic>),
+ };
+ // TODO(ianh): convert that data structure to the right types.
+ return SynchronousFuture<Map<String, List<String>>?>(parsedManifest);
}
- _AssetVariant _chooseVariant(String mainAssetKey, ImageConfiguration config, List<_AssetVariant> candidateVariants) {
- final _AssetVariant mainAsset = _AssetVariant(asset: mainAssetKey,
- devicePixelRatio: _naturalResolution);
- if (config.devicePixelRatio == null || candidateVariants.isEmpty) {
- return mainAsset;
+ String? _chooseVariant(String main, ImageConfiguration config, List<String>? candidates) {
+ if (config.devicePixelRatio == null || candidates == null || candidates.isEmpty) {
+ return main;
}
- final SplayTreeMap<double, _AssetVariant> candidatesByDevicePixelRatio =
- SplayTreeMap<double, _AssetVariant>();
- for (final _AssetVariant candidate in candidateVariants) {
- candidatesByDevicePixelRatio[candidate.devicePixelRatio] = candidate;
+ // TODO(ianh): Consider moving this parsing logic into _manifestParser.
+ final SplayTreeMap<double, String> mapping = SplayTreeMap<double, String>();
+ for (final String candidate in candidates) {
+ mapping[_parseScale(candidate)] = candidate;
}
- candidatesByDevicePixelRatio.putIfAbsent(_naturalResolution, () => mainAsset);
// TODO(ianh): implement support for config.locale, config.textDirection,
// config.size, config.platform (then document this over in the Image.asset
// docs)
- return _findBestVariant(candidatesByDevicePixelRatio, config.devicePixelRatio!);
+ return _findBestVariant(mapping, config.devicePixelRatio!);
}
// Returns the "best" asset variant amongst the available `candidates`.
@@ -394,17 +371,17 @@
// lowest key higher than `value`.
// - If the screen has high device pixel ratio, choose the variant with the
// key nearest to `value`.
- _AssetVariant _findBestVariant(SplayTreeMap<double, _AssetVariant> candidatesByDpr, double value) {
- if (candidatesByDpr.containsKey(value)) {
- return candidatesByDpr[value]!;
+ String? _findBestVariant(SplayTreeMap<double, String> candidates, double value) {
+ if (candidates.containsKey(value)) {
+ return candidates[value]!;
}
- final double? lower = candidatesByDpr.lastKeyBefore(value);
- final double? upper = candidatesByDpr.firstKeyAfter(value);
+ final double? lower = candidates.lastKeyBefore(value);
+ final double? upper = candidates.firstKeyAfter(value);
if (lower == null) {
- return candidatesByDpr[upper]!;
+ return candidates[upper];
}
if (upper == null) {
- return candidatesByDpr[lower]!;
+ return candidates[lower];
}
// On screens with low device-pixel ratios the artifacts from upscaling
@@ -412,12 +389,32 @@
// ratios because the physical pixels are larger. Choose the higher
// resolution image in that case instead of the nearest one.
if (value < _kLowDprLimit || value > (lower + upper) / 2) {
- return candidatesByDpr[upper]!;
+ return candidates[upper];
} else {
- return candidatesByDpr[lower]!;
+ return candidates[lower];
}
}
+ static final RegExp _extractRatioRegExp = RegExp(r'/?(\d+(\.\d*)?)x$');
+
+ double _parseScale(String key) {
+ if (key == assetName) {
+ return _naturalResolution;
+ }
+
+ final Uri assetUri = Uri.parse(key);
+ String directoryPath = '';
+ if (assetUri.pathSegments.length > 1) {
+ directoryPath = assetUri.pathSegments[assetUri.pathSegments.length - 2];
+ }
+
+ final Match? match = _extractRatioRegExp.firstMatch(directoryPath);
+ if (match != null && match.groupCount > 0) {
+ return double.parse(match.group(1)!);
+ }
+ return _naturalResolution; // i.e. default to 1.0x
+ }
+
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) {
@@ -434,120 +431,3 @@
@override
String toString() => '${objectRuntimeType(this, 'AssetImage')}(bundle: $bundle, name: "$keyName")';
}
-
-/// Centralizes parsing and typecasting of the contents of the asset manifest file,
-/// which is generated by the flutter tool at build time.
-abstract class _AssetManifest {
- List<_AssetVariant> getVariants(String key);
-}
-
-/// Parses the binary asset manifest into a data structure that's easier to work with.
-///
-/// The asset manifest is a map of asset files to a list of objects containing
-/// information about variants of that asset.
-///
-/// The entries with each variant object are:
-/// - "asset": the location of this variant to load it from.
-/// - "dpr": The device-pixel-ratio that the asset is best-suited for.
-///
-/// New fields could be added to this object schema to support new asset variation
-/// features, such as themes, locale/region support, reading directions, and so on.
-class _AssetManifestBin implements _AssetManifest {
- _AssetManifestBin(Map<Object?, Object?> standardMessageData): _data = standardMessageData;
-
- factory _AssetManifestBin.fromStandardMessageCodecMessage(ByteData message) {
- final Object? data = const StandardMessageCodec().decodeMessage(message);
- return _AssetManifestBin(data! as Map<Object?, Object?>);
- }
-
- final Map<Object?, Object?> _data;
- final Map<String, List<_AssetVariant>> _typeCastedData = <String, List<_AssetVariant>>{};
-
- @override
- List<_AssetVariant> getVariants(String key) {
- // We lazily delay typecasting to prevent a performance hiccup when parsing
- // large asset manifests.
- if (!_typeCastedData.containsKey(key)) {
- _typeCastedData[key] = ((_data[key] ?? <Object?>[]) as List<Object?>)
- .cast<Map<Object?, Object?>>()
- .map(_AssetVariant.fromManifestData)
- .toList();
- }
- return _typeCastedData[key]!;
- }
-}
-
-class _LegacyAssetManifest implements _AssetManifest {
- _LegacyAssetManifest({
- required this.manifest,
- });
-
- factory _LegacyAssetManifest.fromJsonString(String jsonString) {
- List<_AssetVariant> adaptLegacyVariantList(String mainAsset, List<String> variants) {
- return variants
- .map((String variant) =>
- _AssetVariant(asset: variant, devicePixelRatio: _parseScale(mainAsset, variant)))
- .toList();
- }
-
- if (jsonString == null) {
- return _LegacyAssetManifest(manifest: <String, List<_AssetVariant>>{});
- }
- final Map<String, Object?> parsedJson = json.decode(jsonString) as Map<String, dynamic>;
- final Iterable<String> keys = parsedJson.keys;
- final Map<String, List<String>> parsedManifest = <String, List<String>> {
- for (final String key in keys) key: List<String>.from(parsedJson[key]! as List<dynamic>),
- };
- final Map<String, List<_AssetVariant>> manifestWithParsedVariants =
- parsedManifest.map((String asset, List<String> variants) =>
- MapEntry<String, List<_AssetVariant>>(asset, adaptLegacyVariantList(asset, variants)));
-
- return _LegacyAssetManifest(manifest: manifestWithParsedVariants);
- }
-
- final Map<String, List<_AssetVariant>> manifest;
-
- static final RegExp _extractRatioRegExp = RegExp(r'/?(\d+(\.\d*)?)x$');
- static const double _naturalResolution = 1.0;
-
- @override
- List<_AssetVariant> getVariants(String key) {
- return manifest[key] ?? const <_AssetVariant>[];
- }
-
- static double _parseScale(String mainAsset, String variant) {
- // The legacy asset manifest includes the main asset within its variant list.
- if (mainAsset == variant) {
- return _naturalResolution;
- }
-
- final Uri assetUri = Uri.parse(variant);
- String directoryPath = '';
- if (assetUri.pathSegments.length > 1) {
- directoryPath = assetUri.pathSegments[assetUri.pathSegments.length - 2];
- }
-
- final Match? match = _extractRatioRegExp.firstMatch(directoryPath);
- if (match != null && match.groupCount > 0) {
- return double.parse(match.group(1)!);
- }
-
- return _naturalResolution; // i.e. default to 1.0x
- }
-}
-
-class _AssetVariant {
- _AssetVariant({
- required this.asset,
- required this.devicePixelRatio,
- });
-
- factory _AssetVariant.fromManifestData(Object data) {
- final Map<Object?, Object?> asStructuredData = data as Map<Object?, Object?>;
- return _AssetVariant(asset: asStructuredData['asset']! as String,
- devicePixelRatio: asStructuredData['dpr']! as double);
- }
-
- final double devicePixelRatio;
- final String asset;
-}
diff --git a/packages/flutter/lib/src/services/asset_bundle.dart b/packages/flutter/lib/src/services/asset_bundle.dart
index ba971e2..776035a 100644
--- a/packages/flutter/lib/src/services/asset_bundle.dart
+++ b/packages/flutter/lib/src/services/asset_bundle.dart
@@ -96,25 +96,12 @@
}
/// Retrieve a string from the asset bundle, parse it with the given function,
- /// and return that function's result.
+ /// and return the function's result.
///
/// Implementations may cache the result, so a particular key should only be
/// used with one parser for the lifetime of the asset bundle.
Future<T> loadStructuredData<T>(String key, Future<T> Function(String value) parser);
- /// Retrieve [ByteData] from the asset bundle, parse it with the given function,
- /// and return that function's result.
- ///
- /// Implementations may cache the result, so a particular key should only be
- /// used with one parser for the lifetime of the asset bundle.
- Future<T> loadStructuredBinaryData<T>(String key, FutureOr<T> Function(ByteData data) parser) async {
- final ByteData data = await load(key);
- if (data == null) {
- throw FlutterError('Unable to load asset: $key');
- }
- return parser(data);
- }
-
/// If this is a caching asset bundle, and the given key describes a cached
/// asset, then evict the asset from the cache so that the next time it is
/// loaded, the cache will be reread from the asset bundle.
@@ -169,18 +156,6 @@
return parser(await loadString(key));
}
- /// Retrieve [ByteData] from the asset bundle, parse it with the given function,
- /// and return the function's result.
- ///
- /// The result is not cached. The parser is run each time the resource is
- /// fetched.
- @override
- Future<T> loadStructuredBinaryData<T>(String key, FutureOr<T> Function(ByteData data) parser) async {
- assert(key != null);
- assert(parser != null);
- return parser(await load(key));
- }
-
// TODO(ianh): Once the underlying network logic learns about caching, we
// should implement evict().
@@ -200,7 +175,6 @@
// TODO(ianh): Replace this with an intelligent cache, see https://github.com/flutter/flutter/issues/3568
final Map<String, Future<String>> _stringCache = <String, Future<String>>{};
final Map<String, Future<dynamic>> _structuredDataCache = <String, Future<dynamic>>{};
- final Map<String, Future<dynamic>> _structuredBinaryDataCache = <String, Future<dynamic>>{};
@override
Future<String> loadString(String key, { bool cache = true }) {
@@ -251,69 +225,16 @@
return completer.future;
}
- /// Retrieve bytedata from the asset bundle, parse it with the given function,
- /// and return the function's result.
- ///
- /// The result of parsing the bytedata is cached (the bytedata itself is not).
- /// For any given `key`, the `parser` is only run the first time.
- ///
- /// Once the value has been parsed, the future returned by this function for
- /// subsequent calls will be a [SynchronousFuture], which resolves its
- /// callback synchronously.
- @override
- Future<T> loadStructuredBinaryData<T>(String key, FutureOr<T> Function(ByteData data) parser) {
- assert(key != null);
- assert(parser != null);
-
- if (_structuredBinaryDataCache.containsKey(key)) {
- return _structuredBinaryDataCache[key]! as Future<T>;
- }
-
- // load can return a SynchronousFuture in certain cases, like in the
- // flutter_test framework. So, we need to support both async and sync flows.
- Completer<T>? completer; // For async flow.
- SynchronousFuture<T>? result; // For sync flow.
-
- load(key)
- .then<T>(parser)
- .then<void>((T value) {
- result = SynchronousFuture<T>(value);
- if (completer != null) {
- // The load and parse operation ran asynchronously. We already returned
- // from the loadStructuredBinaryData function and therefore the caller
- // was given the future of the completer.
- completer.complete(value);
- }
- }, onError: (Object err, StackTrace? stack) {
- completer!.completeError(err, stack);
- });
-
- if (result != null) {
- // The above code ran synchronously. We can synchronously return the result.
- _structuredBinaryDataCache[key] = result!;
- return result!;
- }
-
- // Since the above code is being run asynchronously and thus hasn't run its
- // `then` handler yet, we'll return a completer that will be completed
- // when the handler does run.
- completer = Completer<T>();
- _structuredBinaryDataCache[key] = completer.future;
- return completer.future;
- }
-
@override
void evict(String key) {
_stringCache.remove(key);
_structuredDataCache.remove(key);
- _structuredBinaryDataCache.remove(key);
}
@override
void clear() {
_stringCache.clear();
_structuredDataCache.clear();
- _structuredBinaryDataCache.clear();
}
@override
@@ -355,7 +276,7 @@
bool debugUsePlatformChannel = false;
assert(() {
// dart:io is safe to use here since we early return for web
- // above. If that code is changed, this needs to be guarded on
+ // above. If that code is changed, this needs to be gaurded on
// web presence. Override how assets are loaded in tests so that
// the old loader behavior that allows tests to load assets from
// the current package using the package prefix.
diff --git a/packages/flutter/test/painting/image_resolution_test.dart b/packages/flutter/test/painting/image_resolution_test.dart
index 962cf9d..8e04f2a 100644
--- a/packages/flutter/test/painting/image_resolution_test.dart
+++ b/packages/flutter/test/painting/image_resolution_test.dart
@@ -13,14 +13,18 @@
class TestAssetBundle extends CachingAssetBundle {
TestAssetBundle(this._assetBundleMap);
- final Map<String, List<Map<dynamic, dynamic>>> _assetBundleMap;
+ final Map<String, List<String>> _assetBundleMap;
Map<String, int> loadCallCount = <String, int>{};
+ String get _assetBundleContents {
+ return json.encode(_assetBundleMap);
+ }
+
@override
Future<ByteData> load(String key) async {
- if (key == 'AssetManifest.bin') {
- return const StandardMessageCodec().encodeMessage(_assetBundleMap)!;
+ if (key == 'AssetManifest.json') {
+ return ByteData.view(Uint8List.fromList(const Utf8Encoder().convert(_assetBundleContents)).buffer);
}
loadCallCount[key] = loadCallCount[key] ?? 0 + 1;
@@ -38,71 +42,12 @@
}
}
-class BundleWithoutAssetManifestBin extends CachingAssetBundle {
- BundleWithoutAssetManifestBin(this._legacyAssetBundleMap);
-
- final Map<dynamic, List<String>> _legacyAssetBundleMap;
-
- Map<String, int> loadCallCount = <String, int>{};
-
- @override
- Future<ByteData> load(String key) async {
- ByteData testByteData(double scale) => ByteData(8)..setFloat64(0, scale);
-
- if (key == 'AssetManifest.bin') {
- throw FlutterError('AssetManifest.bin was not found.');
- }
- if (key == 'AssetManifest.json') {
- return ByteData.view(Uint8List.fromList(const Utf8Encoder().convert(json.encode(_legacyAssetBundleMap))).buffer);
- }
- switch (key) {
- case 'assets/image.png':
- return testByteData(1.0); // see "...with a main asset and a 1.0x asset"
- case 'assets/2.0x/image.png':
- return testByteData(1.5);
- }
-
- throw FlutterError('Unexpected key: $key');
- }
-
- @override
- Future<ui.ImmutableBuffer> loadBuffer(String key) async {
- final ByteData data = await load(key);
- return ui.ImmutableBuffer.fromUint8List(data.buffer.asUint8List());
- }
-}
-
void main() {
-
- // TODO(andrewkolos): Once google3 is migrated away from using AssetManifest.json,
- // remove all references to it. See https://github.com/flutter/flutter/issues/114913.
- test('AssetBundle falls back to using AssetManifest.json if AssetManifest.bin cannot be found.', () async {
- const String assetPath = 'assets/image.png';
- final Map<dynamic, List<String>> assetBundleMap = <dynamic, List<String>>{};
- assetBundleMap[assetPath] = <String>[];
- final AssetImage assetImage = AssetImage(assetPath, bundle: BundleWithoutAssetManifestBin(assetBundleMap));
- final AssetBundleImageKey key = await assetImage.obtainKey(ImageConfiguration.empty);
- expect(key.name, assetPath);
- expect(key.scale, 1.0);
- });
-
- test('When using AssetManifest.json, on a high DPR device, a high dpr variant is selected.', () async {
- const String assetPath = 'assets/image.png';
- const String asset2xPath = 'assets/2.0x/image.png';
- final Map<dynamic, List<String>> assetBundleMap = <dynamic, List<String>>{};
- assetBundleMap[assetPath] = <String>[asset2xPath];
- final AssetImage assetImage = AssetImage(assetPath, bundle: BundleWithoutAssetManifestBin(assetBundleMap));
- final AssetBundleImageKey key = await assetImage.obtainKey(const ImageConfiguration(devicePixelRatio: 2.0));
- expect(key.name, asset2xPath);
- expect(key.scale, 2.0);
- });
-
group('1.0 scale device tests', () {
void buildAndTestWithOneAsset(String mainAssetPath) {
- final Map<String, List<Map<dynamic, dynamic>>> assetBundleMap =
- <String, List<Map<dynamic, dynamic>>>{};
+ final Map<String, List<String>> assetBundleMap = <String, List<String>>{};
- assetBundleMap[mainAssetPath] = <Map<dynamic,dynamic>>[];
+ assetBundleMap[mainAssetPath] = <String>[];
final AssetImage assetImage = AssetImage(
mainAssetPath,
@@ -148,13 +93,10 @@
const String mainAssetPath = 'assets/normalFolder/normalFile.png';
const String variantPath = 'assets/normalFolder/3.0x/normalFile.png';
- final Map<String, List<Map<dynamic, dynamic>>> assetBundleMap =
- <String, List<Map<dynamic, dynamic>>>{};
+ final Map<String, List<String>> assetBundleMap =
+ <String, List<String>>{};
- final Map<dynamic, dynamic> mainAssetVariantManifestEntry = <dynamic, dynamic>{};
- mainAssetVariantManifestEntry['asset'] = variantPath;
- mainAssetVariantManifestEntry['dpr'] = 3.0;
- assetBundleMap[mainAssetPath] = <Map<dynamic, dynamic>>[mainAssetVariantManifestEntry];
+ assetBundleMap[mainAssetPath] = <String>[mainAssetPath, variantPath];
final TestAssetBundle testAssetBundle = TestAssetBundle(assetBundleMap);
@@ -181,10 +123,10 @@
test('When high-res device and high-res asset not present in bundle then return main variant', () {
const String mainAssetPath = 'assets/normalFolder/normalFile.png';
- final Map<String, List<Map<dynamic, dynamic>>> assetBundleMap =
- <String, List<Map<dynamic, dynamic>>>{};
+ final Map<String, List<String>> assetBundleMap =
+ <String, List<String>>{};
- assetBundleMap[mainAssetPath] = <Map<dynamic, dynamic>>[];
+ assetBundleMap[mainAssetPath] = <String>[mainAssetPath];
final TestAssetBundle testAssetBundle = TestAssetBundle(assetBundleMap);
@@ -214,18 +156,16 @@
const String mainAssetPath = 'assets/normalFolder/normalFile.png';
const String variantPath = 'assets/normalFolder/3.0x/normalFile.png';
+
void buildBundleAndTestVariantLogic(
double deviceRatio,
double chosenAssetRatio,
String expectedAssetPath,
) {
- final Map<String, List<Map<dynamic, dynamic>>> assetBundleMap =
- <String, List<Map<dynamic, dynamic>>>{};
+ final Map<String, List<String>> assetBundleMap =
+ <String, List<String>>{};
- final Map<dynamic, dynamic> mainAssetVariantManifestEntry = <dynamic, dynamic>{};
- mainAssetVariantManifestEntry['asset'] = variantPath;
- mainAssetVariantManifestEntry['dpr'] = 3.0;
- assetBundleMap[mainAssetPath] = <Map<dynamic, dynamic>>[mainAssetVariantManifestEntry];
+ assetBundleMap[mainAssetPath] = <String>[mainAssetPath, variantPath];
final TestAssetBundle testAssetBundle = TestAssetBundle(assetBundleMap);
diff --git a/packages/flutter/test/services/asset_bundle_test.dart b/packages/flutter/test/services/asset_bundle_test.dart
index 4beac89..8a97df3 100644
--- a/packages/flutter/test/services/asset_bundle_test.dart
+++ b/packages/flutter/test/services/asset_bundle_test.dart
@@ -9,14 +9,14 @@
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
-class _TestAssetBundle extends CachingAssetBundle {
+class TestAssetBundle extends CachingAssetBundle {
Map<String, int> loadCallCount = <String, int>{};
@override
Future<ByteData> load(String key) async {
loadCallCount[key] = loadCallCount[key] ?? 0 + 1;
- if (key == 'AssetManifest.bin') {
- return const StandardMessageCodec().encodeMessage(json.decode('{"one":[]}'))!;
+ if (key == 'AssetManifest.json') {
+ return ByteData.view(Uint8List.fromList(const Utf8Encoder().convert('{"one": ["one"]}')).buffer);
}
if (key == 'one') {
@@ -30,7 +30,7 @@
TestWidgetsFlutterBinding.ensureInitialized();
test('Caching asset bundle test', () async {
- final _TestAssetBundle bundle = _TestAssetBundle();
+ final TestAssetBundle bundle = TestAssetBundle();
final ByteData assetData = await bundle.load('one');
expect(assetData.getInt8(0), equals(49));
@@ -53,7 +53,7 @@
test('AssetImage.obtainKey succeeds with ImageConfiguration.empty', () async {
// This is a regression test for https://github.com/flutter/flutter/issues/12392
- final AssetImage assetImage = AssetImage('one', bundle: _TestAssetBundle());
+ final AssetImage assetImage = AssetImage('one', bundle: TestAssetBundle());
final AssetBundleImageKey key = await assetImage.obtainKey(ImageConfiguration.empty);
expect(key.name, 'one');
expect(key.scale, 1.0);
diff --git a/packages/flutter/test/widgets/image_resolution_test.dart b/packages/flutter/test/widgets/image_resolution_test.dart
index da6ac35..644f2c4 100644
--- a/packages/flutter/test/widgets/image_resolution_test.dart
+++ b/packages/flutter/test/widgets/image_resolution_test.dart
@@ -3,7 +3,6 @@
// found in the LICENSE file.
@TestOn('!chrome')
-import 'dart:convert';
import 'dart:ui' as ui show Image;
import 'package:flutter/foundation.dart';
@@ -17,32 +16,27 @@
ByteData testByteData(double scale) => ByteData(8)..setFloat64(0, scale);
double scaleOf(ByteData data) => data.getFloat64(0);
-final Map<dynamic, dynamic> testManifest = json.decode('''
+const String testManifest = '''
{
"assets/image.png" : [
- {"asset": "assets/1.5x/image.png", "dpr": 1.5},
- {"asset": "assets/2.0x/image.png", "dpr": 2.0},
- {"asset": "assets/3.0x/image.png", "dpr": 3.0},
- {"asset": "assets/4.0x/image.png", "dpr": 4.0}
+ "assets/image.png",
+ "assets/1.5x/image.png",
+ "assets/2.0x/image.png",
+ "assets/3.0x/image.png",
+ "assets/4.0x/image.png"
]
}
-''') as Map<dynamic, dynamic>;
+''';
class TestAssetBundle extends CachingAssetBundle {
+ TestAssetBundle({ this.manifest = testManifest });
- TestAssetBundle({ required Map<dynamic, dynamic> manifest }) {
- this.manifest = const StandardMessageCodec().encodeMessage(manifest)!;
- }
-
- late final ByteData manifest;
+ final String manifest;
@override
Future<ByteData> load(String key) {
late ByteData data;
switch (key) {
- case 'AssetManifest.bin':
- data = manifest;
- break;
case 'assets/image.png':
data = testByteData(1.0);
break;
@@ -66,6 +60,14 @@
}
@override
+ Future<String> loadString(String key, { bool cache = true }) {
+ if (key == 'AssetManifest.json') {
+ return SynchronousFuture<String>(manifest);
+ }
+ return SynchronousFuture<String>('');
+ }
+
+ @override
String toString() => '${describeIdentity(this)}()';
}
@@ -104,7 +106,7 @@
devicePixelRatio: ratio,
),
child: DefaultAssetBundle(
- bundle: bundle ?? TestAssetBundle(manifest: testManifest),
+ bundle: bundle ?? TestAssetBundle(),
child: Center(
child: inferSize ?
Image(
@@ -257,21 +259,46 @@
expect(getRenderImage(tester, key).scale, 4.0);
});
+ testWidgets('Image for device pixel ratio 1.0, with no main asset', (WidgetTester tester) async {
+ const String manifest = '''
+ {
+ "assets/image.png" : [
+ "assets/1.5x/image.png",
+ "assets/2.0x/image.png",
+ "assets/3.0x/image.png",
+ "assets/4.0x/image.png"
+ ]
+ }
+ ''';
+ final AssetBundle bundle = TestAssetBundle(manifest: manifest);
+
+ const double ratio = 1.0;
+ Key key = GlobalKey();
+ await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, false, images, bundle));
+ expect(getRenderImage(tester, key).size, const Size(200.0, 200.0));
+ expect(getRenderImage(tester, key).scale, 1.5);
+ key = GlobalKey();
+ await pumpTreeToLayout(tester, buildImageAtRatio(image, key, ratio, true, images, bundle));
+ expect(getRenderImage(tester, key).size, const Size(48.0, 48.0));
+ expect(getRenderImage(tester, key).scale, 1.5);
+ });
+
testWidgets('Image for device pixel ratio 1.0, with a main asset and a 1.0x asset', (WidgetTester tester) async {
// If both a main asset and a 1.0x asset are specified, then prefer
// the 1.0x asset.
- final Map<dynamic, dynamic> manifest = json.decode('''
+ const String manifest = '''
{
"assets/image.png" : [
- {"asset": "assets/1.0x/image.png", "dpr":1.0},
- {"asset": "assets/1.5x/image.png", "dpr":1.5},
- {"asset": "assets/2.0x/image.png", "dpr":2.0},
- {"asset": "assets/3.0x/image.png", "dpr":3.0},
- {"asset": "assets/4.0x/image.png", "dpr":4.0}
+ "assets/image.png",
+ "assets/1.0x/image.png",
+ "assets/1.5x/image.png",
+ "assets/2.0x/image.png",
+ "assets/3.0x/image.png",
+ "assets/4.0x/image.png"
]
}
- ''') as Map<dynamic, dynamic>;
+ ''';
final AssetBundle bundle = TestAssetBundle(manifest: manifest);
const double ratio = 1.0;
@@ -310,13 +337,14 @@
// if higher resolution assets are not available we will pick the best
// available.
testWidgets('Low-resolution assets', (WidgetTester tester) async {
- final AssetBundle bundle = TestAssetBundle(manifest: json.decode('''
+ final AssetBundle bundle = TestAssetBundle(manifest: '''
{
"assets/image.png" : [
- {"asset": "assets/1.5x/image.png", "dpr": 1.5}
+ "assets/image.png",
+ "assets/1.5x/image.png"
]
}
- ''') as Map<dynamic, dynamic>);
+ ''');
Future<void> testRatio({required double ratio, required double expectedScale}) async {
Key key = GlobalKey();
diff --git a/packages/flutter_tools/lib/src/asset.dart b/packages/flutter_tools/lib/src/asset.dart
index 0aed9af..ea0104f 100644
--- a/packages/flutter_tools/lib/src/asset.dart
+++ b/packages/flutter_tools/lib/src/asset.dart
@@ -2,11 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'dart:typed_data';
-
import 'package:meta/meta.dart';
import 'package:package_config/package_config.dart';
-import 'package:standard_message_codec/standard_message_codec.dart';
import 'base/context.dart';
import 'base/deferred_component.dart';
@@ -139,9 +136,6 @@
_splitDeferredAssets = splitDeferredAssets,
_licenseCollector = LicenseCollector(fileSystem: fileSystem);
- // We assume the main asset is designed for a device pixel ratio of 1.0
- static const double _defaultResolution = 1.0;
-
final Logger _logger;
final FileSystem _fileSystem;
final LicenseCollector _licenseCollector;
@@ -167,8 +161,7 @@
DateTime? _lastBuildTimestamp;
- static const String _kAssetManifestBinFileName = 'AssetManifest.bin';
- static const String _kAssetManifestJsonFileName = 'AssetManifest.json';
+ static const String _kAssetManifestJson = 'AssetManifest.json';
static const String _kNoticeFile = 'NOTICES';
// Comically, this can't be name with the more common .gz file extension
// because when it's part of an AAR and brought into another APK via gradle,
@@ -236,13 +229,8 @@
// device.
_lastBuildTimestamp = DateTime.now();
if (flutterManifest.isEmpty) {
- entries[_kAssetManifestJsonFileName] = DevFSStringContent('{}');
- entryKinds[_kAssetManifestJsonFileName] = AssetKind.regular;
- final ByteData emptyAssetManifest =
- const StandardMessageCodec().encodeMessage(<dynamic, dynamic>{})!;
- entries[_kAssetManifestBinFileName] =
- DevFSByteContent(emptyAssetManifest.buffer.asUint8List(0, emptyAssetManifest.lengthInBytes));
- entryKinds[_kAssetManifestBinFileName] = AssetKind.regular;
+ entries[_kAssetManifestJson] = DevFSStringContent('{}');
+ entryKinds[_kAssetManifestJson] = AssetKind.regular;
return 0;
}
@@ -439,11 +427,7 @@
_wildcardDirectories[uri] ??= _fileSystem.directory(uri);
}
- final Map<String, List<String>> assetManifest =
- _createAssetManifest(assetVariants, deferredComponentsAssetVariants);
- final DevFSStringContent assetManifestJson = DevFSStringContent(json.encode(assetManifest));
- final DevFSByteContent assetManifestBinary = _createAssetManifestBinary(assetManifest);
-
+ final DevFSStringContent assetManifest = _createAssetManifest(assetVariants, deferredComponentsAssetVariants);
final DevFSStringContent fontManifest = DevFSStringContent(json.encode(fonts));
final LicenseResult licenseResult = _licenseCollector.obtainLicenses(packageConfig, additionalLicenseFiles);
if (licenseResult.errorMessages.isNotEmpty) {
@@ -467,8 +451,7 @@
_fileSystem.file('DOES_NOT_EXIST_RERUN_FOR_WILDCARD$suffix').absolute);
}
- _setIfChanged(_kAssetManifestJsonFileName, assetManifestJson, AssetKind.regular);
- _setIfChanged(_kAssetManifestBinFileName, assetManifestBinary, AssetKind.regular);
+ _setIfChanged(_kAssetManifestJson, assetManifest, AssetKind.regular);
_setIfChanged(kFontManifestJson, fontManifest, AssetKind.regular);
_setLicenseIfChanged(licenseResult.combinedLicenses, targetPlatform);
return 0;
@@ -477,31 +460,17 @@
@override
List<File> additionalDependencies = <File>[];
- void _setIfChanged(String key, DevFSContent content, AssetKind assetKind) {
- bool areEqual(List<int> o1, List<int> o2) {
- if (o1.length != o2.length) {
- return false;
- }
-
- for (int index = 0; index < o1.length; index++) {
- if (o1[index] != o2[index]) {
- return false;
- }
- }
-
- return true;
- }
-
- final DevFSContent? oldContent = entries[key];
- // In the case that the content is unchanged, we want to avoid an overwrite
- // as the isModified property may be reset to true,
- if (oldContent is DevFSByteContent && content is DevFSByteContent &&
- areEqual(oldContent.bytes, content.bytes)) {
+ void _setIfChanged(String key, DevFSStringContent content, AssetKind assetKind) {
+ if (!entries.containsKey(key)) {
+ entries[key] = content;
+ entryKinds[key] = assetKind;
return;
}
-
- entries[key] = content;
- entryKinds[key] = assetKind;
+ final DevFSStringContent? oldContent = entries[key] as DevFSStringContent?;
+ if (oldContent?.string != content.string) {
+ entries[key] = content;
+ entryKinds[key] = assetKind;
+ }
}
void _setLicenseIfChanged(
@@ -653,14 +622,14 @@
return deferredComponentsAssetVariants;
}
- Map<String, List<String>> _createAssetManifest(
+ DevFSStringContent _createAssetManifest(
Map<_Asset, List<_Asset>> assetVariants,
Map<String, Map<_Asset, List<_Asset>>> deferredComponentsAssetVariants
) {
- final Map<String, List<String>> manifest = <String, List<String>>{};
- final Map<_Asset, List<String>> entries = <_Asset, List<String>>{};
+ final Map<String, List<String>> jsonObject = <String, List<String>>{};
+ final Map<_Asset, List<String>> jsonEntries = <_Asset, List<String>>{};
assetVariants.forEach((_Asset main, List<_Asset> variants) {
- entries[main] = <String>[
+ jsonEntries[main] = <String>[
for (final _Asset variant in variants)
variant.entryUri.path,
];
@@ -668,69 +637,26 @@
if (deferredComponentsAssetVariants != null) {
for (final Map<_Asset, List<_Asset>> componentAssets in deferredComponentsAssetVariants.values) {
componentAssets.forEach((_Asset main, List<_Asset> variants) {
- entries[main] = <String>[
+ jsonEntries[main] = <String>[
for (final _Asset variant in variants)
variant.entryUri.path,
];
});
}
}
- final List<_Asset> sortedKeys = entries.keys.toList()
+ final List<_Asset> sortedKeys = jsonEntries.keys.toList()
..sort((_Asset left, _Asset right) => left.entryUri.path.compareTo(right.entryUri.path));
for (final _Asset main in sortedKeys) {
final String decodedEntryPath = Uri.decodeFull(main.entryUri.path);
- final List<String> rawEntryVariantsPaths = entries[main]!;
+ final List<String> rawEntryVariantsPaths = jsonEntries[main]!;
final List<String> decodedEntryVariantPaths = rawEntryVariantsPaths
.map((String value) => Uri.decodeFull(value))
.toList();
- manifest[decodedEntryPath] = decodedEntryVariantPaths;
+ jsonObject[decodedEntryPath] = decodedEntryVariantPaths;
}
- return manifest;
+ return DevFSStringContent(json.encode(jsonObject));
}
- DevFSByteContent _createAssetManifestBinary(
- Map<String, List<String>> assetManifest
- ) {
- double parseScale(String key) {
- final Uri assetUri = Uri.parse(key);
- String directoryPath = '';
- if (assetUri.pathSegments.length > 1) {
- directoryPath = assetUri.pathSegments[assetUri.pathSegments.length - 2];
- }
-
- final Match? match = _extractRatioRegExp.firstMatch(directoryPath);
- if (match != null && match.groupCount > 0) {
- return double.parse(match.group(1)!);
- }
- return _defaultResolution;
- }
-
- final Map<String, dynamic> result = <String, dynamic>{};
-
- for (final MapEntry<String, dynamic> manifestEntry in assetManifest.entries) {
- final List<dynamic> resultVariants = <dynamic>[];
- final List<String> entries = (manifestEntry.value as List<dynamic>).cast<String>();
- for (final String variant in entries) {
- if (variant == manifestEntry.key) {
- // With the newer binary format, don't include the main asset in it's
- // list of variants. This reduces parsing time at runtime.
- continue;
- }
- final Map<String, dynamic> resultVariant = <String, dynamic>{};
- final double variantDevicePixelRatio = parseScale(variant);
- resultVariant['asset'] = variant;
- resultVariant['dpr'] = variantDevicePixelRatio;
- resultVariants.add(resultVariant);
- }
- result[manifestEntry.key] = resultVariants;
- }
-
- final ByteData message = const StandardMessageCodec().encodeMessage(result)!;
- return DevFSByteContent(message.buffer.asUint8List(0, message.lengthInBytes));
- }
-
- static final RegExp _extractRatioRegExp = RegExp(r'/?(\d+(\.\d*)?)x$');
-
/// Prefixes family names and asset paths of fonts included from packages with
/// 'packages/<package_name>'
List<Font> _parsePackageFonts(
diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml
index aa69dcc..91dafbe 100644
--- a/packages/flutter_tools/pubspec.yaml
+++ b/packages/flutter_tools/pubspec.yaml
@@ -57,8 +57,6 @@
vm_service: 9.4.0
- standard_message_codec: 0.0.1+3
-
_fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
@@ -90,6 +88,7 @@
watcher: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
dev_dependencies:
+ collection: 1.17.0
file_testing: 3.0.0
pubspec_parse: 1.2.1
@@ -98,10 +97,9 @@
json_annotation: 4.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
test: 1.22.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
- collection: 1.17.0
dartdoc:
# Exclude this package from the hosted API docs.
nodoc: true
-# PUBSPEC CHECKSUM: 408d
+# PUBSPEC CHECKSUM: 65eb
diff --git a/packages/flutter_tools/test/general.shard/android/deferred_components_gen_snapshot_validator_test.dart b/packages/flutter_tools/test/general.shard/android/deferred_components_gen_snapshot_validator_test.dart
index 9366653..6962b50 100644
--- a/packages/flutter_tools/test/general.shard/android/deferred_components_gen_snapshot_validator_test.dart
+++ b/packages/flutter_tools/test/general.shard/android/deferred_components_gen_snapshot_validator_test.dart
@@ -220,7 +220,7 @@
expect(logger.statusText, contains('Errors checking the following files:'));
expect(logger.statusText, contains("Invalid loading units yaml file, 'loading-units' entry did not exist."));
- expect(logger.statusText, isNot(contains('Previously existing loading units no longer exist:\n\n LoadingUnit 2\n Libraries:\n - lib1\n')));
+ expect(logger.statusText.contains('Previously existing loading units no longer exist:\n\n LoadingUnit 2\n Libraries:\n - lib1\n'), false);
});
testWithoutContext('loadingUnitCache validator detects malformed file: not a list', () async {
@@ -382,7 +382,7 @@
validator.displayResults();
validator.attemptToolExit();
- expect(logger.statusText, isNot(contains('Errors checking the following files:')));
+ expect(logger.statusText.contains('Errors checking the following files:'), false);
});
testWithoutContext('androidStringMapping modifies strings file', () async {
@@ -448,10 +448,9 @@
.childDirectory('main')
.childFile('AndroidManifest.xml');
expect(manifestOutput.existsSync(), true);
- final String manifestOutputString = manifestOutput.readAsStringSync();
- expect(manifestOutputString, contains('<meta-data android:name="io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager.loadingUnitMapping" android:value="3:component1,2:component2,4:component2"/>'));
- expect(manifestOutputString, isNot(contains('android:value="invalidmapping"')));
- expect(manifestOutputString, contains("<!-- Don't delete the meta-data below."));
+ expect(manifestOutput.readAsStringSync().contains('<meta-data android:name="io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager.loadingUnitMapping" android:value="3:component1,2:component2,4:component2"/>'), true);
+ expect(manifestOutput.readAsStringSync().contains('android:value="invalidmapping"'), false);
+ expect(manifestOutput.readAsStringSync().contains("<!-- Don't delete the meta-data below."), true);
});
testWithoutContext('androidStringMapping adds mapping when no existing mapping', () async {
@@ -696,8 +695,8 @@
.childDirectory('main')
.childFile('AndroidManifest.xml');
expect(manifestOutput.existsSync(), true);
- expect(manifestOutput.readAsStringSync(), contains('<meta-data android:name="io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager.loadingUnitMapping" android:value="3:component1,2:component2,4:component2"/>'));
- expect(manifestOutput.readAsStringSync(), isNot(contains(RegExp(r'android:value[\s\n]*=[\s\n]*"invalidmapping"'))));
- expect(manifestOutput.readAsStringSync(), contains("<!-- Don't delete the meta-data below."));
+ expect(manifestOutput.readAsStringSync().contains('<meta-data android:name="io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager.loadingUnitMapping" android:value="3:component1,2:component2,4:component2"/>'), true);
+ expect(manifestOutput.readAsStringSync().contains(RegExp(r'android:value[\s\n]*=[\s\n]*"invalidmapping"')), false);
+ expect(manifestOutput.readAsStringSync().contains("<!-- Don't delete the meta-data below."), true);
});
}
diff --git a/packages/flutter_tools/test/general.shard/asset_bundle_package_fonts_test.dart b/packages/flutter_tools/test/general.shard/asset_bundle_package_fonts_test.dart
index 4676d68..f44cd20 100644
--- a/packages/flutter_tools/test/general.shard/asset_bundle_package_fonts_test.dart
+++ b/packages/flutter_tools/test/general.shard/asset_bundle_package_fonts_test.dart
@@ -111,9 +111,8 @@
final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
await bundle.build(packagesPath: '.packages');
- expect(bundle.entries.keys, containsAll(
- <String>['AssetManifest.bin', 'AssetManifest.json', 'FontManifest.json', 'NOTICES.Z']
- ));
+ expect(bundle.entries.length, 3); // LICENSE, AssetManifest, FontManifest
+ expect(bundle.entries.containsKey('FontManifest.json'), isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
diff --git a/packages/flutter_tools/test/general.shard/asset_bundle_package_test.dart b/packages/flutter_tools/test/general.shard/asset_bundle_package_test.dart
index 2070a75..5c2ef1d 100644
--- a/packages/flutter_tools/test/general.shard/asset_bundle_package_test.dart
+++ b/packages/flutter_tools/test/general.shard/asset_bundle_package_test.dart
@@ -3,7 +3,6 @@
// found in the LICENSE file.
import 'dart:convert';
-import 'dart:typed_data';
import 'package:file/file.dart';
import 'package:file/memory.dart';
@@ -12,7 +11,6 @@
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
-import 'package:standard_message_codec/standard_message_codec.dart';
import '../src/common.dart';
import '../src/context.dart';
@@ -26,7 +24,6 @@
// rolls into Flutter.
return path.replaceAll('/', globals.fs.path.separator);
}
-
void writePubspecFile(String path, String name, { List<String>? assets }) {
String assetsSection;
if (assets == null) {
@@ -63,6 +60,37 @@
..writeAsStringSync(packages);
}
+ Future<void> buildAndVerifyAssets(
+ List<String> assets,
+ List<String> packages,
+ String? expectedAssetManifest, {
+ bool expectExists = true,
+ }) async {
+ final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
+ await bundle.build(packagesPath: '.packages');
+
+ for (final String packageName in packages) {
+ for (final String asset in assets) {
+ final String entryKey = Uri.encodeFull('packages/$packageName/$asset');
+ expect(bundle.entries.containsKey(entryKey), expectExists,
+ reason: 'Cannot find key on bundle: $entryKey');
+ if (expectExists) {
+ expect(
+ utf8.decode(await bundle.entries[entryKey]!.contentsAsBytes()),
+ asset,
+ );
+ }
+ }
+ }
+
+ if (expectExists) {
+ expect(
+ utf8.decode(await bundle.entries['AssetManifest.json']!.contentsAsBytes()),
+ expectedAssetManifest,
+ );
+ }
+ }
+
void writeAssets(String path, List<String> assets) {
for (final String asset in assets) {
final String fullPath = fixPath(globals.fs.path.join(path, asset));
@@ -73,391 +101,71 @@
}
}
- // TODO(andrewkolos): Delete this group once we stop producing AssetManifest.json
- // as part of build.
- group('Legacy asset manifest (AssetManifest.json)', () {
- Future<void> buildAndVerifyAssets(
- List<String> assets,
- List<String> packages,
- String? expectedAssetManifest, {
- bool expectExists = true,
- }) async {
+ late FileSystem testFileSystem;
+
+ setUp(() async {
+ testFileSystem = MemoryFileSystem(
+ style: globals.platform.isWindows
+ ? FileSystemStyle.windows
+ : FileSystemStyle.posix,
+ );
+ testFileSystem.currentDirectory = testFileSystem.systemTempDirectory.createTempSync('flutter_asset_bundle_test.');
+ });
+
+ group('AssetBundle assets from packages', () {
+ testUsingContext('No assets are bundled when the package has no assets', () async {
+ writePubspecFile('pubspec.yaml', 'test');
+ writePackagesFile('test_package:p/p/lib/');
+ writePubspecFile('p/p/pubspec.yaml', 'test_package');
+
final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
await bundle.build(packagesPath: '.packages');
-
- for (final String packageName in packages) {
- for (final String asset in assets) {
- final String entryKey = Uri.encodeFull('packages/$packageName/$asset');
- expect(bundle.entries.containsKey(entryKey), expectExists,
- reason: 'Cannot find key on bundle: $entryKey');
- if (expectExists) {
- expect(
- utf8.decode(await bundle.entries[entryKey]!.contentsAsBytes()),
- asset,
- );
- }
- }
- }
-
- if (expectExists) {
- expect(
- utf8.decode(await bundle.entries['AssetManifest.json']!.contentsAsBytes()),
- expectedAssetManifest,
- );
- }
- }
-
- late FileSystem testFileSystem;
-
- setUp(() async {
- testFileSystem = MemoryFileSystem(
- style: globals.platform.isWindows
- ? FileSystemStyle.windows
- : FileSystemStyle.posix,
+ expect(bundle.entries.length, 3); // LICENSE, AssetManifest, FontManifest
+ const String expectedAssetManifest = '{}';
+ expect(
+ utf8.decode(await bundle.entries['AssetManifest.json']!.contentsAsBytes()),
+ expectedAssetManifest,
);
- testFileSystem.currentDirectory = testFileSystem.systemTempDirectory.createTempSync('flutter_asset_bundle_test.');
+ expect(
+ utf8.decode(await bundle.entries['FontManifest.json']!.contentsAsBytes()),
+ '[]',
+ );
+ }, overrides: <Type, Generator>{
+ FileSystem: () => testFileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
});
- group('AssetBundle assets from packages', () {
- testUsingContext('No assets are bundled when the package has no assets', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
- writePubspecFile('p/p/pubspec.yaml', 'test_package');
+ testUsingContext('No assets are bundled when the package has an asset that is not listed', () async {
+ writePubspecFile('pubspec.yaml', 'test');
+ writePackagesFile('test_package:p/p/lib/');
+ writePubspecFile('p/p/pubspec.yaml', 'test_package');
- final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
- await bundle.build(packagesPath: '.packages');
- expect(bundle.entries.keys, unorderedEquals(
- <String>['AssetManifest.bin', 'AssetManifest.json', 'FontManifest.json', 'NOTICES.Z']
- ));
- const String expectedAssetManifest = '{}';
- expect(
- utf8.decode(await bundle.entries['AssetManifest.json']!.contentsAsBytes()),
- expectedAssetManifest,
- );
- expect(
- utf8.decode(await bundle.entries['FontManifest.json']!.contentsAsBytes()),
- '[]',
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
+ final List<String> assets = <String>['a/foo'];
+ writeAssets('p/p/', assets);
- testUsingContext('No assets are bundled when the package has an asset that is not listed', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
- writePubspecFile('p/p/pubspec.yaml', 'test_package');
-
- final List<String> assets = <String>['a/foo'];
- writeAssets('p/p/', assets);
-
- final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
- await bundle.build(packagesPath: '.packages');
- expect(bundle.entries.keys, unorderedEquals(
- <String>['AssetManifest.bin', 'AssetManifest.json', 'FontManifest.json', 'NOTICES.Z']
- ));
- const String expectedAssetManifest = '{}';
- expect(
- utf8.decode(await bundle.entries['AssetManifest.json']!.contentsAsBytes()),
- expectedAssetManifest,
- );
- expect(
- utf8.decode(await bundle.entries['FontManifest.json']!.contentsAsBytes()),
- '[]',
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('One asset is bundled when the package has and lists one '
- 'asset its pubspec', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
-
- final List<String> assets = <String>['a/foo'];
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: assets,
- );
-
- writeAssets('p/p/', assets);
-
- const String expectedAssetManifest = '{"packages/test_package/a/foo":'
- '["packages/test_package/a/foo"]}';
- await buildAndVerifyAssets(
- assets,
- <String>['test_package'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('One asset is bundled when the package has one asset, '
- "listed in the app's pubspec", () async {
- final List<String> assetEntries = <String>['packages/test_package/a/foo'];
- writePubspecFile(
- 'pubspec.yaml',
- 'test',
- assets: assetEntries,
- );
- writePackagesFile('test_package:p/p/lib/');
- writePubspecFile('p/p/pubspec.yaml', 'test_package');
-
- final List<String> assets = <String>['a/foo'];
- writeAssets('p/p/lib/', assets);
-
- const String expectedAssetManifest = '{"packages/test_package/a/foo":'
- '["packages/test_package/a/foo"]}';
- await buildAndVerifyAssets(
- assets,
- <String>['test_package'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('One asset and its variant are bundled when the package '
- 'has an asset and a variant, and lists the asset in its pubspec', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: <String>['a/foo', 'a/bar'],
- );
-
- final List<String> assets = <String>['a/foo', 'a/2x/foo', 'a/bar'];
- writeAssets('p/p/', assets);
-
- const String expectedManifest = '{'
- '"packages/test_package/a/bar":'
- '["packages/test_package/a/bar"],'
- '"packages/test_package/a/foo":'
- '["packages/test_package/a/foo","packages/test_package/a/2x/foo"]'
- '}';
-
- await buildAndVerifyAssets(
- assets,
- <String>['test_package'],
- expectedManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('One asset and its variant are bundled when the package '
- 'has an asset and a variant, and the app lists the asset in its pubspec', () async {
- writePubspecFile(
- 'pubspec.yaml',
- 'test',
- assets: <String>['packages/test_package/a/foo'],
- );
- writePackagesFile('test_package:p/p/lib/');
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- );
-
- final List<String> assets = <String>['a/foo', 'a/2x/foo'];
- writeAssets('p/p/lib/', assets);
-
- const String expectedManifest = '{"packages/test_package/a/foo":'
- '["packages/test_package/a/foo","packages/test_package/a/2x/foo"]}';
-
- await buildAndVerifyAssets(
- assets,
- <String>['test_package'],
- expectedManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('Two assets are bundled when the package has and lists '
- 'two assets in its pubspec', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
-
- final List<String> assets = <String>['a/foo', 'a/bar'];
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: assets,
- );
-
- writeAssets('p/p/', assets);
- const String expectedAssetManifest =
- '{"packages/test_package/a/bar":["packages/test_package/a/bar"],'
- '"packages/test_package/a/foo":["packages/test_package/a/foo"]}';
-
- await buildAndVerifyAssets(
- assets,
- <String>['test_package'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext("Two assets are bundled when the package has two assets, listed in the app's pubspec", () async {
- final List<String> assetEntries = <String>[
- 'packages/test_package/a/foo',
- 'packages/test_package/a/bar',
- ];
- writePubspecFile(
- 'pubspec.yaml',
- 'test',
- assets: assetEntries,
- );
- writePackagesFile('test_package:p/p/lib/');
-
- final List<String> assets = <String>['a/foo', 'a/bar'];
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- );
-
- writeAssets('p/p/lib/', assets);
- const String expectedAssetManifest =
- '{"packages/test_package/a/bar":["packages/test_package/a/bar"],'
- '"packages/test_package/a/foo":["packages/test_package/a/foo"]}';
-
- await buildAndVerifyAssets(
- assets,
- <String>['test_package'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('Two assets are bundled when two packages each have and list an asset their pubspec', () async {
- writePubspecFile(
- 'pubspec.yaml',
- 'test',
- );
- writePackagesFile('test_package:p/p/lib/\ntest_package2:p2/p/lib/');
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: <String>['a/foo'],
- );
- writePubspecFile(
- 'p2/p/pubspec.yaml',
- 'test_package2',
- assets: <String>['a/foo'],
- );
-
- final List<String> assets = <String>['a/foo', 'a/2x/foo'];
- writeAssets('p/p/', assets);
- writeAssets('p2/p/', assets);
-
- const String expectedAssetManifest =
- '{"packages/test_package/a/foo":'
- '["packages/test_package/a/foo","packages/test_package/a/2x/foo"],'
- '"packages/test_package2/a/foo":'
- '["packages/test_package2/a/foo","packages/test_package2/a/2x/foo"]}';
-
- await buildAndVerifyAssets(
- assets,
- <String>['test_package', 'test_package2'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext("Two assets are bundled when two packages each have an asset, listed in the app's pubspec", () async {
- final List<String> assetEntries = <String>[
- 'packages/test_package/a/foo',
- 'packages/test_package2/a/foo',
- ];
- writePubspecFile(
- 'pubspec.yaml',
- 'test',
- assets: assetEntries,
- );
- writePackagesFile('test_package:p/p/lib/\ntest_package2:p2/p/lib/');
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- );
- writePubspecFile(
- 'p2/p/pubspec.yaml',
- 'test_package2',
- );
-
- final List<String> assets = <String>['a/foo', 'a/2x/foo'];
- writeAssets('p/p/lib/', assets);
- writeAssets('p2/p/lib/', assets);
-
- const String expectedAssetManifest =
- '{"packages/test_package/a/foo":'
- '["packages/test_package/a/foo","packages/test_package/a/2x/foo"],'
- '"packages/test_package2/a/foo":'
- '["packages/test_package2/a/foo","packages/test_package2/a/2x/foo"]}';
-
- await buildAndVerifyAssets(
- assets,
- <String>['test_package', 'test_package2'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('One asset is bundled when the app depends on a package, '
- 'listing in its pubspec an asset from another package', () async {
- writePubspecFile(
- 'pubspec.yaml',
- 'test',
- );
- writePackagesFile('test_package:p/p/lib/\ntest_package2:p2/p/lib/');
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: <String>['packages/test_package2/a/foo'],
- );
- writePubspecFile(
- 'p2/p/pubspec.yaml',
- 'test_package2',
- );
-
- final List<String> assets = <String>['a/foo', 'a/2x/foo'];
- writeAssets('p2/p/lib/', assets);
-
- const String expectedAssetManifest =
- '{"packages/test_package2/a/foo":'
- '["packages/test_package2/a/foo","packages/test_package2/a/2x/foo"]}';
-
- await buildAndVerifyAssets(
- assets,
- <String>['test_package2'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
+ final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
+ await bundle.build(packagesPath: '.packages');
+ expect(bundle.entries.length, 3); // LICENSE, AssetManifest, FontManifest
+ const String expectedAssetManifest = '{}';
+ expect(
+ utf8.decode(await bundle.entries['AssetManifest.json']!.contentsAsBytes()),
+ expectedAssetManifest,
+ );
+ expect(
+ utf8.decode(await bundle.entries['FontManifest.json']!.contentsAsBytes()),
+ '[]',
+ );
+ }, overrides: <Type, Generator>{
+ FileSystem: () => testFileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
});
- testUsingContext('Asset paths can contain URL reserved characters', () async {
+ testUsingContext('One asset is bundled when the package has and lists one '
+ 'asset its pubspec', () async {
writePubspecFile('pubspec.yaml', 'test');
writePackagesFile('test_package:p/p/lib/');
- final List<String> assets = <String>['a/foo', 'a/foo [x]'];
+ final List<String> assets = <String>['a/foo'];
writePubspecFile(
'p/p/pubspec.yaml',
'test_package',
@@ -465,10 +173,9 @@
);
writeAssets('p/p/', assets);
- const String expectedAssetManifest =
- '{"packages/test_package/a/foo":["packages/test_package/a/foo"],'
- '"packages/test_package/a/foo [x]":["packages/test_package/a/foo [x]"]}';
+ const String expectedAssetManifest = '{"packages/test_package/a/foo":'
+ '["packages/test_package/a/foo"]}';
await buildAndVerifyAssets(
assets,
<String>['test_package'],
@@ -476,571 +183,100 @@
);
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
+ ProcessManager: () => FakeProcessManager.any(),
});
- group('AssetBundle assets from scanned paths', () {
- testUsingContext('Two assets are bundled when scanning their directory', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
-
- final List<String> assetsOnDisk = <String>['a/foo', 'a/bar'];
- final List<String> assetsOnManifest = <String>['a/'];
-
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: assetsOnManifest,
- );
-
- writeAssets('p/p/', assetsOnDisk);
- const String expectedAssetManifest =
- '{"packages/test_package/a/bar":["packages/test_package/a/bar"],'
- '"packages/test_package/a/foo":["packages/test_package/a/foo"]}';
-
- await buildAndVerifyAssets(
- assetsOnDisk,
- <String>['test_package'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('Two assets are bundled when listing one and scanning second directory', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
-
- final List<String> assetsOnDisk = <String>['a/foo', 'abc/bar'];
- final List<String> assetOnManifest = <String>['a/foo', 'abc/'];
-
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: assetOnManifest,
- );
-
- writeAssets('p/p/', assetsOnDisk);
- const String expectedAssetManifest =
- '{"packages/test_package/a/foo":["packages/test_package/a/foo"],'
- '"packages/test_package/abc/bar":["packages/test_package/abc/bar"]}';
-
- await buildAndVerifyAssets(
- assetsOnDisk,
- <String>['test_package'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('One asset is bundled with variant, scanning wrong directory', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
-
- final List<String> assetsOnDisk = <String>['a/foo','a/b/foo','a/bar'];
- final List<String> assetOnManifest = <String>['a','a/bar']; // can't list 'a' as asset, should be 'a/'
-
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: assetOnManifest,
- );
-
- writeAssets('p/p/', assetsOnDisk);
-
- final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
- await bundle.build(packagesPath: '.packages');
-
- expect(bundle.entries['AssetManifest.bin'], isNull,
- reason: 'Invalid pubspec.yaml should not generate AssetManifest.bin' );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
- });
-
- group('AssetBundle assets from scanned paths with MemoryFileSystem', () {
- testUsingContext('One asset is bundled with variant, scanning directory', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
-
- final List<String> assetsOnDisk = <String>['a/foo','a/2x/foo'];
- final List<String> assetOnManifest = <String>['a/',];
-
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: assetOnManifest,
- );
-
- writeAssets('p/p/', assetsOnDisk);
- const String expectedAssetManifest =
- '{"packages/test_package/a/foo":["packages/test_package/a/foo","packages/test_package/a/2x/foo"]}';
-
- await buildAndVerifyAssets(
- assetsOnDisk,
- <String>['test_package'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('No asset is bundled with variant, no assets or directories are listed', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
-
- final List<String> assetsOnDisk = <String>['a/foo', 'a/2x/foo'];
- final List<String> assetOnManifest = <String>[];
-
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: assetOnManifest,
- );
-
- writeAssets('p/p/', assetsOnDisk);
- const String expectedAssetManifest = '{}';
-
- await buildAndVerifyAssets(
- assetOnManifest,
- <String>['test_package'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('Expect error generating manifest, wrong non-existing directory is listed', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
-
- final List<String> assetOnManifest = <String>['c/'];
-
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: assetOnManifest,
- );
-
- await buildAndVerifyAssets(
- assetOnManifest,
- <String>['test_package'],
- null,
- expectExists: false,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
- });
- });
- group('Current asset manifest (AssetManifest.bin)', () {
- Future<String> extractAssetManifestFromBundleAsJson(AssetBundle bundle) async {
- final List<int> manifestBytes = await bundle.entries['AssetManifest.bin']!.contentsAsBytes();
- return json.encode(const StandardMessageCodec().decodeMessage(
- ByteData.sublistView(Uint8List.fromList(manifestBytes))
- ));
- }
-
- Future<void> buildAndVerifyAssets(
- List<String> assets,
- List<String> packages,
- String? expectedAssetManifest, {
- bool expectExists = true,
- }) async {
- final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
- await bundle.build(packagesPath: '.packages');
-
- for (final String packageName in packages) {
- for (final String asset in assets) {
- final String entryKey = Uri.encodeFull('packages/$packageName/$asset');
- expect(bundle.entries.containsKey(entryKey), expectExists,
- reason: 'Cannot find key on bundle: $entryKey');
- if (expectExists) {
- expect(
- utf8.decode(await bundle.entries[entryKey]!.contentsAsBytes()),
- asset,
- );
- }
- }
- }
-
- if (expectExists) {
- final String actualAssetManifest = await extractAssetManifestFromBundleAsJson(bundle);
- expect(
- actualAssetManifest,
- expectedAssetManifest,
- );
- }
- }
-
- void writeAssets(String path, List<String> assets) {
- for (final String asset in assets) {
- final String fullPath = fixPath(globals.fs.path.join(path, asset));
-
- globals.fs.file(fullPath)
- ..createSync(recursive: true)
- ..writeAsStringSync(asset);
- }
- }
-
- late FileSystem testFileSystem;
-
- setUp(() async {
- testFileSystem = MemoryFileSystem(
- style: globals.platform.isWindows
- ? FileSystemStyle.windows
- : FileSystemStyle.posix,
+ testUsingContext('One asset is bundled when the package has one asset, '
+ "listed in the app's pubspec", () async {
+ final List<String> assetEntries = <String>['packages/test_package/a/foo'];
+ writePubspecFile(
+ 'pubspec.yaml',
+ 'test',
+ assets: assetEntries,
);
- testFileSystem.currentDirectory = testFileSystem.systemTempDirectory.createTempSync('flutter_asset_bundle_test.');
+ writePackagesFile('test_package:p/p/lib/');
+ writePubspecFile('p/p/pubspec.yaml', 'test_package');
+
+ final List<String> assets = <String>['a/foo'];
+ writeAssets('p/p/lib/', assets);
+
+ const String expectedAssetManifest = '{"packages/test_package/a/foo":'
+ '["packages/test_package/a/foo"]}';
+ await buildAndVerifyAssets(
+ assets,
+ <String>['test_package'],
+ expectedAssetManifest,
+ );
+ }, overrides: <Type, Generator>{
+ FileSystem: () => testFileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
});
- group('AssetBundle assets from packages', () {
- testUsingContext('No assets are bundled when the package has no assets', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
- writePubspecFile('p/p/pubspec.yaml', 'test_package');
+ testUsingContext('One asset and its variant are bundled when the package '
+ 'has an asset and a variant, and lists the asset in its pubspec', () async {
+ writePubspecFile('pubspec.yaml', 'test');
+ writePackagesFile('test_package:p/p/lib/');
+ writePubspecFile(
+ 'p/p/pubspec.yaml',
+ 'test_package',
+ assets: <String>['a/foo', 'a/bar'],
+ );
- final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
- await bundle.build(packagesPath: '.packages');
- expect(bundle.entries.keys, unorderedEquals(
- <String>['AssetManifest.bin', 'AssetManifest.json', 'FontManifest.json', 'NOTICES.Z']
- ));
- final String actualAssetManifest = await extractAssetManifestFromBundleAsJson(bundle);
- const String expectedAssetManifest = '{}';
- expect(
- actualAssetManifest,
- expectedAssetManifest,
- );
- expect(
- utf8.decode(await bundle.entries['FontManifest.json']!.contentsAsBytes()),
- '[]',
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
+ final List<String> assets = <String>['a/foo', 'a/2x/foo', 'a/bar'];
+ writeAssets('p/p/', assets);
- testUsingContext('No assets are bundled when the package has an asset that is not listed', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
- writePubspecFile('p/p/pubspec.yaml', 'test_package');
+ const String expectedManifest = '{'
+ '"packages/test_package/a/bar":'
+ '["packages/test_package/a/bar"],'
+ '"packages/test_package/a/foo":'
+ '["packages/test_package/a/foo","packages/test_package/a/2x/foo"]'
+ '}';
- final List<String> assets = <String>['a/foo'];
- writeAssets('p/p/', assets);
-
- final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
- await bundle.build(packagesPath: '.packages');
- expect(bundle.entries.keys, unorderedEquals(
- <String>['AssetManifest.bin', 'AssetManifest.json', 'FontManifest.json', 'NOTICES.Z']
- ));
- final String actualAssetManifest = await extractAssetManifestFromBundleAsJson(bundle);
- const String expectedAssetManifest = '{}';
- expect(
- actualAssetManifest,
- expectedAssetManifest,
- );
- expect(
- utf8.decode(await bundle.entries['FontManifest.json']!.contentsAsBytes()),
- '[]',
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('One asset is bundled when the package has and lists one '
- 'asset its pubspec', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
-
- final List<String> assets = <String>['a/foo'];
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: assets,
- );
-
- writeAssets('p/p/', assets);
-
- const String expectedAssetManifest = '{"packages/test_package/a/foo":'
- '[]}';
- await buildAndVerifyAssets(
- assets,
- <String>['test_package'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('One asset is bundled when the package has one asset, '
- "listed in the app's pubspec", () async {
- final List<String> assetEntries = <String>['packages/test_package/a/foo'];
- writePubspecFile(
- 'pubspec.yaml',
- 'test',
- assets: assetEntries,
- );
- writePackagesFile('test_package:p/p/lib/');
- writePubspecFile('p/p/pubspec.yaml', 'test_package');
-
- final List<String> assets = <String>['a/foo'];
- writeAssets('p/p/lib/', assets);
-
- const String expectedAssetManifest = '{"packages/test_package/a/foo":[]}';
- await buildAndVerifyAssets(
- assets,
- <String>['test_package'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('One asset and its variant are bundled when the package '
- 'has an asset and a variant, and lists the asset in its pubspec', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: <String>['a/foo', 'a/bar'],
- );
-
- final List<String> assets = <String>['a/foo', 'a/2x/foo', 'a/bar'];
- writeAssets('p/p/', assets);
-
- const String expectedManifest = '{'
- '"packages/test_package/a/bar":[],'
- '"packages/test_package/a/foo":['
- '{"asset":"packages/test_package/a/2x/foo","dpr":2.0}]'
- '}';
-
- await buildAndVerifyAssets(
- assets,
- <String>['test_package'],
- expectedManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('One asset and its variant are bundled when the package '
- 'has an asset and a variant, and the app lists the asset in its pubspec', () async {
- writePubspecFile(
- 'pubspec.yaml',
- 'test',
- assets: <String>['packages/test_package/a/foo'],
- );
- writePackagesFile('test_package:p/p/lib/');
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- );
-
- final List<String> assets = <String>['a/foo', 'a/2x/foo'];
- writeAssets('p/p/lib/', assets);
-
- const String expectedManifest = '{"packages/test_package/a/foo":'
- '[{"asset":"packages/test_package/a/2x/foo","dpr":2.0}]}';
-
- await buildAndVerifyAssets(
- assets,
- <String>['test_package'],
- expectedManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('Two assets are bundled when the package has and lists '
- 'two assets in its pubspec', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
-
- final List<String> assets = <String>['a/foo', 'a/bar'];
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: assets,
- );
-
- writeAssets('p/p/', assets);
- const String expectedAssetManifest =
- '{"packages/test_package/a/bar":[],'
- '"packages/test_package/a/foo":[]}';
-
- await buildAndVerifyAssets(
- assets,
- <String>['test_package'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext("Two assets are bundled when the package has two assets, listed in the app's pubspec", () async {
- final List<String> assetEntries = <String>[
- 'packages/test_package/a/foo',
- 'packages/test_package/a/bar',
- ];
- writePubspecFile(
- 'pubspec.yaml',
- 'test',
- assets: assetEntries,
- );
- writePackagesFile('test_package:p/p/lib/');
-
- final List<String> assets = <String>['a/foo', 'a/bar'];
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- );
-
- writeAssets('p/p/lib/', assets);
- const String expectedAssetManifest =
- '{"packages/test_package/a/bar":[],'
- '"packages/test_package/a/foo":[]}';
-
- await buildAndVerifyAssets(
- assets,
- <String>['test_package'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('Two assets are bundled when two packages each have and list an asset their pubspec', () async {
- writePubspecFile(
- 'pubspec.yaml',
- 'test',
- );
- writePackagesFile('test_package:p/p/lib/\ntest_package2:p2/p/lib/');
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: <String>['a/foo'],
- );
- writePubspecFile(
- 'p2/p/pubspec.yaml',
- 'test_package2',
- assets: <String>['a/foo'],
- );
-
- final List<String> assets = <String>['a/foo', 'a/2x/foo'];
- writeAssets('p/p/', assets);
- writeAssets('p2/p/', assets);
-
- const String expectedAssetManifest =
- '{"packages/test_package/a/foo":'
- '[{"asset":"packages/test_package/a/2x/foo","dpr":2.0}],'
- '"packages/test_package2/a/foo":'
- '[{"asset":"packages/test_package2/a/2x/foo","dpr":2.0}]}';
-
- await buildAndVerifyAssets(
- assets,
- <String>['test_package', 'test_package2'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext("Two assets are bundled when two packages each have an asset, listed in the app's pubspec", () async {
- final List<String> assetEntries = <String>[
- 'packages/test_package/a/foo',
- 'packages/test_package2/a/foo',
- ];
- writePubspecFile(
- 'pubspec.yaml',
- 'test',
- assets: assetEntries,
- );
- writePackagesFile('test_package:p/p/lib/\ntest_package2:p2/p/lib/');
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- );
- writePubspecFile(
- 'p2/p/pubspec.yaml',
- 'test_package2',
- );
-
- final List<String> assets = <String>['a/foo', 'a/2x/foo'];
- writeAssets('p/p/lib/', assets);
- writeAssets('p2/p/lib/', assets);
-
- const String expectedAssetManifest =
- '{"packages/test_package/a/foo":'
- '[{"asset":"packages/test_package/a/2x/foo","dpr":2.0}],'
- '"packages/test_package2/a/foo":'
- '[{"asset":"packages/test_package2/a/2x/foo","dpr":2.0}]}';
-
- await buildAndVerifyAssets(
- assets,
- <String>['test_package', 'test_package2'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('One asset is bundled when the app depends on a package, '
- 'listing in its pubspec an asset from another package', () async {
- writePubspecFile(
- 'pubspec.yaml',
- 'test',
- );
- writePackagesFile('test_package:p/p/lib/\ntest_package2:p2/p/lib/');
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: <String>['packages/test_package2/a/foo'],
- );
- writePubspecFile(
- 'p2/p/pubspec.yaml',
- 'test_package2',
- );
-
- final List<String> assets = <String>['a/foo', 'a/2x/foo'];
- writeAssets('p2/p/lib/', assets);
-
- const String expectedAssetManifest =
- '{"packages/test_package2/a/foo":'
- '[{"asset":"packages/test_package2/a/2x/foo","dpr":2.0}]}';
-
- await buildAndVerifyAssets(
- assets,
- <String>['test_package2'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
+ await buildAndVerifyAssets(
+ assets,
+ <String>['test_package'],
+ expectedManifest,
+ );
+ }, overrides: <Type, Generator>{
+ FileSystem: () => testFileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
});
- testUsingContext('Asset paths can contain URL reserved characters', () async {
+ testUsingContext('One asset and its variant are bundled when the package '
+ 'has an asset and a variant, and the app lists the asset in its pubspec', () async {
+ writePubspecFile(
+ 'pubspec.yaml',
+ 'test',
+ assets: <String>['packages/test_package/a/foo'],
+ );
+ writePackagesFile('test_package:p/p/lib/');
+ writePubspecFile(
+ 'p/p/pubspec.yaml',
+ 'test_package',
+ );
+
+ final List<String> assets = <String>['a/foo', 'a/2x/foo'];
+ writeAssets('p/p/lib/', assets);
+
+ const String expectedManifest = '{"packages/test_package/a/foo":'
+ '["packages/test_package/a/foo","packages/test_package/a/2x/foo"]}';
+
+ await buildAndVerifyAssets(
+ assets,
+ <String>['test_package'],
+ expectedManifest,
+ );
+ }, overrides: <Type, Generator>{
+ FileSystem: () => testFileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
+ });
+
+ testUsingContext('Two assets are bundled when the package has and lists '
+ 'two assets in its pubspec', () async {
writePubspecFile('pubspec.yaml', 'test');
writePackagesFile('test_package:p/p/lib/');
- final List<String> assets = <String>['a/foo', 'a/foo [x]'];
+ final List<String> assets = <String>['a/foo', 'a/bar'];
writePubspecFile(
'p/p/pubspec.yaml',
'test_package',
@@ -1049,8 +285,8 @@
writeAssets('p/p/', assets);
const String expectedAssetManifest =
- '{"packages/test_package/a/foo":[],'
- '"packages/test_package/a/foo [x]":[]}';
+ '{"packages/test_package/a/bar":["packages/test_package/a/bar"],'
+ '"packages/test_package/a/foo":["packages/test_package/a/foo"]}';
await buildAndVerifyAssets(
assets,
@@ -1062,165 +298,335 @@
ProcessManager: () => FakeProcessManager.any(),
});
- group('AssetBundle assets from scanned paths', () {
- testUsingContext('Two assets are bundled when scanning their directory', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
+ testUsingContext("Two assets are bundled when the package has two assets, listed in the app's pubspec", () async {
+ final List<String> assetEntries = <String>[
+ 'packages/test_package/a/foo',
+ 'packages/test_package/a/bar',
+ ];
+ writePubspecFile(
+ 'pubspec.yaml',
+ 'test',
+ assets: assetEntries,
+ );
+ writePackagesFile('test_package:p/p/lib/');
- final List<String> assetsOnDisk = <String>['a/foo', 'a/bar'];
- final List<String> assetsOnManifest = <String>['a/'];
+ final List<String> assets = <String>['a/foo', 'a/bar'];
+ writePubspecFile(
+ 'p/p/pubspec.yaml',
+ 'test_package',
+ );
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: assetsOnManifest,
- );
+ writeAssets('p/p/lib/', assets);
+ const String expectedAssetManifest =
+ '{"packages/test_package/a/bar":["packages/test_package/a/bar"],'
+ '"packages/test_package/a/foo":["packages/test_package/a/foo"]}';
- writeAssets('p/p/', assetsOnDisk);
- const String expectedAssetManifest =
- '{"packages/test_package/a/bar":[],'
- '"packages/test_package/a/foo":[]}';
-
- await buildAndVerifyAssets(
- assetsOnDisk,
- <String>['test_package'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('Two assets are bundled when listing one and scanning second directory', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
-
- final List<String> assetsOnDisk = <String>['a/foo', 'abc/bar'];
- final List<String> assetOnManifest = <String>['a/foo', 'abc/'];
-
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: assetOnManifest,
- );
-
- writeAssets('p/p/', assetsOnDisk);
- const String expectedAssetManifest =
- '{"packages/test_package/a/foo":[],'
- '"packages/test_package/abc/bar":[]}';
-
- await buildAndVerifyAssets(
- assetsOnDisk,
- <String>['test_package'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('One asset is bundled with variant, scanning wrong directory', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
-
- final List<String> assetsOnDisk = <String>['a/foo','a/b/foo','a/bar'];
- final List<String> assetOnManifest = <String>['a','a/bar']; // can't list 'a' as asset, should be 'a/'
-
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: assetOnManifest,
- );
-
- writeAssets('p/p/', assetsOnDisk);
-
- final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
- await bundle.build(packagesPath: '.packages');
-
- expect(bundle.entries['AssetManifest.bin'], isNull,
- reason: 'Invalid pubspec.yaml should not generate AssetManifest.bin' );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
+ await buildAndVerifyAssets(
+ assets,
+ <String>['test_package'],
+ expectedAssetManifest,
+ );
+ }, overrides: <Type, Generator>{
+ FileSystem: () => testFileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
});
- group('AssetBundle assets from scanned paths with MemoryFileSystem', () {
- testUsingContext('One asset is bundled with variant, scanning directory', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
+ testUsingContext('Two assets are bundled when two packages each have and list an asset their pubspec', () async {
+ writePubspecFile(
+ 'pubspec.yaml',
+ 'test',
+ );
+ writePackagesFile('test_package:p/p/lib/\ntest_package2:p2/p/lib/');
+ writePubspecFile(
+ 'p/p/pubspec.yaml',
+ 'test_package',
+ assets: <String>['a/foo'],
+ );
+ writePubspecFile(
+ 'p2/p/pubspec.yaml',
+ 'test_package2',
+ assets: <String>['a/foo'],
+ );
- final List<String> assetsOnDisk = <String>['a/foo','a/2x/foo'];
- final List<String> assetOnManifest = <String>['a/',];
+ final List<String> assets = <String>['a/foo', 'a/2x/foo'];
+ writeAssets('p/p/', assets);
+ writeAssets('p2/p/', assets);
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: assetOnManifest,
- );
+ const String expectedAssetManifest =
+ '{"packages/test_package/a/foo":'
+ '["packages/test_package/a/foo","packages/test_package/a/2x/foo"],'
+ '"packages/test_package2/a/foo":'
+ '["packages/test_package2/a/foo","packages/test_package2/a/2x/foo"]}';
- writeAssets('p/p/', assetsOnDisk);
- const String expectedAssetManifest =
- '{"packages/test_package/a/foo":[{"asset":"packages/test_package/a/2x/foo","dpr":2.0}]}';
+ await buildAndVerifyAssets(
+ assets,
+ <String>['test_package', 'test_package2'],
+ expectedAssetManifest,
+ );
+ }, overrides: <Type, Generator>{
+ FileSystem: () => testFileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
+ });
- await buildAndVerifyAssets(
- assetsOnDisk,
- <String>['test_package'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
+ testUsingContext("Two assets are bundled when two packages each have an asset, listed in the app's pubspec", () async {
+ final List<String> assetEntries = <String>[
+ 'packages/test_package/a/foo',
+ 'packages/test_package2/a/foo',
+ ];
+ writePubspecFile(
+ 'pubspec.yaml',
+ 'test',
+ assets: assetEntries,
+ );
+ writePackagesFile('test_package:p/p/lib/\ntest_package2:p2/p/lib/');
+ writePubspecFile(
+ 'p/p/pubspec.yaml',
+ 'test_package',
+ );
+ writePubspecFile(
+ 'p2/p/pubspec.yaml',
+ 'test_package2',
+ );
- testUsingContext('No asset is bundled with variant, no assets or directories are listed', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
+ final List<String> assets = <String>['a/foo', 'a/2x/foo'];
+ writeAssets('p/p/lib/', assets);
+ writeAssets('p2/p/lib/', assets);
- final List<String> assetsOnDisk = <String>['a/foo', 'a/2x/foo'];
- final List<String> assetOnManifest = <String>[];
+ const String expectedAssetManifest =
+ '{"packages/test_package/a/foo":'
+ '["packages/test_package/a/foo","packages/test_package/a/2x/foo"],'
+ '"packages/test_package2/a/foo":'
+ '["packages/test_package2/a/foo","packages/test_package2/a/2x/foo"]}';
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: assetOnManifest,
- );
+ await buildAndVerifyAssets(
+ assets,
+ <String>['test_package', 'test_package2'],
+ expectedAssetManifest,
+ );
+ }, overrides: <Type, Generator>{
+ FileSystem: () => testFileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
+ });
- writeAssets('p/p/', assetsOnDisk);
- const String expectedAssetManifest = '{}';
+ testUsingContext('One asset is bundled when the app depends on a package, '
+ 'listing in its pubspec an asset from another package', () async {
+ writePubspecFile(
+ 'pubspec.yaml',
+ 'test',
+ );
+ writePackagesFile('test_package:p/p/lib/\ntest_package2:p2/p/lib/');
+ writePubspecFile(
+ 'p/p/pubspec.yaml',
+ 'test_package',
+ assets: <String>['packages/test_package2/a/foo'],
+ );
+ writePubspecFile(
+ 'p2/p/pubspec.yaml',
+ 'test_package2',
+ );
- await buildAndVerifyAssets(
- assetOnManifest,
- <String>['test_package'],
- expectedAssetManifest,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
+ final List<String> assets = <String>['a/foo', 'a/2x/foo'];
+ writeAssets('p2/p/lib/', assets);
- testUsingContext('Expect error generating manifest, wrong non-existing directory is listed', () async {
- writePubspecFile('pubspec.yaml', 'test');
- writePackagesFile('test_package:p/p/lib/');
+ const String expectedAssetManifest =
+ '{"packages/test_package2/a/foo":'
+ '["packages/test_package2/a/foo","packages/test_package2/a/2x/foo"]}';
- final List<String> assetOnManifest = <String>['c/'];
+ await buildAndVerifyAssets(
+ assets,
+ <String>['test_package2'],
+ expectedAssetManifest,
+ );
+ }, overrides: <Type, Generator>{
+ FileSystem: () => testFileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
+ });
+ });
- writePubspecFile(
- 'p/p/pubspec.yaml',
- 'test_package',
- assets: assetOnManifest,
- );
+ testUsingContext('Asset paths can contain URL reserved characters', () async {
+ writePubspecFile('pubspec.yaml', 'test');
+ writePackagesFile('test_package:p/p/lib/');
- await buildAndVerifyAssets(
- assetOnManifest,
- <String>['test_package'],
- null,
- expectExists: false,
- );
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
+ final List<String> assets = <String>['a/foo', 'a/foo [x]'];
+ writePubspecFile(
+ 'p/p/pubspec.yaml',
+ 'test_package',
+ assets: assets,
+ );
+
+ writeAssets('p/p/', assets);
+ const String expectedAssetManifest =
+ '{"packages/test_package/a/foo":["packages/test_package/a/foo"],'
+ '"packages/test_package/a/foo [x]":["packages/test_package/a/foo [x]"]}';
+
+ await buildAndVerifyAssets(
+ assets,
+ <String>['test_package'],
+ expectedAssetManifest,
+ );
+ }, overrides: <Type, Generator>{
+ FileSystem: () => testFileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
+ });
+
+ group('AssetBundle assets from scanned paths', () {
+ testUsingContext('Two assets are bundled when scanning their directory', () async {
+ writePubspecFile('pubspec.yaml', 'test');
+ writePackagesFile('test_package:p/p/lib/');
+
+ final List<String> assetsOnDisk = <String>['a/foo', 'a/bar'];
+ final List<String> assetsOnManifest = <String>['a/'];
+
+ writePubspecFile(
+ 'p/p/pubspec.yaml',
+ 'test_package',
+ assets: assetsOnManifest,
+ );
+
+ writeAssets('p/p/', assetsOnDisk);
+ const String expectedAssetManifest =
+ '{"packages/test_package/a/bar":["packages/test_package/a/bar"],'
+ '"packages/test_package/a/foo":["packages/test_package/a/foo"]}';
+
+ await buildAndVerifyAssets(
+ assetsOnDisk,
+ <String>['test_package'],
+ expectedAssetManifest,
+ );
+ }, overrides: <Type, Generator>{
+ FileSystem: () => testFileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
+ });
+
+ testUsingContext('Two assets are bundled when listing one and scanning second directory', () async {
+ writePubspecFile('pubspec.yaml', 'test');
+ writePackagesFile('test_package:p/p/lib/');
+
+ final List<String> assetsOnDisk = <String>['a/foo', 'abc/bar'];
+ final List<String> assetOnManifest = <String>['a/foo', 'abc/'];
+
+ writePubspecFile(
+ 'p/p/pubspec.yaml',
+ 'test_package',
+ assets: assetOnManifest,
+ );
+
+ writeAssets('p/p/', assetsOnDisk);
+ const String expectedAssetManifest =
+ '{"packages/test_package/a/foo":["packages/test_package/a/foo"],'
+ '"packages/test_package/abc/bar":["packages/test_package/abc/bar"]}';
+
+ await buildAndVerifyAssets(
+ assetsOnDisk,
+ <String>['test_package'],
+ expectedAssetManifest,
+ );
+ }, overrides: <Type, Generator>{
+ FileSystem: () => testFileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
+ });
+
+ testUsingContext('One asset is bundled with variant, scanning wrong directory', () async {
+ writePubspecFile('pubspec.yaml', 'test');
+ writePackagesFile('test_package:p/p/lib/');
+
+ final List<String> assetsOnDisk = <String>['a/foo','a/b/foo','a/bar'];
+ final List<String> assetOnManifest = <String>['a','a/bar']; // can't list 'a' as asset, should be 'a/'
+
+ writePubspecFile(
+ 'p/p/pubspec.yaml',
+ 'test_package',
+ assets: assetOnManifest,
+ );
+
+ writeAssets('p/p/', assetsOnDisk);
+
+ final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
+ await bundle.build(packagesPath: '.packages');
+
+ expect(bundle.entries['AssetManifest.json'], isNull,
+ reason: 'Invalid pubspec.yaml should not generate AssetManifest.json' );
+ }, overrides: <Type, Generator>{
+ FileSystem: () => testFileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
+ });
+ });
+
+ group('AssetBundle assets from scanned paths with MemoryFileSystem', () {
+ testUsingContext('One asset is bundled with variant, scanning directory', () async {
+ writePubspecFile('pubspec.yaml', 'test');
+ writePackagesFile('test_package:p/p/lib/');
+
+ final List<String> assetsOnDisk = <String>['a/foo','a/2x/foo'];
+ final List<String> assetOnManifest = <String>['a/',];
+
+ writePubspecFile(
+ 'p/p/pubspec.yaml',
+ 'test_package',
+ assets: assetOnManifest,
+ );
+
+ writeAssets('p/p/', assetsOnDisk);
+ const String expectedAssetManifest =
+ '{"packages/test_package/a/foo":["packages/test_package/a/foo","packages/test_package/a/2x/foo"]}';
+
+ await buildAndVerifyAssets(
+ assetsOnDisk,
+ <String>['test_package'],
+ expectedAssetManifest,
+ );
+ }, overrides: <Type, Generator>{
+ FileSystem: () => testFileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
+ });
+
+ testUsingContext('No asset is bundled with variant, no assets or directories are listed', () async {
+ writePubspecFile('pubspec.yaml', 'test');
+ writePackagesFile('test_package:p/p/lib/');
+
+ final List<String> assetsOnDisk = <String>['a/foo', 'a/2x/foo'];
+ final List<String> assetOnManifest = <String>[];
+
+ writePubspecFile(
+ 'p/p/pubspec.yaml',
+ 'test_package',
+ assets: assetOnManifest,
+ );
+
+ writeAssets('p/p/', assetsOnDisk);
+ const String expectedAssetManifest = '{}';
+
+ await buildAndVerifyAssets(
+ assetOnManifest,
+ <String>['test_package'],
+ expectedAssetManifest,
+ );
+ }, overrides: <Type, Generator>{
+ FileSystem: () => testFileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
+ });
+
+ testUsingContext('Expect error generating manifest, wrong non-existing directory is listed', () async {
+ writePubspecFile('pubspec.yaml', 'test');
+ writePackagesFile('test_package:p/p/lib/');
+
+ final List<String> assetOnManifest = <String>['c/'];
+
+ writePubspecFile(
+ 'p/p/pubspec.yaml',
+ 'test_package',
+ assets: assetOnManifest,
+ );
+
+ await buildAndVerifyAssets(
+ assetOnManifest,
+ <String>['test_package'],
+ null,
+ expectExists: false,
+ );
+ }, overrides: <Type, Generator>{
+ FileSystem: () => testFileSystem,
+ ProcessManager: () => FakeProcessManager.any(),
});
});
}
diff --git a/packages/flutter_tools/test/general.shard/asset_bundle_test.dart b/packages/flutter_tools/test/general.shard/asset_bundle_test.dart
index 47f47b7..bd272f8 100644
--- a/packages/flutter_tools/test/general.shard/asset_bundle_test.dart
+++ b/packages/flutter_tools/test/general.shard/asset_bundle_test.dart
@@ -3,7 +3,6 @@
// found in the LICENSE file.
import 'dart:convert';
-import 'dart:typed_data';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/artifacts.dart';
@@ -14,7 +13,6 @@
import 'package:flutter_tools/src/bundle_builder.dart';
import 'package:flutter_tools/src/devfs.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
-import 'package:standard_message_codec/standard_message_codec.dart';
import '../src/common.dart';
import '../src/context.dart';
@@ -50,16 +48,11 @@
final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
await bundle.build(packagesPath: '.packages');
- expect(bundle.entries.keys, unorderedEquals(<String>['AssetManifest.json', 'AssetManifest.bin']));
- const String expectedJsonAssetManifest = '{}';
+ expect(bundle.entries.length, 1);
+ const String expectedAssetManifest = '{}';
expect(
utf8.decode(await bundle.entries['AssetManifest.json']!.contentsAsBytes()),
- expectedJsonAssetManifest,
- );
- const String expectedBinAssetManifest = '{}';
- expect(
- await _extractBinAssetManifestFromBundleAsJson(bundle),
- expectedBinAssetManifest
+ expectedAssetManifest,
);
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
@@ -79,8 +72,12 @@
''');
final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
await bundle.build(packagesPath: '.packages');
- expect(bundle.entries.keys, unorderedEquals(<String>['AssetManifest.json',
- 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z', 'assets/foo/bar.txt']));
+ // Expected assets:
+ // - asset manifest
+ // - font manifest
+ // - license file
+ // - assets/foo/bar.txt
+ expect(bundle.entries.length, 4);
expect(bundle.needsBuild(), false);
// Simulate modifying the files by updating the filestat time manually.
@@ -90,9 +87,13 @@
expect(bundle.needsBuild(), true);
await bundle.build(packagesPath: '.packages');
- expect(bundle.entries.keys, unorderedEquals(<String>['AssetManifest.json',
- 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z', 'assets/foo/bar.txt',
- 'assets/foo/fizz.txt']));
+ // Expected assets:
+ // - asset manifest
+ // - font manifest
+ // - license file
+ // - assets/foo/bar.txt
+ // - assets/foo/fizz.txt
+ expect(bundle.entries.length, 5);
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
@@ -111,8 +112,12 @@
globals.fs.file('.packages').createSync();
final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
await bundle.build(packagesPath: '.packages');
- expect(bundle.entries.keys, unorderedEquals(<String>['AssetManifest.json',
- 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z', 'assets/foo/bar.txt']));
+ // Expected assets:
+ // - asset manifest
+ // - font manifest
+ // - license file
+ // - assets/foo/bar.txt
+ expect(bundle.entries.length, 4);
expect(bundle.needsBuild(), false);
// Delete the wildcard directory and update pubspec file.
@@ -133,8 +138,12 @@
// supporting file deletion.
expect(bundle.needsBuild(), true);
await bundle.build(packagesPath: '.packages');
- expect(bundle.entries.keys, unorderedEquals(<String>['AssetManifest.json',
- 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z', 'assets/foo/bar.txt']));
+ // Expected assets:
+ // - asset manifest
+ // - font manifest
+ // - license file
+ // - assets/foo/bar.txt
+ expect(bundle.entries.length, 4);
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
@@ -157,8 +166,12 @@
globals.fs.file('.packages').createSync();
final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
await bundle.build(packagesPath: '.packages');
- expect(bundle.entries.keys, unorderedEquals(<String>['AssetManifest.json',
- 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z', 'assets/foo/bar.txt']));
+ // Expected assets:
+ // - asset manifest
+ // - font manifest
+ // - license file
+ // - assets/foo/bar.txt
+ expect(bundle.entries.length, 4);
expect(bundle.needsBuild(), false);
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
@@ -190,8 +203,12 @@
splitDeferredAssets: true,
).createBundle();
await bundle.build(packagesPath: '.packages', deferredComponentsEnabled: true);
- expect(bundle.entries.keys, unorderedEquals(<String>['AssetManifest.json',
- 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z', 'assets/foo/bar.txt']));
+ // Expected assets:
+ // - asset manifest
+ // - font manifest
+ // - license file
+ // - assets/foo/bar.txt
+ expect(bundle.entries.length, 4);
expect(bundle.deferredComponentsEntries.length, 1);
expect(bundle.deferredComponentsEntries['component1']!.length, 2);
expect(bundle.needsBuild(), false);
@@ -220,9 +237,12 @@
''');
final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
await bundle.build(packagesPath: '.packages');
- expect(bundle.entries.keys, unorderedEquals(<String>['assets/foo/bar.txt',
- 'assets/bar/barbie.txt', 'assets/wild/dash.txt', 'AssetManifest.json',
- 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z']));
+ // Expected assets:
+ // - asset manifest
+ // - font manifest
+ // - license file
+ // - assets/foo/bar.txt
+ expect(bundle.entries.length, 6);
expect(bundle.deferredComponentsEntries.isEmpty, true);
expect(bundle.needsBuild(), false);
}, overrides: <Type, Generator>{
@@ -255,11 +275,14 @@
splitDeferredAssets: true,
).createBundle();
await bundle.build(packagesPath: '.packages', deferredComponentsEnabled: true);
- expect(bundle.entries.keys, unorderedEquals(<String>['assets/foo/bar.txt',
- 'AssetManifest.json', 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z']));
- expect(bundle.deferredComponentsEntries.keys, unorderedEquals(<String>['component1']));
- expect(bundle.deferredComponentsEntries['component1']!.keys,
- unorderedEquals(<String>['assets/bar/barbie.txt', 'assets/wild/dash.txt']));
+ // Expected assets:
+ // - asset manifest
+ // - font manifest
+ // - license file
+ // - assets/foo/bar.txt
+ expect(bundle.entries.length, 4);
+ expect(bundle.deferredComponentsEntries.length, 1);
+ expect(bundle.deferredComponentsEntries['component1']!.length, 2);
expect(bundle.needsBuild(), false);
// Simulate modifying the files by updating the filestat time manually.
@@ -270,13 +293,9 @@
expect(bundle.needsBuild(), true);
await bundle.build(packagesPath: '.packages', deferredComponentsEnabled: true);
- expect(bundle.entries.keys, unorderedEquals(<String>['assets/foo/bar.txt',
- 'AssetManifest.json', 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z']));
+ expect(bundle.entries.length, 4);
expect(bundle.deferredComponentsEntries.length, 1);
- expect(bundle.deferredComponentsEntries['component1']!.keys,
- unorderedEquals(<String>['assets/bar/barbie.txt', 'assets/wild/dash.txt',
- 'assets/wild/fizz.txt']));
-
+ expect(bundle.deferredComponentsEntries['component1']!.length, 3);
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
@@ -316,8 +335,7 @@
final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
await bundle.build(packagesPath: '.packages');
- final DevFSContent? assetManifestBin = bundle.entries['AssetManifest.bin'];
- final DevFSStringContent? assetManifestJson = bundle.entries['AssetManifest.json']
+ final DevFSStringContent? assetManifest = bundle.entries['AssetManifest.json']
as DevFSStringContent?;
final DevFSStringContent? fontManifest = bundle.entries['FontManifest.json']
as DevFSStringContent?;
@@ -326,8 +344,7 @@
await bundle.build(packagesPath: '.packages');
- expect(assetManifestBin, bundle.entries['AssetManifest.bin']);
- expect(assetManifestJson, bundle.entries['AssetManifest.json']);
+ expect(assetManifest, bundle.entries['AssetManifest.json']);
expect(fontManifest, bundle.entries['FontManifest.json']);
expect(license, bundle.entries['NOTICES']);
}, overrides: <Type, Generator>{
@@ -622,8 +639,7 @@
await bundle.build(packagesPath: '.packages');
- expect(bundle.entries.keys, unorderedEquals(<String>['packages/foo/bar/fizz.txt',
- 'AssetManifest.json', 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z']));
+ expect(bundle.entries, hasLength(4));
expect(bundle.needsBuild(), false);
// Does not track dependency's wildcard directories.
@@ -723,7 +739,6 @@
expect(await bundle.build(packagesPath: '.packages'), 0);
expect((bundle.entries['FontManifest.json']! as DevFSStringContent).string, '[]');
expect((bundle.entries['AssetManifest.json']! as DevFSStringContent).string, '{}');
- expect(await _extractBinAssetManifestFromBundleAsJson(bundle), '{}');
expect(testLogger.errorText, contains(
'package:foo has `uses-material-design: true` set'
));
@@ -759,8 +774,7 @@
final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
expect(await bundle.build(packagesPath: '.packages'), 0);
- expect(bundle.entries.keys, unorderedEquals(<String>['assets/foo.txt',
- 'AssetManifest.json', 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z']));
+ expect(bundle.entries.length, 4);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
@@ -798,17 +812,9 @@
// The assets from deferred components and regular assets
// are both included in alphabetical order
expect((bundle.entries['AssetManifest.json']! as DevFSStringContent).string, '{"assets/apple.jpg":["assets/apple.jpg"],"assets/bar.jpg":["assets/bar.jpg"],"assets/foo.jpg":["assets/foo.jpg"],"assets/zebra.jpg":["assets/zebra.jpg"]}');
- expect(await _extractBinAssetManifestFromBundleAsJson(bundle), '{"assets/apple.jpg":[],"assets/bar.jpg":[],"assets/foo.jpg":[],"assets/zebra.jpg":[]}');
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
Platform: () => FakePlatform(),
});
}
-
-Future<String> _extractBinAssetManifestFromBundleAsJson(AssetBundle bundle) async {
- final List<int> manifestBytes = await bundle.entries['AssetManifest.bin']!.contentsAsBytes();
- return json.encode(const StandardMessageCodec().decodeMessage(
- ByteData.sublistView(Uint8List.fromList(manifestBytes))
- ));
-}
diff --git a/packages/flutter_tools/test/general.shard/asset_bundle_variant_test.dart b/packages/flutter_tools/test/general.shard/asset_bundle_variant_test.dart
index 9b48bc2..9bec3b7 100644
--- a/packages/flutter_tools/test/general.shard/asset_bundle_variant_test.dart
+++ b/packages/flutter_tools/test/general.shard/asset_bundle_variant_test.dart
@@ -3,7 +3,6 @@
// found in the LICENSE file.
import 'dart:convert';
-import 'dart:typed_data';
import 'package:file/file.dart';
import 'package:file/memory.dart';
@@ -16,418 +15,209 @@
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/project.dart';
-import 'package:standard_message_codec/standard_message_codec.dart';
import '../src/common.dart';
void main() {
- // TODO(andrewkolos): Delete this group once we stop producing AssetManifest.json
- // as part of build.
- group('Legacy asset manifest (AssetManifest.json)', () {
- Future<Map<String, List<String>>> extractAssetManifestFromBundle(ManifestAssetBundle bundle) async {
- final String manifestJson = utf8.decode(await bundle.entries['AssetManifest.json']!.contentsAsBytes());
- final Map<String, dynamic> parsedJson = json.decode(manifestJson) as Map<String, dynamic>;
- final Iterable<String> keys = parsedJson.keys;
- final Map<String, List<String>> parsedManifest = <String, List<String>> {
- for (final String key in keys) key: List<String>.from(parsedJson[key] as List<dynamic>),
- };
- return parsedManifest;
- }
+ Future<Map<String, List<String>>> extractAssetManifestFromBundle(ManifestAssetBundle bundle) async {
+ final String manifestJson = utf8.decode(await bundle.entries['AssetManifest.json']!.contentsAsBytes());
+ final Map<String, dynamic> parsedJson = json.decode(manifestJson) as Map<String, dynamic>;
+ final Iterable<String> keys = parsedJson.keys;
+ final Map<String, List<String>> parsedManifest = <String, List<String>> {
+ for (final String key in keys) key: List<String>.from(parsedJson[key] as List<dynamic>),
+ };
+ return parsedManifest;
+ }
- group('AssetBundle asset variants (with Unix-style paths)', () {
- late Platform platform;
- late FileSystem fs;
+ group('AssetBundle asset variants (with Unix-style paths)', () {
+ late Platform platform;
+ late FileSystem fs;
- setUp(() {
- platform = FakePlatform();
- fs = MemoryFileSystem.test();
- Cache.flutterRoot = Cache.defaultFlutterRoot(
- platform: platform,
- fileSystem: fs,
- userMessages: UserMessages()
- );
+ setUp(() {
+ platform = FakePlatform();
+ fs = MemoryFileSystem.test();
+ Cache.flutterRoot = Cache.defaultFlutterRoot(
+ platform: platform,
+ fileSystem: fs,
+ userMessages: UserMessages()
+ );
- fs.file('.packages').createSync();
+ fs.file('.packages').createSync();
- fs.file('pubspec.yaml').writeAsStringSync(
- '''
- name: test
- dependencies:
- flutter:
- sdk: flutter
+ fs.file('pubspec.yaml').writeAsStringSync(
+'''
+name: test
+dependencies:
flutter:
- assets:
- - assets/
- '''
- );
- });
-
- testWithoutContext('Only images in folders named with device pixel ratios (e.g. 2x, 3.0x) should be considered as variants of other images', () async {
- const String image = 'assets/image.jpg';
- const String image2xVariant = 'assets/2x/image.jpg';
- const String imageNonVariant = 'assets/notAVariant/image.jpg';
-
- final List<String> assets = <String>[
- image,
- image2xVariant,
- imageNonVariant
- ];
-
- for (final String asset in assets) {
- final File assetFile = fs.file(asset);
- assetFile.createSync(recursive: true);
- assetFile.writeAsStringSync(asset);
- }
-
- final ManifestAssetBundle bundle = ManifestAssetBundle(
- logger: BufferLogger.test(),
- fileSystem: fs,
- platform: platform,
- );
-
- await bundle.build(
- packagesPath: '.packages',
- flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
- );
-
- final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle);
-
- expect(manifest, hasLength(2));
- expect(manifest[image], equals(<String>[image, image2xVariant]));
- expect(manifest[imageNonVariant], equals(<String>[imageNonVariant]));
- });
-
- testWithoutContext('Asset directories are recursively searched for assets', () async {
- const String topLevelImage = 'assets/image.jpg';
- const String secondLevelImage = 'assets/folder/secondLevel.jpg';
- const String secondLevel2xVariant = 'assets/folder/2x/secondLevel.jpg';
-
- final List<String> assets = <String>[
- topLevelImage,
- secondLevelImage,
- secondLevel2xVariant
- ];
-
- for (final String asset in assets) {
- final File assetFile = fs.file(asset);
- assetFile.createSync(recursive: true);
- assetFile.writeAsStringSync(asset);
- }
-
- final ManifestAssetBundle bundle = ManifestAssetBundle(
- logger: BufferLogger.test(),
- fileSystem: fs,
- platform: platform,
- );
-
- await bundle.build(
- packagesPath: '.packages',
- flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
- );
-
- final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle);
- expect(manifest, hasLength(2));
- expect(manifest[topLevelImage], equals(<String>[topLevelImage]));
- expect(manifest[secondLevelImage], equals(<String>[secondLevelImage, secondLevel2xVariant]));
- });
-
- testWithoutContext('Asset paths should never be URI-encoded', () async {
- const String image = 'assets/normalFolder/i have URI-reserved_characters.jpg';
- const String imageVariant = 'assets/normalFolder/3x/i have URI-reserved_characters.jpg';
-
- final List<String> assets = <String>[
- image,
- imageVariant
- ];
-
- for (final String asset in assets) {
- final File assetFile = fs.file(asset);
- assetFile.createSync(recursive: true);
- assetFile.writeAsStringSync(asset);
- }
-
- final ManifestAssetBundle bundle = ManifestAssetBundle(
- logger: BufferLogger.test(),
- fileSystem: fs,
- platform: platform,
- );
-
- await bundle.build(
- packagesPath: '.packages',
- flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
- );
-
- final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle);
- expect(manifest, hasLength(1));
- expect(manifest[image], equals(<String>[image, imageVariant]));
- });
+ sdk: flutter
+flutter:
+ assets:
+ - assets/
+'''
+ );
});
+ testWithoutContext('Only images in folders named with device pixel ratios (e.g. 2x, 3.0x) should be considered as variants of other images', () async {
+ const String image = 'assets/image.jpg';
+ const String image2xVariant = 'assets/2x/image.jpg';
+ const String imageNonVariant = 'assets/notAVariant/image.jpg';
- group('AssetBundle asset variants (with Windows-style filepaths)', () {
- late final Platform platform;
- late final FileSystem fs;
+ final List<String> assets = <String>[
+ image,
+ image2xVariant,
+ imageNonVariant
+ ];
- setUp(() {
- platform = FakePlatform(operatingSystem: 'windows');
- fs = MemoryFileSystem.test(style: FileSystemStyle.windows);
- Cache.flutterRoot = Cache.defaultFlutterRoot(
- platform: platform,
- fileSystem: fs,
- userMessages: UserMessages()
- );
+ for (final String asset in assets) {
+ final File assetFile = fs.file(asset);
+ assetFile.createSync(recursive: true);
+ assetFile.writeAsStringSync(asset);
+ }
- fs.file('.packages').createSync();
+ final ManifestAssetBundle bundle = ManifestAssetBundle(
+ logger: BufferLogger.test(),
+ fileSystem: fs,
+ platform: platform,
+ );
- fs.file('pubspec.yaml').writeAsStringSync(
- '''
- name: test
- dependencies:
- flutter:
- sdk: flutter
- flutter:
- assets:
- - assets/
- '''
- );
- });
+ await bundle.build(
+ packagesPath: '.packages',
+ flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
+ );
- testWithoutContext('Variant detection works with windows-style filepaths', () async {
- const List<String> assets = <String>[
- r'assets\foo.jpg',
- r'assets\2x\foo.jpg',
- r'assets\somewhereElse\bar.jpg',
- r'assets\somewhereElse\2x\bar.jpg',
- ];
+ final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle);
- for (final String asset in assets) {
- final File assetFile = fs.file(asset);
- assetFile.createSync(recursive: true);
- assetFile.writeAsStringSync(asset);
- }
+ expect(manifest, hasLength(2));
+ expect(manifest[image], equals(<String>[image, image2xVariant]));
+ expect(manifest[imageNonVariant], equals(<String>[imageNonVariant]));
+ });
- final ManifestAssetBundle bundle = ManifestAssetBundle(
- logger: BufferLogger.test(),
- fileSystem: fs,
- platform: platform,
- );
+ testWithoutContext('Asset directories are recursively searched for assets', () async {
+ const String topLevelImage = 'assets/image.jpg';
+ const String secondLevelImage = 'assets/folder/secondLevel.jpg';
+ const String secondLevel2xVariant = 'assets/folder/2x/secondLevel.jpg';
- await bundle.build(
- packagesPath: '.packages',
- flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
- );
+ final List<String> assets = <String>[
+ topLevelImage,
+ secondLevelImage,
+ secondLevel2xVariant
+ ];
- final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle);
+ for (final String asset in assets) {
+ final File assetFile = fs.file(asset);
+ assetFile.createSync(recursive: true);
+ assetFile.writeAsStringSync(asset);
+ }
- expect(manifest, hasLength(2));
- expect(manifest['assets/foo.jpg'], equals(<String>['assets/foo.jpg', 'assets/2x/foo.jpg']));
- expect(manifest['assets/somewhereElse/bar.jpg'], equals(<String>['assets/somewhereElse/bar.jpg', 'assets/somewhereElse/2x/bar.jpg']));
- });
+ final ManifestAssetBundle bundle = ManifestAssetBundle(
+ logger: BufferLogger.test(),
+ fileSystem: fs,
+ platform: platform,
+ );
+
+ await bundle.build(
+ packagesPath: '.packages',
+ flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
+ );
+
+ final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle);
+ expect(manifest, hasLength(2));
+ expect(manifest[topLevelImage], equals(<String>[topLevelImage]));
+ expect(manifest[secondLevelImage], equals(<String>[secondLevelImage, secondLevel2xVariant]));
+ });
+
+ testWithoutContext('Asset paths should never be URI-encoded', () async {
+ const String image = 'assets/normalFolder/i have URI-reserved_characters.jpg';
+ const String imageVariant = 'assets/normalFolder/3x/i have URI-reserved_characters.jpg';
+
+ final List<String> assets = <String>[
+ image,
+ imageVariant
+ ];
+
+ for (final String asset in assets) {
+ final File assetFile = fs.file(asset);
+ assetFile.createSync(recursive: true);
+ assetFile.writeAsStringSync(asset);
+ }
+
+ final ManifestAssetBundle bundle = ManifestAssetBundle(
+ logger: BufferLogger.test(),
+ fileSystem: fs,
+ platform: platform,
+ );
+
+ await bundle.build(
+ packagesPath: '.packages',
+ flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
+ );
+
+ final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle);
+ expect(manifest, hasLength(1));
+ expect(manifest[image], equals(<String>[image, imageVariant]));
});
});
- group('Current asset manifest (AssetManifest.bin)', () {
- Future<String> extractAssetManifestFromBundleAsJson(ManifestAssetBundle bundle) async {
- final List<int> manifestBytes = await bundle.entries['AssetManifest.bin']!.contentsAsBytes();
- return json.encode(const StandardMessageCodec().decodeMessage(
- ByteData.sublistView(Uint8List.fromList(manifestBytes))
- ));
- }
- group('AssetBundle asset variants (with Unix-style paths)', () {
- late Platform platform;
- late FileSystem fs;
+ group('AssetBundle asset variants (with Windows-style filepaths)', () {
+ late final Platform platform;
+ late final FileSystem fs;
- setUp(() {
- platform = FakePlatform();
- fs = MemoryFileSystem.test();
- Cache.flutterRoot = Cache.defaultFlutterRoot(
- platform: platform,
- fileSystem: fs,
- userMessages: UserMessages()
- );
+ setUp(() {
+ platform = FakePlatform(operatingSystem: 'windows');
+ fs = MemoryFileSystem.test(style: FileSystemStyle.windows);
+ Cache.flutterRoot = Cache.defaultFlutterRoot(
+ platform: platform,
+ fileSystem: fs,
+ userMessages: UserMessages()
+ );
- fs.file('.packages').createSync();
+ fs.file('.packages').createSync();
- fs.file('pubspec.yaml').writeAsStringSync(
- '''
- name: test
- dependencies:
- flutter:
- sdk: flutter
+ fs.file('pubspec.yaml').writeAsStringSync(
+'''
+name: test
+dependencies:
flutter:
- assets:
- - assets/
- '''
- );
- });
-
- testWithoutContext('Only images in folders named with device pixel ratios (e.g. 2x, 3.0x) should be considered as variants of other images', () async {
- const String image = 'assets/image.jpg';
- const String image2xVariant = 'assets/2x/image.jpg';
- const String imageNonVariant = 'assets/notAVariant/image.jpg';
-
- final List<String> assets = <String>[
- image,
- image2xVariant,
- imageNonVariant
- ];
-
- for (final String asset in assets) {
- final File assetFile = fs.file(asset);
- assetFile.createSync(recursive: true);
- assetFile.writeAsStringSync(asset);
- }
-
- final ManifestAssetBundle bundle = ManifestAssetBundle(
- logger: BufferLogger.test(),
- fileSystem: fs,
- platform: platform,
- );
-
- await bundle.build(
- packagesPath: '.packages',
- flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
- );
-
- const String expectedManifest = '{"$image":[{"asset":"$image2xVariant","dpr":2.0}],'
- '"$imageNonVariant":[]}';
-
- final String manifest = await extractAssetManifestFromBundleAsJson(bundle);
- expect(manifest, equals(expectedManifest));
- });
-
- testWithoutContext('Asset directories are recursively searched for assets', () async {
- const String topLevelImage = 'assets/image.jpg';
- const String secondLevelImage = 'assets/folder/secondLevel.jpg';
- const String secondLevel2xVariant = 'assets/folder/2x/secondLevel.jpg';
-
- final List<String> assets = <String>[
- topLevelImage,
- secondLevelImage,
- secondLevel2xVariant
- ];
-
- for (final String asset in assets) {
- final File assetFile = fs.file(asset);
- assetFile.createSync(recursive: true);
- assetFile.writeAsStringSync(asset);
- }
-
- final ManifestAssetBundle bundle = ManifestAssetBundle(
- logger: BufferLogger.test(),
- fileSystem: fs,
- platform: platform,
- );
-
- await bundle.build(
- packagesPath: '.packages',
- flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
- );
-
- const String expectedManifest = '{'
- '"$secondLevelImage":[{"asset":"$secondLevel2xVariant","dpr":2.0}],'
- '"$topLevelImage":[]'
- '}';
-
- final String manifest = await extractAssetManifestFromBundleAsJson(bundle);
- expect(manifest, equals(expectedManifest));
- });
-
- testWithoutContext('Asset paths should never be URI-encoded', () async {
- const String image = 'assets/normalFolder/i have URI-reserved_characters.jpg';
- const String imageVariant = 'assets/normalFolder/3x/i have URI-reserved_characters.jpg';
-
- final List<String> assets = <String>[
- image,
- imageVariant
- ];
-
- for (final String asset in assets) {
- final File assetFile = fs.file(asset);
- assetFile.createSync(recursive: true);
- assetFile.writeAsStringSync(asset);
- }
-
- final ManifestAssetBundle bundle = ManifestAssetBundle(
- logger: BufferLogger.test(),
- fileSystem: fs,
- platform: platform,
- );
-
- await bundle.build(
- packagesPath: '.packages',
- flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
- );
-
- const String expectedManifest = '{"$image":[{"asset":"$imageVariant","dpr":3.0}]}';
-
- final String manifest = await extractAssetManifestFromBundleAsJson(bundle);
- expect(manifest, equals(expectedManifest));
- });
+ sdk: flutter
+flutter:
+ assets:
+ - assets/
+'''
+ );
});
+ testWithoutContext('Variant detection works with windows-style filepaths', () async {
+ const List<String> assets = <String>[
+ r'assets\foo.jpg',
+ r'assets\2x\foo.jpg',
+ r'assets\somewhereElse\bar.jpg',
+ r'assets\somewhereElse\2x\bar.jpg',
+ ];
- group('AssetBundle asset variants (with Windows-style filepaths)', () {
- late final Platform platform;
- late final FileSystem fs;
+ for (final String asset in assets) {
+ final File assetFile = fs.file(asset);
+ assetFile.createSync(recursive: true);
+ assetFile.writeAsStringSync(asset);
+ }
- setUp(() {
- platform = FakePlatform(operatingSystem: 'windows');
- fs = MemoryFileSystem.test(style: FileSystemStyle.windows);
- Cache.flutterRoot = Cache.defaultFlutterRoot(
- platform: platform,
- fileSystem: fs,
- userMessages: UserMessages()
- );
+ final ManifestAssetBundle bundle = ManifestAssetBundle(
+ logger: BufferLogger.test(),
+ fileSystem: fs,
+ platform: platform,
+ );
- fs.file('.packages').createSync();
+ await bundle.build(
+ packagesPath: '.packages',
+ flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
+ );
- fs.file('pubspec.yaml').writeAsStringSync(
- '''
- name: test
- dependencies:
- flutter:
- sdk: flutter
- flutter:
- assets:
- - assets/
- '''
- );
- });
+ final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle);
- testWithoutContext('Variant detection works with windows-style filepaths', () async {
- const List<String> assets = <String>[
- r'assets\foo.jpg',
- r'assets\2x\foo.jpg',
- r'assets\somewhereElse\bar.jpg',
- r'assets\somewhereElse\2x\bar.jpg',
- ];
-
- for (final String asset in assets) {
- final File assetFile = fs.file(asset);
- assetFile.createSync(recursive: true);
- assetFile.writeAsStringSync(asset);
- }
-
- final ManifestAssetBundle bundle = ManifestAssetBundle(
- logger: BufferLogger.test(),
- fileSystem: fs,
- platform: platform,
- );
-
- await bundle.build(
- packagesPath: '.packages',
- flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
- );
-
- const String expectedManifest = '{"assets/foo.jpg":[{"asset":"assets/2x/foo.jpg","dpr":2.0}],'
- '"assets/somewhereElse/bar.jpg":[{"asset":"assets/somewhereElse/2x/bar.jpg","dpr":2.0}]}';
-
- final String manifest = await extractAssetManifestFromBundleAsJson(bundle);
- expect(manifest, equals(expectedManifest));
- });
+ expect(manifest, hasLength(2));
+ expect(manifest['assets/foo.jpg'], equals(<String>['assets/foo.jpg', 'assets/2x/foo.jpg']));
+ expect(manifest['assets/somewhereElse/bar.jpg'], equals(<String>['assets/somewhereElse/bar.jpg', 'assets/somewhereElse/2x/bar.jpg']));
});
});
}