diff --git a/dev/benchmarks/multiple_flutters/module/pubspec.yaml b/dev/benchmarks/multiple_flutters/module/pubspec.yaml index a719903916..560d6e94bd 100644 --- a/dev/benchmarks/multiple_flutters/module/pubspec.yaml +++ b/dev/benchmarks/multiple_flutters/module/pubspec.yaml @@ -41,7 +41,7 @@ dependencies: typed_data: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" win32: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - xdg_directories: 0.2.0+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + xdg_directories: 0.2.0+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" flutter: uses-material-design: true @@ -51,4 +51,4 @@ flutter: androidPackage: com.example.multiple_flutters_module iosBundleIdentifier: com.example.multipleFluttersModule -# PUBSPEC CHECKSUM: e9fc +# PUBSPEC CHECKSUM: eafd diff --git a/dev/integration_tests/android_views/pubspec.yaml b/dev/integration_tests/android_views/pubspec.yaml index 2c4b7b949d..b79785edd7 100644 --- a/dev/integration_tests/android_views/pubspec.yaml +++ b/dev/integration_tests/android_views/pubspec.yaml @@ -47,7 +47,7 @@ dependencies: vm_service: 9.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" win32: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - xdg_directories: 0.2.0+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + xdg_directories: 0.2.0+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" dev_dependencies: flutter_test: @@ -94,4 +94,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 11b0 +# PUBSPEC CHECKSUM: 28b1 diff --git a/dev/integration_tests/hybrid_android_views/pubspec.yaml b/dev/integration_tests/hybrid_android_views/pubspec.yaml index 8be5780ee2..7da82861d7 100644 --- a/dev/integration_tests/hybrid_android_views/pubspec.yaml +++ b/dev/integration_tests/hybrid_android_views/pubspec.yaml @@ -45,7 +45,7 @@ dependencies: vm_service: 9.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" win32: 3.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - xdg_directories: 0.2.0+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + xdg_directories: 0.2.0+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" dev_dependencies: flutter_test: @@ -92,4 +92,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 11b0 +# PUBSPEC CHECKSUM: 28b1 diff --git a/packages/flutter_tools/lib/src/asset.dart b/packages/flutter_tools/lib/src/asset.dart index 81dd56d75e..59baa4af03 100644 --- a/packages/flutter_tools/lib/src/asset.dart +++ b/packages/flutter_tools/lib/src/asset.dart @@ -2,8 +2,11 @@ // 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'; @@ -162,7 +165,11 @@ class ManifestAssetBundle implements AssetBundle { DateTime? _lastBuildTimestamp; - static const String _kAssetManifestJson = 'AssetManifest.json'; + // We assume the main asset is designed for a device pixel ratio of 1.0. + static const double _defaultResolution = 1.0; + static const String _kAssetManifestJsonFilename = 'AssetManifest.json'; + static const String _kAssetManifestBinFilename = 'AssetManifest.bin'; + 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, @@ -230,8 +237,15 @@ class ManifestAssetBundle implements AssetBundle { // device. _lastBuildTimestamp = DateTime.now(); if (flutterManifest.isEmpty) { - entries[_kAssetManifestJson] = DevFSStringContent('{}'); - entryKinds[_kAssetManifestJson] = AssetKind.regular; + entries[_kAssetManifestJsonFilename] = DevFSStringContent('{}'); + entryKinds[_kAssetManifestJsonFilename] = AssetKind.regular; + entries[_kAssetManifestJsonFilename] = DevFSStringContent('{}'); + entryKinds[_kAssetManifestJsonFilename] = AssetKind.regular; + final ByteData emptyAssetManifest = + const StandardMessageCodec().encodeMessage({})!; + entries[_kAssetManifestBinFilename] = + DevFSByteContent(emptyAssetManifest.buffer.asUint8List(0, emptyAssetManifest.lengthInBytes)); + entryKinds[_kAssetManifestBinFilename] = AssetKind.regular; return 0; } @@ -428,7 +442,10 @@ class ManifestAssetBundle implements AssetBundle { _wildcardDirectories[uri] ??= _fileSystem.directory(uri); } - final DevFSStringContent assetManifest = _createAssetManifest(assetVariants, deferredComponentsAssetVariants); + final Map> assetManifest = + _createAssetManifest(assetVariants, deferredComponentsAssetVariants); + final DevFSStringContent assetManifestJson = DevFSStringContent(json.encode(assetManifest)); + final DevFSByteContent assetManifestBinary = _createAssetManifestBinary(assetManifest); final DevFSStringContent fontManifest = DevFSStringContent(json.encode(fonts)); final LicenseResult licenseResult = _licenseCollector.obtainLicenses(packageConfig, additionalLicenseFiles); if (licenseResult.errorMessages.isNotEmpty) { @@ -452,7 +469,8 @@ class ManifestAssetBundle implements AssetBundle { _fileSystem.file('DOES_NOT_EXIST_RERUN_FOR_WILDCARD$suffix').absolute); } - _setIfChanged(_kAssetManifestJson, assetManifest, AssetKind.regular); + _setIfChanged(_kAssetManifestJsonFilename, assetManifestJson, AssetKind.regular); + _setIfChanged(_kAssetManifestBinFilename, assetManifestBinary, AssetKind.regular); _setIfChanged(kFontManifestJson, fontManifest, AssetKind.regular); _setLicenseIfChanged(licenseResult.combinedLicenses, targetPlatform); return 0; @@ -460,18 +478,31 @@ class ManifestAssetBundle implements AssetBundle { @override List additionalDependencies = []; - - void _setIfChanged(String key, DevFSStringContent content, AssetKind assetKind) { - if (!entries.containsKey(key)) { - entries[key] = content; - entryKinds[key] = assetKind; + void _setIfChanged(String key, DevFSContent content, AssetKind assetKind) { + 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 && + _compareIntLists(oldContent.bytes, content.bytes)) { return; } - final DevFSStringContent? oldContent = entries[key] as DevFSStringContent?; - if (oldContent?.string != content.string) { - entries[key] = content; - entryKinds[key] = assetKind; + + entries[key] = content; + entryKinds[key] = assetKind; + } + + static bool _compareIntLists(List o1, List 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; } void _setLicenseIfChanged( @@ -623,14 +654,14 @@ class ManifestAssetBundle implements AssetBundle { return deferredComponentsAssetVariants; } - DevFSStringContent _createAssetManifest( + Map> _createAssetManifest( Map<_Asset, List<_Asset>> assetVariants, Map>> deferredComponentsAssetVariants ) { - final Map> jsonObject = >{}; - final Map<_Asset, List> jsonEntries = <_Asset, List>{}; + final Map> manifest = >{}; + final Map<_Asset, List> entries = <_Asset, List>{}; assetVariants.forEach((_Asset main, List<_Asset> variants) { - jsonEntries[main] = [ + entries[main] = [ for (final _Asset variant in variants) variant.entryUri.path, ]; @@ -638,24 +669,69 @@ class ManifestAssetBundle implements AssetBundle { if (deferredComponentsAssetVariants != null) { for (final Map<_Asset, List<_Asset>> componentAssets in deferredComponentsAssetVariants.values) { componentAssets.forEach((_Asset main, List<_Asset> variants) { - jsonEntries[main] = [ + entries[main] = [ for (final _Asset variant in variants) variant.entryUri.path, ]; }); } } - final List<_Asset> sortedKeys = jsonEntries.keys.toList() + final List<_Asset> sortedKeys = entries.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 rawEntryVariantsPaths = jsonEntries[main]!; + final List rawEntryVariantsPaths = entries[main]!; final List decodedEntryVariantPaths = rawEntryVariantsPaths .map((String value) => Uri.decodeFull(value)) .toList(); - jsonObject[decodedEntryPath] = decodedEntryVariantPaths; + manifest[decodedEntryPath] = decodedEntryVariantPaths; } - return DevFSStringContent(json.encode(jsonObject)); + return manifest; + } + + // Matches path-like strings ending in a number followed by an 'x'. + // Example matches include "assets/animals/2.0x", "plants/3x", and "2.7x". + static final RegExp _extractPixelRatioFromKeyRegExp = RegExp(r'/?(\d+(\.\d*)?)x$'); + + DevFSByteContent _createAssetManifestBinary( + Map> 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 = _extractPixelRatioFromKeyRegExp.firstMatch(directoryPath); + if (match != null && match.groupCount > 0) { + return double.parse(match.group(1)!); + } + return _defaultResolution; + } + + final Map result = {}; + + for (final MapEntry manifestEntry in assetManifest.entries) { + final List resultVariants = []; + final List entries = (manifestEntry.value as List).cast(); + 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 resultVariant = {}; + 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)); } /// Prefixes family names and asset paths of fonts included from packages with diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 724902683c..576579e400 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -57,6 +57,8 @@ dependencies: vm_service: 9.4.0 + standard_message_codec: 0.0.1+3 + _fe_analyzer_shared: 52.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" analyzer: 5.4.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" @@ -102,4 +104,4 @@ dartdoc: # Exclude this package from the hosted API docs. nodoc: true -# PUBSPEC CHECKSUM: 02f9 +# PUBSPEC CHECKSUM: 899b 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 f44cd20a9a..febaa31742 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,8 +111,8 @@ $fontsSection final AssetBundle bundle = AssetBundleFactory.instance.createBundle(); await bundle.build(packagesPath: '.packages'); - expect(bundle.entries.length, 3); // LICENSE, AssetManifest, FontManifest - expect(bundle.entries.containsKey('FontManifest.json'), isTrue); + expect(bundle.entries.keys, unorderedEquals(['AssetManifest.bin', + 'AssetManifest.json', 'FontManifest.json', 'NOTICES.Z'])); }, overrides: { 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 5c2ef1d3bf..54c8092277 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,6 +3,7 @@ // found in the LICENSE file. import 'dart:convert'; +import 'dart:typed_data'; import 'package:file/file.dart'; import 'package:file/memory.dart'; @@ -11,6 +12,7 @@ import 'package:flutter_tools/src/asset.dart'; 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'; @@ -63,9 +65,17 @@ $assetsSection Future buildAndVerifyAssets( List assets, List packages, - String? expectedAssetManifest, { + String? expectedJsonAssetManifest, + String? expectedBinAssetManifestAsJson, { bool expectExists = true, }) async { + Future extractAssetManifestBinFromBundleAsJson(AssetBundle bundle) async { + final List manifestBytes = await bundle.entries['AssetManifest.bin']!.contentsAsBytes(); + return json.encode(const StandardMessageCodec().decodeMessage( + ByteData.sublistView(Uint8List.fromList(manifestBytes)) + )); + } + final AssetBundle bundle = AssetBundleFactory.instance.createBundle(); await bundle.build(packagesPath: '.packages'); @@ -86,7 +96,11 @@ $assetsSection if (expectExists) { expect( utf8.decode(await bundle.entries['AssetManifest.json']!.contentsAsBytes()), - expectedAssetManifest, + expectedJsonAssetManifest, + ); + expect( + await extractAssetManifestBinFromBundleAsJson(bundle), + expectedBinAssetManifestAsJson ); } } @@ -120,7 +134,8 @@ $assetsSection final AssetBundle bundle = AssetBundleFactory.instance.createBundle(); await bundle.build(packagesPath: '.packages'); - expect(bundle.entries.length, 3); // LICENSE, AssetManifest, FontManifest + expect(bundle.entries.keys, unorderedEquals( + ['NOTICES.Z', 'AssetManifest.json', 'AssetManifest.bin', 'FontManifest.json'])); const String expectedAssetManifest = '{}'; expect( utf8.decode(await bundle.entries['AssetManifest.json']!.contentsAsBytes()), @@ -145,7 +160,8 @@ $assetsSection final AssetBundle bundle = AssetBundleFactory.instance.createBundle(); await bundle.build(packagesPath: '.packages'); - expect(bundle.entries.length, 3); // LICENSE, AssetManifest, FontManifest + expect(bundle.entries.keys, unorderedEquals( + ['NOTICES.Z', 'AssetManifest.json', 'AssetManifest.bin', 'FontManifest.json'])); const String expectedAssetManifest = '{}'; expect( utf8.decode(await bundle.entries['AssetManifest.json']!.contentsAsBytes()), @@ -174,12 +190,14 @@ $assetsSection writeAssets('p/p/', assets); - const String expectedAssetManifest = '{"packages/test_package/a/foo":' + const String expectedJsonAssetManifest = '{"packages/test_package/a/foo":' '["packages/test_package/a/foo"]}'; + const String expectedBinAssetManifest = '{"packages/test_package/a/foo":[]}'; await buildAndVerifyAssets( assets, ['test_package'], - expectedAssetManifest, + expectedJsonAssetManifest, + expectedBinAssetManifest ); }, overrides: { FileSystem: () => testFileSystem, @@ -202,10 +220,12 @@ $assetsSection const String expectedAssetManifest = '{"packages/test_package/a/foo":' '["packages/test_package/a/foo"]}'; + const String expectedBinAssetManifest = '{"packages/test_package/a/foo":[]}'; await buildAndVerifyAssets( assets, ['test_package'], expectedAssetManifest, + expectedBinAssetManifest ); }, overrides: { FileSystem: () => testFileSystem, @@ -232,10 +252,18 @@ $assetsSection '["packages/test_package/a/foo","packages/test_package/a/2x/foo"]' '}'; + const String expectedBinManifest = '{' + '"packages/test_package/a/bar":[],' + '"packages/test_package/a/foo":' + '[{"asset":"packages/test_package/a/2x/foo","dpr":2.0}]' + '}'; + + await buildAndVerifyAssets( assets, ['test_package'], expectedManifest, + expectedBinManifest ); }, overrides: { FileSystem: () => testFileSystem, @@ -259,12 +287,16 @@ $assetsSection writeAssets('p/p/lib/', assets); const String expectedManifest = '{"packages/test_package/a/foo":' - '["packages/test_package/a/foo","packages/test_package/a/2x/foo"]}'; + '["packages/test_package/a/foo","packages/test_package/a/2x/foo"]}'; + + const String expectedBinManifest = '{"packages/test_package/a/foo":' + '[{"asset":"packages/test_package/a/2x/foo","dpr":2.0}]}'; await buildAndVerifyAssets( assets, ['test_package'], expectedManifest, + expectedBinManifest ); }, overrides: { FileSystem: () => testFileSystem, @@ -287,11 +319,16 @@ $assetsSection const String expectedAssetManifest = '{"packages/test_package/a/bar":["packages/test_package/a/bar"],' '"packages/test_package/a/foo":["packages/test_package/a/foo"]}'; + const String expectedBinAssetManifest = + '{"packages/test_package/a/bar":[],' + '"packages/test_package/a/foo":[]}'; + await buildAndVerifyAssets( assets, ['test_package'], expectedAssetManifest, + expectedBinAssetManifest ); }, overrides: { FileSystem: () => testFileSystem, @@ -320,11 +357,15 @@ $assetsSection const String expectedAssetManifest = '{"packages/test_package/a/bar":["packages/test_package/a/bar"],' '"packages/test_package/a/foo":["packages/test_package/a/foo"]}'; + const String expectedBinAssetManifest = + '{"packages/test_package/a/bar":[],' + '"packages/test_package/a/foo":[]}'; await buildAndVerifyAssets( assets, ['test_package'], expectedAssetManifest, + expectedBinAssetManifest ); }, overrides: { FileSystem: () => testFileSystem, @@ -357,11 +398,17 @@ $assetsSection '["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"]}'; + const String expectedBinAssetManifest = + '{"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, ['test_package', 'test_package2'], expectedAssetManifest, + expectedBinAssetManifest ); }, overrides: { FileSystem: () => testFileSystem, @@ -398,10 +445,17 @@ $assetsSection '"packages/test_package2/a/foo":' '["packages/test_package2/a/foo","packages/test_package2/a/2x/foo"]}'; + const String expectedBinAssetManifest = + '{"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, ['test_package', 'test_package2'], expectedAssetManifest, + expectedBinAssetManifest ); }, overrides: { FileSystem: () => testFileSystem, @@ -431,11 +485,15 @@ $assetsSection const String expectedAssetManifest = '{"packages/test_package2/a/foo":' '["packages/test_package2/a/foo","packages/test_package2/a/2x/foo"]}'; + const String expectedBinAssetManifest = + '{"packages/test_package2/a/foo":' + '[{"asset":"packages/test_package2/a/2x/foo","dpr":2.0}]}'; await buildAndVerifyAssets( assets, ['test_package2'], expectedAssetManifest, + expectedBinAssetManifest ); }, overrides: { FileSystem: () => testFileSystem, @@ -458,11 +516,15 @@ $assetsSection 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 expectedBinAssetManifest = + '{"packages/test_package/a/foo":[],' + '"packages/test_package/a/foo [x]":[]}'; await buildAndVerifyAssets( assets, ['test_package'], expectedAssetManifest, + expectedBinAssetManifest ); }, overrides: { FileSystem: () => testFileSystem, @@ -487,11 +549,15 @@ $assetsSection const String expectedAssetManifest = '{"packages/test_package/a/bar":["packages/test_package/a/bar"],' '"packages/test_package/a/foo":["packages/test_package/a/foo"]}'; + const String expectedBinAssetManifest = + '{"packages/test_package/a/bar":[],' + '"packages/test_package/a/foo":[]}'; await buildAndVerifyAssets( assetsOnDisk, ['test_package'], expectedAssetManifest, + expectedBinAssetManifest ); }, overrides: { FileSystem: () => testFileSystem, @@ -515,11 +581,15 @@ $assetsSection const String expectedAssetManifest = '{"packages/test_package/a/foo":["packages/test_package/a/foo"],' '"packages/test_package/abc/bar":["packages/test_package/abc/bar"]}'; + const String expectedBinAssetManifest = + '{"packages/test_package/a/foo":[],' + '"packages/test_package/abc/bar":[]}'; await buildAndVerifyAssets( assetsOnDisk, ['test_package'], expectedAssetManifest, + expectedBinAssetManifest ); }, overrides: { FileSystem: () => testFileSystem, @@ -569,11 +639,14 @@ $assetsSection writeAssets('p/p/', assetsOnDisk); const String expectedAssetManifest = '{"packages/test_package/a/foo":["packages/test_package/a/foo","packages/test_package/a/2x/foo"]}'; + const String expectedBinAssetManifest = + '{"packages/test_package/a/foo":[{"asset":"packages/test_package/a/2x/foo","dpr":2.0}]}'; await buildAndVerifyAssets( assetsOnDisk, ['test_package'], expectedAssetManifest, + expectedBinAssetManifest ); }, overrides: { FileSystem: () => testFileSystem, @@ -595,11 +668,14 @@ $assetsSection writeAssets('p/p/', assetsOnDisk); const String expectedAssetManifest = '{}'; + const String expectedBinAssetManifest = '{}'; + await buildAndVerifyAssets( assetOnManifest, ['test_package'], expectedAssetManifest, + expectedBinAssetManifest ); }, overrides: { FileSystem: () => testFileSystem, @@ -622,6 +698,7 @@ $assetsSection assetOnManifest, ['test_package'], null, + null, expectExists: false, ); }, overrides: { 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 2b34bafed9..7e2a24b317 100644 --- a/packages/flutter_tools/test/general.shard/asset_bundle_test.dart +++ b/packages/flutter_tools/test/general.shard/asset_bundle_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:convert'; +import 'dart:typed_data'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/artifacts.dart'; @@ -13,6 +14,7 @@ import 'package:flutter_tools/src/build_info.dart'; 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'; @@ -48,12 +50,20 @@ void main() { final AssetBundle bundle = AssetBundleFactory.instance.createBundle(); await bundle.build(packagesPath: '.packages'); - expect(bundle.entries.length, 1); - const String expectedAssetManifest = '{}'; + expect(bundle.entries.keys, + unorderedEquals(['AssetManifest.json', 'AssetManifest.bin']) + ); + const String expectedJsonAssetManifest = '{}'; + const Map expectedBinAssetManifest = {}; expect( utf8.decode(await bundle.entries['AssetManifest.json']!.contentsAsBytes()), - expectedAssetManifest, + expectedJsonAssetManifest, ); + expect( + const StandardMessageCodec().decodeMessage(ByteData.sublistView(Uint8List.fromList(await bundle.entries['AssetManifest.bin']!.contentsAsBytes()))), + expectedBinAssetManifest + ); + }, overrides: { FileSystem: () => testFileSystem, ProcessManager: () => FakeProcessManager.any(), @@ -72,14 +82,8 @@ flutter: '''); final AssetBundle bundle = AssetBundleFactory.instance.createBundle(); await bundle.build(packagesPath: '.packages'); - // Expected assets: - // - asset manifest - // - font manifest - // - license file - // - assets/foo/bar.txt - expect(bundle.entries.length, 4); - expect(bundle.needsBuild(), false); - + expect(bundle.entries.keys, unorderedEquals(['AssetManifest.json', + 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z', 'assets/foo/bar.txt'])); // Simulate modifying the files by updating the filestat time manually. globals.fs.file(globals.fs.path.join('assets', 'foo', 'fizz.txt')) ..createSync(recursive: true) @@ -87,13 +91,9 @@ flutter: expect(bundle.needsBuild(), true); await bundle.build(packagesPath: '.packages'); - // Expected assets: - // - asset manifest - // - font manifest - // - license file - // - assets/foo/bar.txt - // - assets/foo/fizz.txt - expect(bundle.entries.length, 5); + expect(bundle.entries.keys, unorderedEquals(['AssetManifest.json', + 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z', 'assets/foo/bar.txt', + 'assets/foo/fizz.txt'])); }, overrides: { FileSystem: () => testFileSystem, ProcessManager: () => FakeProcessManager.any(), @@ -112,12 +112,8 @@ flutter: globals.fs.file('.packages').createSync(); final AssetBundle bundle = AssetBundleFactory.instance.createBundle(); await bundle.build(packagesPath: '.packages'); - // Expected assets: - // - asset manifest - // - font manifest - // - license file - // - assets/foo/bar.txt - expect(bundle.entries.length, 4); + expect(bundle.entries.keys, unorderedEquals(['AssetManifest.json', + 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z', 'assets/foo/bar.txt'])); expect(bundle.needsBuild(), false); // Delete the wildcard directory and update pubspec file. @@ -138,12 +134,8 @@ name: example''') // supporting file deletion. expect(bundle.needsBuild(), true); await bundle.build(packagesPath: '.packages'); - // Expected assets: - // - asset manifest - // - font manifest - // - license file - // - assets/foo/bar.txt - expect(bundle.entries.length, 4); + expect(bundle.entries.keys, unorderedEquals(['AssetManifest.json', + 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z', 'assets/foo/bar.txt'])); }, overrides: { FileSystem: () => testFileSystem, ProcessManager: () => FakeProcessManager.any(), @@ -166,12 +158,8 @@ flutter: globals.fs.file('.packages').createSync(); final AssetBundle bundle = AssetBundleFactory.instance.createBundle(); await bundle.build(packagesPath: '.packages'); - // Expected assets: - // - asset manifest - // - font manifest - // - license file - // - assets/foo/bar.txt - expect(bundle.entries.length, 4); + expect(bundle.entries.keys, unorderedEquals(['AssetManifest.json', + 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z', 'assets/foo/bar.txt'])); expect(bundle.needsBuild(), false); }, overrides: { FileSystem: () => testFileSystem, @@ -203,12 +191,8 @@ flutter: splitDeferredAssets: true, ).createBundle(); await bundle.build(packagesPath: '.packages', deferredComponentsEnabled: true); - // Expected assets: - // - asset manifest - // - font manifest - // - license file - // - assets/foo/bar.txt - expect(bundle.entries.length, 4); + expect(bundle.entries.keys, unorderedEquals(['AssetManifest.json', + 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z', 'assets/foo/bar.txt'])); expect(bundle.deferredComponentsEntries.length, 1); expect(bundle.deferredComponentsEntries['component1']!.length, 2); expect(bundle.needsBuild(), false); @@ -237,12 +221,9 @@ flutter: '''); final AssetBundle bundle = AssetBundleFactory.instance.createBundle(); await bundle.build(packagesPath: '.packages'); - // Expected assets: - // - asset manifest - // - font manifest - // - license file - // - assets/foo/bar.txt - expect(bundle.entries.length, 6); + expect(bundle.entries.keys, unorderedEquals(['assets/foo/bar.txt', + 'assets/bar/barbie.txt', 'assets/wild/dash.txt', 'AssetManifest.json', + 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z'])); expect(bundle.deferredComponentsEntries.isEmpty, true); expect(bundle.needsBuild(), false); }, overrides: { @@ -275,12 +256,8 @@ flutter: splitDeferredAssets: true, ).createBundle(); await bundle.build(packagesPath: '.packages', deferredComponentsEnabled: true); - // Expected assets: - // - asset manifest - // - font manifest - // - license file - // - assets/foo/bar.txt - expect(bundle.entries.length, 4); + expect(bundle.entries.keys, unorderedEquals(['assets/foo/bar.txt', + 'AssetManifest.json', 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z'])); expect(bundle.deferredComponentsEntries.length, 1); expect(bundle.deferredComponentsEntries['component1']!.length, 2); expect(bundle.needsBuild(), false); @@ -293,7 +270,8 @@ flutter: expect(bundle.needsBuild(), true); await bundle.build(packagesPath: '.packages', deferredComponentsEnabled: true); - expect(bundle.entries.length, 4); + expect(bundle.entries.keys, unorderedEquals(['assets/foo/bar.txt', + 'AssetManifest.json', 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z'])); expect(bundle.deferredComponentsEntries.length, 1); expect(bundle.deferredComponentsEntries['component1']!.length, 3); }, overrides: { @@ -641,7 +619,8 @@ flutter: await bundle.build(packagesPath: '.packages'); - expect(bundle.entries, hasLength(4)); + expect(bundle.entries.keys, unorderedEquals(['packages/foo/bar/fizz.txt', + 'AssetManifest.json', 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z'])); expect(bundle.needsBuild(), false); // Does not track dependency's wildcard directories. @@ -776,7 +755,8 @@ flutter: final AssetBundle bundle = AssetBundleFactory.instance.createBundle(); expect(await bundle.build(packagesPath: '.packages'), 0); - expect(bundle.entries.length, 4); + expect(bundle.entries.keys, unorderedEquals(['assets/foo.txt', + 'AssetManifest.json', 'AssetManifest.bin', 'FontManifest.json', 'NOTICES.Z'])); }, overrides: { FileSystem: () => MemoryFileSystem.test(), ProcessManager: () => FakeProcessManager.any(),