Tighten asset variant detection criteria to only include device-pixel-ratio variants (#110721)
diff --git a/dev/integration_tests/flutter_gallery/pubspec.yaml b/dev/integration_tests/flutter_gallery/pubspec.yaml
index bd4a6d6..445505a 100644
--- a/dev/integration_tests/flutter_gallery/pubspec.yaml
+++ b/dev/integration_tests/flutter_gallery/pubspec.yaml
@@ -169,7 +169,6 @@
- packages/flutter_gallery_assets/products/table.png
- packages/flutter_gallery_assets/products/teaset.png
- packages/flutter_gallery_assets/products/top.png
- - packages/flutter_gallery_assets/people/ali.png
- packages/flutter_gallery_assets/people/square/ali.png
- packages/flutter_gallery_assets/people/square/peter.png
- packages/flutter_gallery_assets/people/square/sandra.png
diff --git a/packages/flutter_tools/lib/src/asset.dart b/packages/flutter_tools/lib/src/asset.dart
index c7e438c..c45d611 100644
--- a/packages/flutter_tools/lib/src/asset.dart
+++ b/packages/flutter_tools/lib/src/asset.dart
@@ -23,6 +23,9 @@
const String kFontManifestJson = 'FontManifest.json';
+// Should match '2x', '/1x', '1.5x', etc.
+final RegExp _assetVariantDirectoryRegExp = RegExp(r'/?(\d+(\.\d*)?)x$');
+
/// The effect of adding `uses-material-design: true` to the pubspec is to insert
/// the following snippet into the asset manifest:
///
@@ -92,7 +95,6 @@
/// Returns 0 for success; non-zero for failure.
Future<int> build({
String manifestPath = defaultManifestPath,
- String? assetDirPath,
required String packagesPath,
bool deferredComponentsEnabled = false,
TargetPlatform? targetPlatform,
@@ -205,23 +207,22 @@
@override
Future<int> build({
String manifestPath = defaultManifestPath,
- String? assetDirPath,
+ FlutterProject? flutterProject,
required String packagesPath,
bool deferredComponentsEnabled = false,
TargetPlatform? targetPlatform,
}) async {
- assetDirPath ??= getAssetBuildDirectory();
- FlutterProject flutterProject;
- try {
- flutterProject = FlutterProject.fromDirectory(_fileSystem.file(manifestPath).parent);
- } on Exception catch (e) {
- _logger.printStatus('Error detected in pubspec.yaml:', emphasis: true);
- _logger.printError('$e');
- return 1;
- }
+
if (flutterProject == null) {
- return 1;
+ try {
+ flutterProject = FlutterProject.fromDirectory(_fileSystem.file(manifestPath).parent);
+ } on Exception catch (e) {
+ _logger.printStatus('Error detected in pubspec.yaml:', emphasis: true);
+ _logger.printError('$e');
+ return 1;
+ }
}
+
final FlutterManifest flutterManifest = flutterProject.manifest;
// If the last build time isn't set before this early return, empty pubspecs will
// hang on hot reload, as the incremental dill files will never be copied to the
@@ -243,27 +244,14 @@
final List<Uri> wildcardDirectories = <Uri>[];
// The _assetVariants map contains an entry for each asset listed
- // in the pubspec.yaml file's assets and font and sections. The
+ // in the pubspec.yaml file's assets and font sections. The
// value of each image asset is a list of resolution-specific "variants",
// see _AssetDirectoryCache.
- final List<String> excludeDirs = <String>[
- assetDirPath,
- getBuildDirectory(),
- if (flutterProject.ios.existsSync())
- flutterProject.ios.hostAppRoot.path,
- if (flutterProject.macos.existsSync())
- flutterProject.macos.managedDirectory.path,
- if (flutterProject.windows.existsSync())
- flutterProject.windows.managedDirectory.path,
- if (flutterProject.linux.existsSync())
- flutterProject.linux.managedDirectory.path,
- ];
final Map<_Asset, List<_Asset>>? assetVariants = _parseAssets(
packageConfig,
flutterManifest,
wildcardDirectories,
assetBasePath,
- excludeDirs: excludeDirs,
);
if (assetVariants == null) {
@@ -277,7 +265,6 @@
assetBasePath,
wildcardDirectories,
flutterProject.directory,
- excludeDirs: excludeDirs,
);
if (!_splitDeferredAssets || !deferredComponentsEnabled) {
// Include the assets in the regular set of assets if not using deferred
@@ -373,8 +360,7 @@
// variant files exist. An image's main entry is treated the same as a
// "1x" resolution variant and if both exist then the explicit 1x
// variant is preferred.
- if (assetFile.existsSync()) {
- assert(!variants.contains(asset));
+ if (assetFile.existsSync() && !variants.contains(asset)) {
variants.insert(0, asset);
}
for (final _Asset variant in variants) {
@@ -407,8 +393,7 @@
// variant files exist. An image's main entry is treated the same as a
// "1x" resolution variant and if both exist then the explicit 1x
// variant is preferred.
- if (assetFile.existsSync()) {
- assert(!assetsMap[asset]!.contains(asset));
+ if (assetFile.existsSync() && !assetsMap[asset]!.contains(asset)) {
assetsMap[asset]!.insert(0, asset);
}
for (final _Asset variant in assetsMap[asset]!) {
@@ -606,7 +591,7 @@
}
for (final DeferredComponent component in components) {
deferredComponentsAssetVariants[component.name] = <_Asset, List<_Asset>>{};
- final _AssetDirectoryCache cache = _AssetDirectoryCache(<String>[], _fileSystem);
+ final _AssetDirectoryCache cache = _AssetDirectoryCache(_fileSystem);
for (final Uri assetUri in component.assets) {
if (assetUri.path.endsWith('/')) {
wildcardDirectories.add(assetUri);
@@ -617,7 +602,6 @@
cache,
deferredComponentsAssetVariants[component.name]!,
assetUri,
- excludeDirs: excludeDirs,
);
} else {
_parseAssetFromFile(
@@ -728,13 +712,12 @@
FlutterManifest flutterManifest,
List<Uri> wildcardDirectories,
String assetBase, {
- List<String> excludeDirs = const <String>[],
String? packageName,
Package? attributedPackage,
}) {
final Map<_Asset, List<_Asset>> result = <_Asset, List<_Asset>>{};
- final _AssetDirectoryCache cache = _AssetDirectoryCache(excludeDirs, _fileSystem);
+ final _AssetDirectoryCache cache = _AssetDirectoryCache(_fileSystem);
for (final Uri assetUri in flutterManifest.assets) {
if (assetUri.path.endsWith('/')) {
wildcardDirectories.add(assetUri);
@@ -745,7 +728,6 @@
cache,
result,
assetUri,
- excludeDirs: excludeDirs,
packageName: packageName,
attributedPackage: attributedPackage,
);
@@ -757,7 +739,6 @@
cache,
result,
assetUri,
- excludeDirs: excludeDirs,
packageName: packageName,
attributedPackage: attributedPackage,
);
@@ -772,7 +753,6 @@
cache,
result,
shaderUri,
- excludeDirs: excludeDirs,
packageName: packageName,
attributedPackage: attributedPackage,
assetKind: AssetKind.shader,
@@ -808,7 +788,6 @@
_AssetDirectoryCache cache,
Map<_Asset, List<_Asset>> result,
Uri assetUri, {
- List<String> excludeDirs = const <String>[],
String? packageName,
Package? attributedPackage,
}) {
@@ -820,10 +799,9 @@
return;
}
- final Iterable<File> files = _fileSystem
- .directory(directoryPath)
- .listSync()
- .whereType<File>();
+ final Iterable<FileSystemEntity> entities = _fileSystem.directory(directoryPath).listSync();
+
+ final Iterable<File> files = entities.whereType<File>();
for (final File file in files) {
final String relativePath = _fileSystem.path.relative(file.path, from: assetBase);
final Uri uri = Uri.file(relativePath, windows: _platform.isWindows);
@@ -839,6 +817,22 @@
attributedPackage: attributedPackage,
);
}
+
+ final Iterable<Directory> nonVariantSubDirectories = entities
+ .whereType<Directory>()
+ .where((Directory directory) => !_assetVariantDirectoryRegExp.hasMatch(directory.basename));
+ for (final Directory dir in nonVariantSubDirectories) {
+ final String relativePath = _fileSystem.path.relative(dir.path, from: assetBase);
+ final Uri relativePathsUri = Uri.directory(relativePath, windows: _platform.isWindows);
+
+ _parseAssetsFromFolder(packageConfig,
+ flutterManifest,
+ assetBase,
+ cache,
+ result,
+ relativePathsUri
+ );
+ }
}
void _parseAssetFromFile(
@@ -1011,54 +1005,48 @@
// Given an assets directory like this:
//
-// assets/foo
-// assets/var1/foo
-// assets/var2/foo
-// assets/bar
+// assets/foo.png
+// assets/2x/foo.png
+// assets/3.0x/foo.png
+// assets/bar/foo.png
+// assets/bar.png
//
-// variantsFor('assets/foo') => ['/assets/var1/foo', '/assets/var2/foo']
-// variantsFor('assets/bar') => []
+// variantsFor('assets/foo.png') => ['/assets/foo.png', '/assets/2x/foo.png', 'assets/3.0x/foo.png']
+// variantsFor('assets/bar.png') => ['/assets/bar.png']
+// variantsFor('assets/bar/foo.png') => ['/assets/bar/foo.png']
class _AssetDirectoryCache {
- _AssetDirectoryCache(Iterable<String> excluded, this._fileSystem)
- : _excluded = excluded
- .map<String>(_fileSystem.path.absolute)
- .toList();
+ _AssetDirectoryCache(this._fileSystem);
final FileSystem _fileSystem;
- final List<String> _excluded;
- final Map<String, Map<String, List<String>>> _cache = <String, Map<String, List<String>>>{};
+ final Map<String, List<String>> _cache = <String, List<String>>{};
List<String> variantsFor(String assetPath) {
- final String assetName = _fileSystem.path.basename(assetPath);
final String directory = _fileSystem.path.dirname(assetPath);
if (!_fileSystem.directory(directory).existsSync()) {
return const <String>[];
}
- if (_cache[directory] == null) {
- final List<String> paths = <String>[];
- for (final FileSystemEntity entity in _fileSystem.directory(directory).listSync(recursive: true)) {
- final String path = entity.path;
- if (_fileSystem.isFileSync(path)
- && assetPath != path
- && !_excluded.any((String exclude) => _fileSystem.path.isWithin(exclude, path))) {
- paths.add(path);
- }
- }
-
- final Map<String, List<String>> variants = <String, List<String>>{};
- for (final String path in paths) {
- final String variantName = _fileSystem.path.basename(path);
- if (directory == _fileSystem.path.dirname(path)) {
- continue;
- }
- variants[variantName] ??= <String>[];
- variants[variantName]!.add(path);
- }
- _cache[directory] = variants;
+ if (_cache.containsKey(assetPath)) {
+ return _cache[assetPath]!;
}
- return _cache[directory]![assetName] ?? const <String>[];
+ final List<FileSystemEntity> entitiesInDirectory = _fileSystem.directory(directory).listSync();
+
+ final List<String> pathsOfVariants = <String>[
+ // It's possible that the user specifies only explicit variants (e.g. .../1x/asset.png),
+ // so there does not necessarily need to be a file at the given path.
+ if (_fileSystem.file(assetPath).existsSync())
+ assetPath,
+ ...entitiesInDirectory
+ .whereType<Directory>()
+ .where((Directory dir) => _assetVariantDirectoryRegExp.hasMatch(dir.basename))
+ .expand((Directory dir) => dir.listSync())
+ .whereType<File>()
+ .map((File file) => file.path),
+ ];
+
+ _cache[assetPath] = pathsOfVariants;
+ return pathsOfVariants;
}
}
diff --git a/packages/flutter_tools/lib/src/bundle_builder.dart b/packages/flutter_tools/lib/src/bundle_builder.dart
index f41c682..50480ba 100644
--- a/packages/flutter_tools/lib/src/bundle_builder.dart
+++ b/packages/flutter_tools/lib/src/bundle_builder.dart
@@ -121,7 +121,6 @@
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
final int result = await assetBundle.build(
manifestPath: manifestPath,
- assetDirPath: assetDirPath,
packagesPath: packagesPath,
targetPlatform: targetPlatform,
);
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 dc4d868..1b651b3 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
@@ -222,11 +222,11 @@
assets: <String>['a/foo'],
);
- final List<String> assets = <String>['a/foo', 'a/v/foo'];
+ final List<String> assets = <String>['a/foo', 'a/2x/foo'];
writeAssets('p/p/', assets);
const String expectedManifest = '{"packages/test_package/a/foo":'
- '["packages/test_package/a/foo","packages/test_package/a/v/foo"]}';
+ '["packages/test_package/a/foo","packages/test_package/a/2x/foo"]}';
await buildAndVerifyAssets(
assets,
@@ -251,11 +251,11 @@
'test_package',
);
- final List<String> assets = <String>['a/foo', 'a/v/foo'];
+ 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/v/foo"]}';
+ '["packages/test_package/a/foo","packages/test_package/a/2x/foo"]}';
await buildAndVerifyAssets(
assets,
@@ -344,15 +344,15 @@
assets: <String>['a/foo'],
);
- final List<String> assets = <String>['a/foo', 'a/v/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/v/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/v/foo"]}';
+ '["packages/test_package2/a/foo","packages/test_package2/a/2x/foo"]}';
await buildAndVerifyAssets(
assets,
@@ -384,15 +384,15 @@
'test_package2',
);
- final List<String> assets = <String>['a/foo', 'a/v/foo'];
+ 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/v/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/v/foo"]}';
+ '["packages/test_package2/a/foo","packages/test_package2/a/2x/foo"]}';
await buildAndVerifyAssets(
assets,
@@ -421,12 +421,12 @@
'test_package2',
);
- final List<String> assets = <String>['a/foo', 'a/v/foo'];
+ 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/v/foo"]}';
+ '["packages/test_package2/a/foo","packages/test_package2/a/2x/foo"]}';
await buildAndVerifyAssets(
assets,
@@ -553,7 +553,7 @@
writePubspecFile('pubspec.yaml', 'test');
writePackagesFile('test_package:p/p/lib/');
- final List<String> assetsOnDisk = <String>['a/foo','a/b/foo'];
+ final List<String> assetsOnDisk = <String>['a/foo','a/2x/foo'];
final List<String> assetOnManifest = <String>['a/',];
writePubspecFile(
@@ -564,7 +564,7 @@
writeAssets('p/p/', assetsOnDisk);
const String expectedAssetManifest =
- '{"packages/test_package/a/foo":["packages/test_package/a/foo","packages/test_package/a/b/foo"]}';
+ '{"packages/test_package/a/foo":["packages/test_package/a/foo","packages/test_package/a/2x/foo"]}';
await buildAndVerifyAssets(
assetsOnDisk,
@@ -580,7 +580,7 @@
writePubspecFile('pubspec.yaml', 'test');
writePackagesFile('test_package:p/p/lib/');
- final List<String> assetsOnDisk = <String>['a/foo', 'a/b/foo'];
+ final List<String> assetsOnDisk = <String>['a/foo', 'a/2x/foo'];
final List<String> assetOnManifest = <String>[];
writePubspecFile(
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 079f3f4..e2cd837 100644
--- a/packages/flutter_tools/test/general.shard/asset_bundle_test.dart
+++ b/packages/flutter_tools/test/general.shard/asset_bundle_test.dart
@@ -643,7 +643,7 @@
flutter:
assets:
- - foo.txt
+ - assets/foo.txt
''');
globals.fs.file('assets/foo.txt').createSync(recursive: true);
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 5b2546c..c635a5f 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
@@ -9,37 +9,43 @@
import 'package:flutter_tools/src/asset.dart';
import 'package:flutter_tools/src/base/file_system.dart';
+import 'package:flutter_tools/src/base/logger.dart';
+import 'package:flutter_tools/src/base/platform.dart';
+import 'package:flutter_tools/src/base/user_messages.dart';
+import 'package:flutter_tools/src/cache.dart';
-import 'package:flutter_tools/src/globals.dart' as globals;
+import 'package:flutter_tools/src/project.dart';
import '../src/common.dart';
-import '../src/context.dart';
void main() {
- String fixPath(String path) {
- // The in-memory file system is strict about slashes on Windows being the
- // correct way so until https://github.com/google/file.dart/issues/112 is
- // fixed we fix them here.
- // TODO(dantup): Remove this function once the above issue is fixed and
- // rolls into Flutter.
- return path.replaceAll('/', globals.fs.path.separator);
+
+ 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', () {
- late FileSystem testFileSystem;
- setUp(() async {
- testFileSystem = MemoryFileSystem(
- style: globals.platform.isWindows
- ? FileSystemStyle.windows
- : FileSystemStyle.posix,
- );
- testFileSystem.currentDirectory = testFileSystem.systemTempDirectory.createTempSync('flutter_asset_bundle_variant_test.');
- });
+ group('AssetBundle asset variants (with POSIX-style paths)', () {
+ late final Platform platform;
+ late final FileSystem fs;
- testUsingContext('main asset and variants', () async {
- globals.fs.file('pubspec.yaml')
- ..createSync()
- ..writeAsStringSync(
+ setUpAll(() {
+ platform = FakePlatform();
+ fs = MemoryFileSystem.test();
+ Cache.flutterRoot = Cache.defaultFlutterRoot(
+ platform: platform,
+ fileSystem: fs,
+ userMessages: UserMessages()
+ );
+
+ fs.file('.packages').createSync();
+
+ fs.file('pubspec.yaml').writeAsStringSync(
'''
name: test
dependencies:
@@ -47,46 +53,187 @@
sdk: flutter
flutter:
assets:
- - a/b/c/foo
+ - assets/
'''
);
- globals.fs.file('.packages').createSync();
+ });
+
+ 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>[
- 'a/b/c/foo',
- 'a/b/c/var1/foo',
- 'a/b/c/var2/foo',
- 'a/b/c/var3/foo',
+ image,
+ image2xVariant,
+ imageNonVariant
];
+
for (final String asset in assets) {
- globals.fs.file(fixPath(asset))
- ..createSync(recursive: true)
- ..writeAsStringSync(asset);
+ final File assetFile = fs.file(asset);
+ assetFile.createSync(recursive: true);
+ assetFile.writeAsStringSync(asset);
}
- AssetBundle bundle = AssetBundleFactory.instance.createBundle();
- await bundle.build(packagesPath: '.packages');
+ final ManifestAssetBundle bundle = ManifestAssetBundle(
+ logger: BufferLogger.test(),
+ fileSystem: fs,
+ platform: platform,
+ );
- // The main asset file, /a/b/c/foo, and its variants exist.
+ await bundle.build(
+ packagesPath: '.packages',
+ flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
+ );
+
+ final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle);
+ final List<String> variantsForImage = manifest[image]!;
+
+ expect(variantsForImage, contains(image2xVariant));
+ expect(variantsForImage, isNot(contains(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) {
- expect(bundle.entries.containsKey(asset), true);
- expect(utf8.decode(await bundle.entries[asset]!.contentsAsBytes()), asset);
+ final File assetFile = fs.file(asset);
+ assetFile.createSync(recursive: true);
+ assetFile.writeAsStringSync(asset);
}
- globals.fs.file(fixPath('a/b/c/foo')).deleteSync();
- bundle = AssetBundleFactory.instance.createBundle();
- await bundle.build(packagesPath: '.packages');
+ final ManifestAssetBundle bundle = ManifestAssetBundle(
+ logger: BufferLogger.test(),
+ fileSystem: fs,
+ platform: platform,
+ );
- // Now the main asset file, /a/b/c/foo, does not exist. This is OK because
- // the /a/b/c/*/foo variants do exist.
- expect(bundle.entries.containsKey('a/b/c/foo'), false);
- for (final String asset in assets.skip(1)) {
- expect(bundle.entries.containsKey(asset), true);
- expect(utf8.decode(await bundle.entries[asset]!.contentsAsBytes()), asset);
+ await bundle.build(
+ packagesPath: '.packages',
+ flutterProject: FlutterProject.fromDirectoryTest(fs.currentDirectory),
+ );
+
+ final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle);
+ expect(manifest, contains(secondLevelImage));
+ expect(manifest, contains(topLevelImage));
+ expect(manifest[secondLevelImage], hasLength(2));
+ expect(manifest[secondLevelImage], contains(secondLevelImage));
+ expect(manifest[secondLevelImage], contains(secondLevel2xVariant));
+ });
+ });
+
+
+ group('AssetBundle asset variants (with Windows-style filepaths)', () {
+ late final Platform platform;
+ late final FileSystem fs;
+
+ String correctPathSeparators(String path) {
+ // The in-memory file system is strict about slashes on Windows being the
+ // correct way. See https://github.com/google/file.dart/issues/112.
+ return path.replaceAll('/', fs.path.separator);
+ }
+
+ setUpAll(() {
+ 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('pubspec.yaml').writeAsStringSync(
+'''
+name: test
+dependencies:
+ flutter:
+ 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';
+
+ final List<String> assets = <String>[
+ image,
+ image2xVariant,
+ imageNonVariant
+ ];
+
+ for (final String asset in assets) {
+ final File assetFile = fs.file(correctPathSeparators(asset));
+ assetFile.createSync(recursive: true);
+ assetFile.writeAsStringSync(asset);
}
- }, overrides: <Type, Generator>{
- FileSystem: () => testFileSystem,
- ProcessManager: () => FakeProcessManager.any(),
+
+ 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);
+ final List<String> variantsForImage = manifest[image]!;
+
+ expect(variantsForImage, contains(image2xVariant));
+ expect(variantsForImage, isNot(contains(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(correctPathSeparators(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, contains(secondLevelImage));
+ expect(manifest, contains(topLevelImage));
+ expect(manifest[secondLevelImage], hasLength(2));
+ expect(manifest[secondLevelImage], contains(secondLevelImage));
+ expect(manifest[secondLevelImage], contains(secondLevel2xVariant));
});
});
}