diff --git a/packages/flutter/test/painting/image_resolution_test.dart b/packages/flutter/test/painting/image_resolution_test.dart index 6813668de7..8e04f2aada 100644 --- a/packages/flutter/test/painting/image_resolution_test.dart +++ b/packages/flutter/test/painting/image_resolution_test.dart @@ -105,14 +105,12 @@ void main() { bundle: testAssetBundle, ); - // we have the exact match for this scale, let's use it assetImage.obtainKey(ImageConfiguration.empty) .then(expectAsync1((AssetBundleImageKey bundleKey) { expect(bundleKey.name, mainAssetPath); expect(bundleKey.scale, 1.0); })); - // we also have the exact match for this scale, let's use it assetImage.obtainKey(ImageConfiguration( bundle: testAssetBundle, devicePixelRatio: 3.0, @@ -122,7 +120,7 @@ void main() { })); }); - test('When high-res device and high-res asset not present in bundle then return main variant', () { + 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> assetBundleMap = diff --git a/packages/flutter_tools/lib/src/asset.dart b/packages/flutter_tools/lib/src/asset.dart index 61bee0e776..7d851d8ee9 100644 --- a/packages/flutter_tools/lib/src/asset.dart +++ b/packages/flutter_tools/lib/src/asset.dart @@ -644,7 +644,12 @@ class ManifestAssetBundle implements AssetBundle { 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) { - jsonObject[main.entryUri.path] = jsonEntries[main]!; + final String decodedEntryPath = Uri.decodeFull(main.entryUri.path); + final List rawEntryVariantsPaths = jsonEntries[main]!; + final List decodedEntryVariantPaths = rawEntryVariantsPaths + .map((String value) => Uri.decodeFull(value)) + .toList(); + jsonObject[decodedEntryPath] = decodedEntryVariantPaths; } return DevFSStringContent(json.encode(jsonObject)); } 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 cd5e1b1c99..5c2ef1d3bf 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 @@ -447,7 +447,7 @@ $assetsSection writePubspecFile('pubspec.yaml', 'test'); writePackagesFile('test_package:p/p/lib/'); - final List assets = ['a/foo', 'a/foo[x]']; + final List assets = ['a/foo', 'a/foo [x]']; writePubspecFile( 'p/p/pubspec.yaml', 'test_package', @@ -457,7 +457,7 @@ $assetsSection writeAssets('p/p/', assets); const String expectedAssetManifest = '{"packages/test_package/a/foo":["packages/test_package/a/foo"],' - '"packages/test_package/a/foo%5Bx%5D":["packages/test_package/a/foo%5Bx%5D"]}'; + '"packages/test_package/a/foo [x]":["packages/test_package/a/foo [x]"]}'; await buildAndVerifyAssets( assets, 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 c635a5f537..9bec3b7670 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 @@ -30,11 +30,11 @@ void main() { return parsedManifest; } - group('AssetBundle asset variants (with POSIX-style paths)', () { - late final Platform platform; - late final FileSystem fs; + group('AssetBundle asset variants (with Unix-style paths)', () { + late Platform platform; + late FileSystem fs; - setUpAll(() { + setUp(() { platform = FakePlatform(); fs = MemoryFileSystem.test(); Cache.flutterRoot = Cache.defaultFlutterRoot( @@ -87,10 +87,10 @@ flutter: ); final Map> manifest = await extractAssetManifestFromBundle(bundle); - final List variantsForImage = manifest[image]!; - expect(variantsForImage, contains(image2xVariant)); - expect(variantsForImage, isNot(contains(imageNonVariant))); + expect(manifest, hasLength(2)); + expect(manifest[image], equals([image, image2xVariant])); + expect(manifest[imageNonVariant], equals([imageNonVariant])); }); testWithoutContext('Asset directories are recursively searched for assets', () async { @@ -122,11 +122,40 @@ flutter: ); final Map> 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)); + expect(manifest, hasLength(2)); + expect(manifest[topLevelImage], equals([topLevelImage])); + expect(manifest[secondLevelImage], equals([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 assets = [ + 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> manifest = await extractAssetManifestFromBundle(bundle); + expect(manifest, hasLength(1)); + expect(manifest[image], equals([image, imageVariant])); }); }); @@ -135,13 +164,7 @@ flutter: 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(() { + setUp(() { platform = FakePlatform(operatingSystem: 'windows'); fs = MemoryFileSystem.test(style: FileSystemStyle.windows); Cache.flutterRoot = Cache.defaultFlutterRoot( @@ -165,19 +188,16 @@ flutter: ); }); - 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 assets = [ - image, - image2xVariant, - imageNonVariant + testWithoutContext('Variant detection works with windows-style filepaths', () async { + const List assets = [ + 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(correctPathSeparators(asset)); + final File assetFile = fs.file(asset); assetFile.createSync(recursive: true); assetFile.writeAsStringSync(asset); } @@ -194,46 +214,10 @@ flutter: ); final Map> manifest = await extractAssetManifestFromBundle(bundle); - final List 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 assets = [ - 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> 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)); + expect(manifest, hasLength(2)); + expect(manifest['assets/foo.jpg'], equals(['assets/foo.jpg', 'assets/2x/foo.jpg'])); + expect(manifest['assets/somewhereElse/bar.jpg'], equals(['assets/somewhereElse/bar.jpg', 'assets/somewhereElse/2x/bar.jpg'])); }); }); }