Unverified Commit f8c9f72b authored by Andrew Kolos's avatar Andrew Kolos Committed by GitHub

URI-decode asset paths before writing them to the asset manifest (#112415)

parent ecdfa953
...@@ -105,14 +105,12 @@ void main() { ...@@ -105,14 +105,12 @@ void main() {
bundle: testAssetBundle, bundle: testAssetBundle,
); );
// we have the exact match for this scale, let's use it
assetImage.obtainKey(ImageConfiguration.empty) assetImage.obtainKey(ImageConfiguration.empty)
.then(expectAsync1((AssetBundleImageKey bundleKey) { .then(expectAsync1((AssetBundleImageKey bundleKey) {
expect(bundleKey.name, mainAssetPath); expect(bundleKey.name, mainAssetPath);
expect(bundleKey.scale, 1.0); expect(bundleKey.scale, 1.0);
})); }));
// we also have the exact match for this scale, let's use it
assetImage.obtainKey(ImageConfiguration( assetImage.obtainKey(ImageConfiguration(
bundle: testAssetBundle, bundle: testAssetBundle,
devicePixelRatio: 3.0, devicePixelRatio: 3.0,
...@@ -122,7 +120,7 @@ void main() { ...@@ -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'; const String mainAssetPath = 'assets/normalFolder/normalFile.png';
final Map<String, List<String>> assetBundleMap = final Map<String, List<String>> assetBundleMap =
......
...@@ -644,7 +644,12 @@ class ManifestAssetBundle implements AssetBundle { ...@@ -644,7 +644,12 @@ class ManifestAssetBundle implements AssetBundle {
final List<_Asset> sortedKeys = jsonEntries.keys.toList() final List<_Asset> sortedKeys = jsonEntries.keys.toList()
..sort((_Asset left, _Asset right) => left.entryUri.path.compareTo(right.entryUri.path)); ..sort((_Asset left, _Asset right) => left.entryUri.path.compareTo(right.entryUri.path));
for (final _Asset main in sortedKeys) { for (final _Asset main in sortedKeys) {
jsonObject[main.entryUri.path] = jsonEntries[main]!; final String decodedEntryPath = Uri.decodeFull(main.entryUri.path);
final List<String> rawEntryVariantsPaths = jsonEntries[main]!;
final List<String> decodedEntryVariantPaths = rawEntryVariantsPaths
.map((String value) => Uri.decodeFull(value))
.toList();
jsonObject[decodedEntryPath] = decodedEntryVariantPaths;
} }
return DevFSStringContent(json.encode(jsonObject)); return DevFSStringContent(json.encode(jsonObject));
} }
......
...@@ -447,7 +447,7 @@ $assetsSection ...@@ -447,7 +447,7 @@ $assetsSection
writePubspecFile('pubspec.yaml', 'test'); writePubspecFile('pubspec.yaml', 'test');
writePackagesFile('test_package:p/p/lib/'); writePackagesFile('test_package:p/p/lib/');
final List<String> assets = <String>['a/foo', 'a/foo[x]']; final List<String> assets = <String>['a/foo', 'a/foo [x]'];
writePubspecFile( writePubspecFile(
'p/p/pubspec.yaml', 'p/p/pubspec.yaml',
'test_package', 'test_package',
...@@ -457,7 +457,7 @@ $assetsSection ...@@ -457,7 +457,7 @@ $assetsSection
writeAssets('p/p/', assets); writeAssets('p/p/', assets);
const String expectedAssetManifest = const String expectedAssetManifest =
'{"packages/test_package/a/foo":["packages/test_package/a/foo"],' '{"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( await buildAndVerifyAssets(
assets, assets,
......
...@@ -30,11 +30,11 @@ void main() { ...@@ -30,11 +30,11 @@ void main() {
return parsedManifest; return parsedManifest;
} }
group('AssetBundle asset variants (with POSIX-style paths)', () { group('AssetBundle asset variants (with Unix-style paths)', () {
late final Platform platform; late Platform platform;
late final FileSystem fs; late FileSystem fs;
setUpAll(() { setUp(() {
platform = FakePlatform(); platform = FakePlatform();
fs = MemoryFileSystem.test(); fs = MemoryFileSystem.test();
Cache.flutterRoot = Cache.defaultFlutterRoot( Cache.flutterRoot = Cache.defaultFlutterRoot(
...@@ -87,10 +87,10 @@ flutter: ...@@ -87,10 +87,10 @@ flutter:
); );
final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle); final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle);
final List<String> variantsForImage = manifest[image]!;
expect(variantsForImage, contains(image2xVariant)); expect(manifest, hasLength(2));
expect(variantsForImage, isNot(contains(imageNonVariant))); expect(manifest[image], equals(<String>[image, image2xVariant]));
expect(manifest[imageNonVariant], equals(<String>[imageNonVariant]));
}); });
testWithoutContext('Asset directories are recursively searched for assets', () async { testWithoutContext('Asset directories are recursively searched for assets', () async {
...@@ -122,11 +122,40 @@ flutter: ...@@ -122,11 +122,40 @@ flutter:
); );
final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle); final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle);
expect(manifest, contains(secondLevelImage)); expect(manifest, hasLength(2));
expect(manifest, contains(topLevelImage)); expect(manifest[topLevelImage], equals(<String>[topLevelImage]));
expect(manifest[secondLevelImage], hasLength(2)); expect(manifest[secondLevelImage], equals(<String>[secondLevelImage, secondLevel2xVariant]));
expect(manifest[secondLevelImage], contains(secondLevelImage)); });
expect(manifest[secondLevelImage], contains(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]));
}); });
}); });
...@@ -135,13 +164,7 @@ flutter: ...@@ -135,13 +164,7 @@ flutter:
late final Platform platform; late final Platform platform;
late final FileSystem fs; late final FileSystem fs;
String correctPathSeparators(String path) { setUp(() {
// 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'); platform = FakePlatform(operatingSystem: 'windows');
fs = MemoryFileSystem.test(style: FileSystemStyle.windows); fs = MemoryFileSystem.test(style: FileSystemStyle.windows);
Cache.flutterRoot = Cache.defaultFlutterRoot( Cache.flutterRoot = Cache.defaultFlutterRoot(
...@@ -165,19 +188,16 @@ flutter: ...@@ -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 { testWithoutContext('Variant detection works with windows-style filepaths', () async {
const String image = 'assets/image.jpg'; const List<String> assets = <String>[
const String image2xVariant = 'assets/2x/image.jpg'; r'assets\foo.jpg',
const String imageNonVariant = 'assets/notAVariant/image.jpg'; r'assets\2x\foo.jpg',
r'assets\somewhereElse\bar.jpg',
final List<String> assets = <String>[ r'assets\somewhereElse\2x\bar.jpg',
image,
image2xVariant,
imageNonVariant
]; ];
for (final String asset in assets) { for (final String asset in assets) {
final File assetFile = fs.file(correctPathSeparators(asset)); final File assetFile = fs.file(asset);
assetFile.createSync(recursive: true); assetFile.createSync(recursive: true);
assetFile.writeAsStringSync(asset); assetFile.writeAsStringSync(asset);
} }
...@@ -194,46 +214,10 @@ flutter: ...@@ -194,46 +214,10 @@ flutter:
); );
final Map<String, List<String>> manifest = await extractAssetManifestFromBundle(bundle); 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(manifest, hasLength(2));
final File assetFile = fs.file(correctPathSeparators(asset)); expect(manifest['assets/foo.jpg'], equals(<String>['assets/foo.jpg', 'assets/2x/foo.jpg']));
assetFile.createSync(recursive: true); expect(manifest['assets/somewhereElse/bar.jpg'], equals(<String>['assets/somewhereElse/bar.jpg', 'assets/somewhereElse/2x/bar.jpg']));
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));
}); });
}); });
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment