mirror of
https://github.com/flutter/flutter
synced 2024-09-13 05:11:45 +00:00
Fix AssetsEntry::equals
(#143355)
In service of https://github.com/flutter/flutter/issues/143348. **Issue.** The `equals` implementation of `AssetsEntry` is incorrect. It compares `flavors` lists using reference equality. This PR addresses this. This also adds a test to make sure valid asset `flavors` declarations are parsed correctly. While we are here, this PR also includes a couple of refactorings: * `flutter_manifest_test.dart` is a bit large. To better match our style guide, I've factored out some related tests into their own file. * A couple of changes to the `_validateListType` function in `flutter_manifest.dart`: * The function now returns a list of errors instead of accepting a list to append onto. This is more readable and also allows callers to know which errors were found by the call. * The function is renamed to `_validateList` and now accepts an `Object?` instead of an `YamlList`. If the argument is null, an appropriate error message is contained in the output. This saves callers that are only interested in validation from having to write their own null-check, which they all did before. * Some error strings were tweaked for increased readability and/or grammatical correctness.
This commit is contained in:
parent
4280d29534
commit
14bcc694ff
|
@ -949,7 +949,7 @@ class ManifestAssetBundle implements AssetBundle {
|
||||||
Uri assetUri, {
|
Uri assetUri, {
|
||||||
String? packageName,
|
String? packageName,
|
||||||
Package? attributedPackage,
|
Package? attributedPackage,
|
||||||
List<String>? flavors,
|
Set<String>? flavors,
|
||||||
}) {
|
}) {
|
||||||
final String directoryPath;
|
final String directoryPath;
|
||||||
try {
|
try {
|
||||||
|
@ -1000,7 +1000,7 @@ class ManifestAssetBundle implements AssetBundle {
|
||||||
String? packageName,
|
String? packageName,
|
||||||
Package? attributedPackage,
|
Package? attributedPackage,
|
||||||
AssetKind assetKind = AssetKind.regular,
|
AssetKind assetKind = AssetKind.regular,
|
||||||
List<String>? flavors,
|
Set<String>? flavors,
|
||||||
}) {
|
}) {
|
||||||
final _Asset asset = _resolveAsset(
|
final _Asset asset = _resolveAsset(
|
||||||
packageConfig,
|
packageConfig,
|
||||||
|
@ -1116,7 +1116,7 @@ class ManifestAssetBundle implements AssetBundle {
|
||||||
Package? attributedPackage, {
|
Package? attributedPackage, {
|
||||||
Uri? originUri,
|
Uri? originUri,
|
||||||
AssetKind assetKind = AssetKind.regular,
|
AssetKind assetKind = AssetKind.regular,
|
||||||
List<String>? flavors,
|
Set<String>? flavors,
|
||||||
}) {
|
}) {
|
||||||
final String assetPath = _fileSystem.path.fromUri(assetUri);
|
final String assetPath = _fileSystem.path.fromUri(assetUri);
|
||||||
if (assetUri.pathSegments.first == 'packages'
|
if (assetUri.pathSegments.first == 'packages'
|
||||||
|
@ -1155,7 +1155,7 @@ class ManifestAssetBundle implements AssetBundle {
|
||||||
Package? attributedPackage, {
|
Package? attributedPackage, {
|
||||||
AssetKind assetKind = AssetKind.regular,
|
AssetKind assetKind = AssetKind.regular,
|
||||||
Uri? originUri,
|
Uri? originUri,
|
||||||
List<String>? flavors,
|
Set<String>? flavors,
|
||||||
}) {
|
}) {
|
||||||
assert(assetUri.pathSegments.first == 'packages');
|
assert(assetUri.pathSegments.first == 'packages');
|
||||||
if (assetUri.pathSegments.length > 1) {
|
if (assetUri.pathSegments.length > 1) {
|
||||||
|
@ -1192,8 +1192,8 @@ class _Asset {
|
||||||
required this.entryUri,
|
required this.entryUri,
|
||||||
required this.package,
|
required this.package,
|
||||||
this.kind = AssetKind.regular,
|
this.kind = AssetKind.regular,
|
||||||
List<String>? flavors,
|
Set<String>? flavors,
|
||||||
}): originUri = originUri ?? entryUri, flavors = flavors ?? const <String>[];
|
}): originUri = originUri ?? entryUri, flavors = flavors ?? const <String>{};
|
||||||
|
|
||||||
final String baseDir;
|
final String baseDir;
|
||||||
|
|
||||||
|
@ -1212,7 +1212,7 @@ class _Asset {
|
||||||
|
|
||||||
final AssetKind kind;
|
final AssetKind kind;
|
||||||
|
|
||||||
final List<String> flavors;
|
final Set<String> flavors;
|
||||||
|
|
||||||
File lookupAssetFile(FileSystem fileSystem) {
|
File lookupAssetFile(FileSystem fileSystem) {
|
||||||
return fileSystem.file(fileSystem.path.join(baseDir, fileSystem.path.fromUri(relativeUri)));
|
return fileSystem.file(fileSystem.path.join(baseDir, fileSystem.path.fromUri(relativeUri)));
|
||||||
|
|
|
@ -479,3 +479,22 @@ Match? firstMatchInFile(File file, RegExp regExp) {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tests for shallow equality on two sets.
|
||||||
|
bool setEquals<T>(Set<T>? a, Set<T>? b) {
|
||||||
|
if (a == null) {
|
||||||
|
return b == null;
|
||||||
|
}
|
||||||
|
if (b == null || a.length != b.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (identical(a, b)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (final T value in a) {
|
||||||
|
if (!b.contains(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -519,17 +519,7 @@ void _validateFlutter(YamlMap? yaml, List<String> errors) {
|
||||||
_validateFonts(yamlValue, errors);
|
_validateFonts(yamlValue, errors);
|
||||||
}
|
}
|
||||||
case 'licenses':
|
case 'licenses':
|
||||||
if (yamlValue is! YamlList) {
|
errors.addAll(_validateList<String>(yamlValue, '"$yamlKey"', 'files'));
|
||||||
errors.add('Expected "$yamlKey" to be a list of files, but got $yamlValue (${yamlValue.runtimeType})');
|
|
||||||
} else if (yamlValue.isEmpty) {
|
|
||||||
break;
|
|
||||||
} else if (yamlValue.first is! String) {
|
|
||||||
errors.add(
|
|
||||||
'Expected "$yamlKey" to contain strings, but the first element is $yamlValue (${yamlValue.runtimeType}).',
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
_validateListType<String>(yamlValue, errors, '"$yamlKey"', 'files');
|
|
||||||
}
|
|
||||||
case 'module':
|
case 'module':
|
||||||
if (yamlValue is! YamlMap) {
|
if (yamlValue is! YamlMap) {
|
||||||
errors.add('Expected "$yamlKey" to be an object, but got $yamlValue (${yamlValue.runtimeType}).');
|
errors.add('Expected "$yamlKey" to be an object, but got $yamlValue (${yamlValue.runtimeType}).');
|
||||||
|
@ -563,15 +553,22 @@ void _validateFlutter(YamlMap? yaml, List<String> errors) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _validateListType<T>(YamlList yamlList, List<String> errors, String context, String typeAlias) {
|
List<String> _validateList<T>(Object? yamlList, String context, String typeAlias) {
|
||||||
|
final List<String> errors = <String>[];
|
||||||
|
|
||||||
|
if (yamlList is! YamlList) {
|
||||||
|
return <String>['Expected $context to be a list of $typeAlias, but got $yamlList (${yamlList.runtimeType}).'];
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < yamlList.length; i++) {
|
for (int i = 0; i < yamlList.length; i++) {
|
||||||
if (yamlList[i] is! T) {
|
if (yamlList[i] is! T) {
|
||||||
// ignore: avoid_dynamic_calls
|
// ignore: avoid_dynamic_calls
|
||||||
errors.add('Expected $context to be a list of $typeAlias, but element $i was a ${yamlList[i].runtimeType}');
|
errors.add('Expected $context to be a list of $typeAlias, but element at index $i was a ${yamlList[i].runtimeType}.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
void _validateDeferredComponents(MapEntry<Object?, Object?> kvp, List<String> errors) {
|
void _validateDeferredComponents(MapEntry<Object?, Object?> kvp, List<String> errors) {
|
||||||
final Object? yamlList = kvp.value;
|
final Object? yamlList = kvp.value;
|
||||||
if (yamlList != null && (yamlList is! YamlList || yamlList[0] is! YamlMap)) {
|
if (yamlList != null && (yamlList is! YamlList || yamlList[0] is! YamlMap)) {
|
||||||
|
@ -588,12 +585,11 @@ void _validateDeferredComponents(MapEntry<Object?, Object?> kvp, List<String> er
|
||||||
errors.add('Expected the $i element in "${kvp.key}" to have required key "name" of type String');
|
errors.add('Expected the $i element in "${kvp.key}" to have required key "name" of type String');
|
||||||
}
|
}
|
||||||
if (valueMap.containsKey('libraries')) {
|
if (valueMap.containsKey('libraries')) {
|
||||||
final Object? libraries = valueMap['libraries'];
|
errors.addAll(_validateList<String>(
|
||||||
if (libraries is! YamlList) {
|
valueMap['libraries'],
|
||||||
errors.add('Expected "libraries" key in the $i element of "${kvp.key}" to be a list, but got $libraries (${libraries.runtimeType}).');
|
'"libraries" key in the element at index $i of "${kvp.key}"',
|
||||||
} else {
|
'String',
|
||||||
_validateListType<String>(libraries, errors, '"libraries" key in the $i element of "${kvp.key}"', 'dart library Strings');
|
));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (valueMap.containsKey('assets')) {
|
if (valueMap.containsKey('assets')) {
|
||||||
errors.addAll(_validateAssets(valueMap['assets']));
|
errors.addAll(_validateAssets(valueMap['assets']));
|
||||||
|
@ -700,11 +696,11 @@ void _validateFonts(YamlList fonts, List<String> errors) {
|
||||||
class AssetsEntry {
|
class AssetsEntry {
|
||||||
const AssetsEntry({
|
const AssetsEntry({
|
||||||
required this.uri,
|
required this.uri,
|
||||||
this.flavors = const <String>[],
|
this.flavors = const <String>{},
|
||||||
});
|
});
|
||||||
|
|
||||||
final Uri uri;
|
final Uri uri;
|
||||||
final List<String> flavors;
|
final Set<String> flavors;
|
||||||
|
|
||||||
static const String _pathKey = 'path';
|
static const String _pathKey = 'path';
|
||||||
static const String _flavorKey = 'flavors';
|
static const String _flavorKey = 'flavors';
|
||||||
|
@ -762,8 +758,11 @@ class AssetsEntry {
|
||||||
'Got ${flavors.runtimeType} instead.');
|
'Got ${flavors.runtimeType} instead.');
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<String> flavorsListErrors = <String>[];
|
final List<String> flavorsListErrors = _validateList<String>(
|
||||||
_validateListType<String>(flavors, flavorsListErrors, 'flavors list of entry "$path"', 'String');
|
flavors,
|
||||||
|
'flavors list of entry "$path"',
|
||||||
|
'String',
|
||||||
|
);
|
||||||
if (flavorsListErrors.isNotEmpty) {
|
if (flavorsListErrors.isNotEmpty) {
|
||||||
return (null, 'Asset manifest entry is malformed. '
|
return (null, 'Asset manifest entry is malformed. '
|
||||||
'Expected "$_flavorKey" entry to be a list of strings.\n'
|
'Expected "$_flavorKey" entry to be a list of strings.\n'
|
||||||
|
@ -772,7 +771,7 @@ class AssetsEntry {
|
||||||
|
|
||||||
final AssetsEntry entry = AssetsEntry(
|
final AssetsEntry entry = AssetsEntry(
|
||||||
uri: Uri(pathSegments: path.split('/')),
|
uri: Uri(pathSegments: path.split('/')),
|
||||||
flavors: List<String>.from(flavors),
|
flavors: Set<String>.from(flavors),
|
||||||
);
|
);
|
||||||
|
|
||||||
return (entry, null);
|
return (entry, null);
|
||||||
|
@ -788,9 +787,15 @@ class AssetsEntry {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return uri == other.uri && flavors == other.flavors;
|
return uri == other.uri && setEquals(flavors, other.flavors);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(uri.hashCode, flavors.hashCode);
|
int get hashCode => Object.hashAll(<Object?>[
|
||||||
|
uri.hashCode,
|
||||||
|
Object.hashAllUnordered(flavors),
|
||||||
|
]);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'AssetsEntry(uri: $uri, flavors: $flavors)';
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
|
import 'package:flutter_tools/src/flutter_manifest.dart';
|
||||||
|
|
||||||
|
import '../src/common.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('parsing of assets section in flutter manifests', () {
|
||||||
|
testWithoutContext('ignores empty list of assets', () {
|
||||||
|
final BufferLogger logger = BufferLogger.test();
|
||||||
|
|
||||||
|
const String manifest = '''
|
||||||
|
name: test
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
flutter:
|
||||||
|
assets: []
|
||||||
|
''';
|
||||||
|
|
||||||
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
|
manifest,
|
||||||
|
logger: logger,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(flutterManifest, isNotNull);
|
||||||
|
expect(flutterManifest!.assets, isEmpty);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('parses two simple asset declarations', () async {
|
||||||
|
final BufferLogger logger = BufferLogger.test();
|
||||||
|
const String manifest = '''
|
||||||
|
name: test
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
||||||
|
assets:
|
||||||
|
- a/foo
|
||||||
|
- a/bar
|
||||||
|
''';
|
||||||
|
|
||||||
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
|
manifest,
|
||||||
|
logger: logger,
|
||||||
|
)!;
|
||||||
|
|
||||||
|
expect(flutterManifest.assets, <AssetsEntry>[
|
||||||
|
AssetsEntry(uri: Uri.parse('a/foo')),
|
||||||
|
AssetsEntry(uri: Uri.parse('a/bar')),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('does not crash on empty entry', () {
|
||||||
|
final BufferLogger logger = BufferLogger.test();
|
||||||
|
const String manifest = '''
|
||||||
|
name: test
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
||||||
|
assets:
|
||||||
|
- lib/gallery/example_code.dart
|
||||||
|
-
|
||||||
|
''';
|
||||||
|
|
||||||
|
FlutterManifest.createFromString(
|
||||||
|
manifest,
|
||||||
|
logger: logger,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(logger.errorText, contains('Asset manifest contains a null or empty uri.'));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('handles special characters in asset URIs', () {
|
||||||
|
final BufferLogger logger = BufferLogger.test();
|
||||||
|
|
||||||
|
const String manifest = '''
|
||||||
|
name: test
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
||||||
|
assets:
|
||||||
|
- lib/gallery/abc#xyz
|
||||||
|
- lib/gallery/abc?xyz
|
||||||
|
- lib/gallery/aaa bbb
|
||||||
|
''';
|
||||||
|
|
||||||
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
|
manifest,
|
||||||
|
logger: logger,
|
||||||
|
)!;
|
||||||
|
final List<AssetsEntry> assets = flutterManifest.assets;
|
||||||
|
|
||||||
|
expect(assets, <AssetsEntry>[
|
||||||
|
AssetsEntry(uri: Uri.parse('lib/gallery/abc%23xyz')),
|
||||||
|
AssetsEntry(uri: Uri.parse('lib/gallery/abc%3Fxyz')),
|
||||||
|
AssetsEntry(uri: Uri.parse('lib/gallery/aaa%20bbb')),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('parses an asset with flavors', () async {
|
||||||
|
final BufferLogger logger = BufferLogger.test();
|
||||||
|
const String manifest = '''
|
||||||
|
name: test
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
||||||
|
assets:
|
||||||
|
- path: a/foo
|
||||||
|
flavors:
|
||||||
|
- apple
|
||||||
|
- strawberry
|
||||||
|
''';
|
||||||
|
|
||||||
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
|
manifest,
|
||||||
|
logger: logger,
|
||||||
|
)!;
|
||||||
|
|
||||||
|
expect(flutterManifest.assets, <AssetsEntry>[
|
||||||
|
AssetsEntry(
|
||||||
|
uri: Uri.parse('a/foo'),
|
||||||
|
flavors: const <String>{'apple', 'strawberry'},
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext("prints an error when an asset entry's flavor is not a string", () async {
|
||||||
|
final BufferLogger logger = BufferLogger.test();
|
||||||
|
|
||||||
|
const String manifest = '''
|
||||||
|
name: test
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
||||||
|
assets:
|
||||||
|
- assets/folder/
|
||||||
|
- path: assets/vanilla/
|
||||||
|
flavors:
|
||||||
|
- key1: value1
|
||||||
|
key2: value2
|
||||||
|
''';
|
||||||
|
FlutterManifest.createFromString(manifest, logger: logger);
|
||||||
|
expect(logger.errorText, contains(
|
||||||
|
'Asset manifest entry is malformed. '
|
||||||
|
'Expected "flavors" entry to be a list of strings.',
|
||||||
|
));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -136,50 +136,6 @@ flutter:
|
||||||
expect(flutterManifest.generateSyntheticPackage, false);
|
expect(flutterManifest.generateSyntheticPackage, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest has two assets', () async {
|
|
||||||
const String manifest = '''
|
|
||||||
name: test
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
flutter:
|
|
||||||
uses-material-design: true
|
|
||||||
assets:
|
|
||||||
- a/foo
|
|
||||||
- a/bar
|
|
||||||
''';
|
|
||||||
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
|
||||||
manifest,
|
|
||||||
logger: logger,
|
|
||||||
)!;
|
|
||||||
|
|
||||||
expect(flutterManifest.assets, <AssetsEntry>[
|
|
||||||
AssetsEntry(uri: Uri.parse('a/foo')),
|
|
||||||
AssetsEntry(uri: Uri.parse('a/bar')),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest assets entry flavor is not a string', () async {
|
|
||||||
const String manifest = '''
|
|
||||||
name: test
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
flutter:
|
|
||||||
uses-material-design: true
|
|
||||||
assets:
|
|
||||||
- assets/folder/
|
|
||||||
- path: assets/vanilla/
|
|
||||||
flavors:
|
|
||||||
- key1: value1
|
|
||||||
key2: value2
|
|
||||||
''';
|
|
||||||
FlutterManifest.createFromString(manifest, logger: logger);
|
|
||||||
expect(logger.errorText, contains('Asset manifest entry is malformed. '
|
|
||||||
'Expected "flavors" entry to be a list of strings.'));
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest has one font family with one asset', () async {
|
testWithoutContext('FlutterManifest has one font family with one asset', () async {
|
||||||
const String manifest = '''
|
const String manifest = '''
|
||||||
name: test
|
name: test
|
||||||
|
@ -796,25 +752,6 @@ flutter:
|
||||||
expect(flutterManifest!.fonts.length, 0);
|
expect(flutterManifest!.fonts.length, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest ignores empty list of assets', () {
|
|
||||||
const String manifest = '''
|
|
||||||
name: test
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
flutter:
|
|
||||||
assets: []
|
|
||||||
''';
|
|
||||||
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
|
||||||
manifest,
|
|
||||||
logger: logger,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(flutterManifest, isNotNull);
|
|
||||||
expect(flutterManifest!.assets.length, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest returns proper error when font detail is '
|
testWithoutContext('FlutterManifest returns proper error when font detail is '
|
||||||
'not a list of maps', () {
|
'not a list of maps', () {
|
||||||
const String manifest = '''
|
const String manifest = '''
|
||||||
|
@ -887,55 +824,6 @@ flutter:
|
||||||
expect(logger.errorText, contains('Expected a map.'));
|
expect(logger.errorText, contains('Expected a map.'));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest does not crash on empty entry', () {
|
|
||||||
const String manifest = '''
|
|
||||||
name: test
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
flutter:
|
|
||||||
uses-material-design: true
|
|
||||||
assets:
|
|
||||||
- lib/gallery/example_code.dart
|
|
||||||
-
|
|
||||||
''';
|
|
||||||
|
|
||||||
FlutterManifest.createFromString(
|
|
||||||
manifest,
|
|
||||||
logger: logger,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(logger.errorText, contains('Asset manifest contains a null or empty uri.'));
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest handles special characters in asset URIs', () {
|
|
||||||
const String manifest = '''
|
|
||||||
name: test
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
flutter:
|
|
||||||
uses-material-design: true
|
|
||||||
assets:
|
|
||||||
- lib/gallery/abc#xyz
|
|
||||||
- lib/gallery/abc?xyz
|
|
||||||
- lib/gallery/aaa bbb
|
|
||||||
''';
|
|
||||||
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
|
||||||
manifest,
|
|
||||||
logger: logger,
|
|
||||||
)!;
|
|
||||||
final List<AssetsEntry> assets = flutterManifest.assets;
|
|
||||||
|
|
||||||
expect(assets, hasLength(3));
|
|
||||||
expect(assets, <AssetsEntry>[
|
|
||||||
AssetsEntry(uri: Uri.parse('lib/gallery/abc%23xyz')),
|
|
||||||
AssetsEntry(uri: Uri.parse('lib/gallery/abc%3Fxyz')),
|
|
||||||
AssetsEntry(uri: Uri.parse('lib/gallery/aaa%20bbb')),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest returns proper error when flutter is a '
|
testWithoutContext('FlutterManifest returns proper error when flutter is a '
|
||||||
'list instead of a map', () {
|
'list instead of a map', () {
|
||||||
const String manifest = '''
|
const String manifest = '''
|
||||||
|
@ -1186,7 +1074,7 @@ flutter:
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(flutterManifest, null);
|
expect(flutterManifest, null);
|
||||||
expect(logger.errorText, 'Expected "licenses" to be a list of files, but got foo.txt (String)\n');
|
expect(logger.errorText, 'Expected "licenses" to be a list of files, but got foo.txt (String).\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest validates individual list items', () async {
|
testWithoutContext('FlutterManifest validates individual list items', () async {
|
||||||
|
@ -1207,7 +1095,8 @@ flutter:
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(flutterManifest, null);
|
expect(flutterManifest, null);
|
||||||
expect(logger.errorText, 'Expected "licenses" to be a list of files, but element 1 was a YamlMap\n');
|
expect(logger.errorText, 'Expected "licenses" to be a list of files, but '
|
||||||
|
'element at index 1 was a YamlMap.\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest parses single deferred components', () async {
|
testWithoutContext('FlutterManifest parses single deferred components', () async {
|
||||||
|
@ -1360,7 +1249,9 @@ flutter:
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(flutterManifest, null);
|
expect(flutterManifest, null);
|
||||||
expect(logger.errorText, 'Expected "libraries" key in the 0 element of "deferred-components" to be a list, but got blah (String).\n');
|
expect(logger.errorText, 'Expected "libraries" key in the element at '
|
||||||
|
'index 0 of "deferred-components" to be a list of String, but '
|
||||||
|
'got blah (String).\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest deferred component libraries is string', () async {
|
testWithoutContext('FlutterManifest deferred component libraries is string', () async {
|
||||||
|
@ -1382,7 +1273,9 @@ flutter:
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(flutterManifest, null);
|
expect(flutterManifest, null);
|
||||||
expect(logger.errorText, 'Expected "libraries" key in the 0 element of "deferred-components" to be a list of dart library Strings, but element 0 was a YamlMap\n');
|
expect(logger.errorText, 'Expected "libraries" key in the element at '
|
||||||
|
'index 0 of "deferred-components" to be a list of String, but '
|
||||||
|
'element at index 0 was a YamlMap.\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest deferred component assets is string', () async {
|
testWithoutContext('FlutterManifest deferred component assets is string', () async {
|
||||||
|
|
Loading…
Reference in a new issue