Unverified Commit 8ed40ddd authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] refactor FlutterManifest to be context-free (#54555)

parent 62621507
......@@ -128,7 +128,11 @@ class _ManifestAssetBundle implements AssetBundle {
packagesPath ??= globals.fs.path.absolute(PackageMap.globalPackagesPath);
FlutterManifest flutterManifest;
try {
flutterManifest = FlutterManifest.createFromPath(manifestPath);
flutterManifest = FlutterManifest.createFromPath(
manifestPath,
logger: globals.logger,
fileSystem: globals.fs,
);
} on Exception catch (e) {
globals.printStatus('Error detected in pubspec.yaml:', emphasis: true);
globals.printError('$e');
......@@ -187,7 +191,11 @@ class _ManifestAssetBundle implements AssetBundle {
final Uri packageUri = package.packageUriRoot;
if (packageUri != null && packageUri.scheme == 'file') {
final String packageManifestPath = globals.fs.path.fromUri(packageUri.resolve('../pubspec.yaml'));
final FlutterManifest packageFlutterManifest = FlutterManifest.createFromPath(packageManifestPath);
final FlutterManifest packageFlutterManifest = FlutterManifest.createFromPath(
packageManifestPath,
logger: globals.logger,
fileSystem: globals.fs,
);
if (packageFlutterManifest == null) {
continue;
}
......
......@@ -7,42 +7,45 @@ import 'package:pub_semver/pub_semver.dart';
import 'package:yaml/yaml.dart';
import 'base/file_system.dart';
import 'base/logger.dart';
import 'base/user_messages.dart';
import 'base/utils.dart';
import 'cache.dart';
import 'globals.dart' as globals;
import 'plugins.dart';
/// A wrapper around the `flutter` section in the `pubspec.yaml` file.
class FlutterManifest {
FlutterManifest._();
FlutterManifest._(this._logger);
/// Returns an empty manifest.
static FlutterManifest empty() {
final FlutterManifest manifest = FlutterManifest._();
factory FlutterManifest.empty({ @required Logger logger }) {
final FlutterManifest manifest = FlutterManifest._(logger);
manifest._descriptor = const <String, dynamic>{};
manifest._flutterDescriptor = const <String, dynamic>{};
return manifest;
}
/// Returns null on invalid manifest. Returns empty manifest on missing file.
static FlutterManifest createFromPath(String path) {
if (path == null || !globals.fs.isFileSync(path)) {
return _createFromYaml(null);
static FlutterManifest createFromPath(String path, {
@required FileSystem fileSystem,
@required Logger logger,
}) {
if (path == null || !fileSystem.isFileSync(path)) {
return _createFromYaml(null, logger);
}
final String manifest = globals.fs.file(path).readAsStringSync();
return createFromString(manifest);
final String manifest = fileSystem.file(path).readAsStringSync();
return FlutterManifest.createFromString(manifest, logger: logger);
}
/// Returns null on missing or invalid manifest
@visibleForTesting
static FlutterManifest createFromString(String manifest) {
return _createFromYaml(loadYaml(manifest) as YamlMap);
static FlutterManifest createFromString(String manifest, { @required Logger logger }) {
return _createFromYaml(loadYaml(manifest) as YamlMap, logger);
}
static FlutterManifest _createFromYaml(YamlMap yamlDocument) {
final FlutterManifest pubspec = FlutterManifest._();
if (yamlDocument != null && !_validate(yamlDocument)) {
static FlutterManifest _createFromYaml(YamlMap yamlDocument, Logger logger) {
final FlutterManifest pubspec = FlutterManifest._(logger);
if (yamlDocument != null && !_validate(yamlDocument, logger)) {
return null;
}
......@@ -63,6 +66,8 @@ class FlutterManifest {
return pubspec;
}
final Logger _logger;
/// A map representation of the entire `pubspec.yaml` file.
Map<String, dynamic> _descriptor;
......@@ -91,7 +96,7 @@ class FlutterManifest {
version = Version.parse(verStr);
} on Exception {
if (!_hasShowInvalidVersionMsg) {
globals.printStatus(userMessages.invalidVersionSettingHintMessage(verStr), emphasis: true);
_logger.printStatus(userMessages.invalidVersionSettingHintMessage(verStr), emphasis: true);
_hasShowInvalidVersionMsg = true;
}
}
......@@ -203,14 +208,14 @@ class FlutterManifest {
final List<Uri> results = <Uri>[];
for (final Object asset in assets) {
if (asset is! String || asset == null || asset == '') {
globals.printError('Asset manifest contains a null or empty uri.');
_logger.printError('Asset manifest contains a null or empty uri.');
continue;
}
final String stringAsset = asset as String;
try {
results.add(Uri(pathSegments: stringAsset.split('/')));
} on FormatException {
globals.printError('Asset manifest contains invalid uri: $asset.');
_logger.printError('Asset manifest contains invalid uri: $asset.');
}
}
return results;
......@@ -233,11 +238,11 @@ class FlutterManifest {
final YamlList fontFiles = fontFamily['fonts'] as YamlList;
final String familyName = fontFamily['family'] as String;
if (familyName == null) {
globals.printError('Warning: Missing family name for font.', emphasis: true);
_logger.printError('Warning: Missing family name for font.', emphasis: true);
continue;
}
if (fontFiles == null) {
globals.printError('Warning: No fonts specified for font $familyName', emphasis: true);
_logger.printError('Warning: No fonts specified for font $familyName', emphasis: true);
continue;
}
......@@ -245,7 +250,7 @@ class FlutterManifest {
for (final Map<dynamic, dynamic> fontFile in fontFiles.cast<Map<dynamic, dynamic>>()) {
final String asset = fontFile['asset'] as String;
if (asset == null) {
globals.printError('Warning: Missing asset in fonts for $familyName', emphasis: true);
_logger.printError('Warning: Missing asset in fonts for $familyName', emphasis: true);
continue;
}
......@@ -310,16 +315,16 @@ class FontAsset {
}
@visibleForTesting
String buildSchemaDir(FileSystem fs) {
return globals.fs.path.join(
globals.fs.path.absolute(Cache.flutterRoot), 'packages', 'flutter_tools', 'schema',
String buildSchemaDir(FileSystem fileSystem) {
return fileSystem.path.join(
fileSystem.path.absolute(Cache.flutterRoot), 'packages', 'flutter_tools', 'schema',
);
}
@visibleForTesting
String buildSchemaPath(FileSystem fs) {
return globals.fs.path.join(
buildSchemaDir(fs),
String buildSchemaPath(FileSystem fileSystem) {
return fileSystem.path.join(
buildSchemaDir(fileSystem),
'pubspec_yaml.json',
);
}
......@@ -327,7 +332,7 @@ String buildSchemaPath(FileSystem fs) {
/// This method should be kept in sync with the schema in
/// `$FLUTTER_ROOT/packages/flutter_tools/schema/pubspec_yaml.json`,
/// but avoid introducing dependencies on packages for simple validation.
bool _validate(YamlMap manifest) {
bool _validate(YamlMap manifest, Logger logger) {
final List<String> errors = <String>[];
for (final MapEntry<dynamic, dynamic> kvp in manifest.entries) {
if (kvp.key is! String) {
......@@ -357,8 +362,8 @@ bool _validate(YamlMap manifest) {
}
if (errors.isNotEmpty) {
globals.printStatus('Error detected in pubspec.yaml:', emphasis: true);
globals.printError(errors.join('\n'));
logger.printStatus('Error detected in pubspec.yaml:', emphasis: true);
logger.printError(errors.join('\n'));
return false;
}
......
......@@ -43,7 +43,10 @@ Logger get logger => context.get<Logger>();
OperatingSystemUtils get os => context.get<OperatingSystemUtils>();
PersistentToolState get persistentToolState => PersistentToolState.instance;
Usage get flutterUsage => context.get<Usage>();
FlutterProjectFactory get projectFactory => context.get<FlutterProjectFactory>() ?? FlutterProjectFactory();
FlutterProjectFactory get projectFactory => context.get<FlutterProjectFactory>() ?? FlutterProjectFactory(
logger: logger,
fileSystem: fs,
);
const FileSystem _kLocalFs = LocalFileSystem();
......
......@@ -12,6 +12,7 @@ import 'android/gradle_utils.dart' as gradle;
import 'artifacts.dart';
import 'base/common.dart';
import 'base/file_system.dart';
import 'base/logger.dart';
import 'build_info.dart';
import 'bundle.dart' as bundle;
import 'features.dart';
......@@ -24,7 +25,14 @@ import 'plugins.dart';
import 'template.dart';
class FlutterProjectFactory {
FlutterProjectFactory();
FlutterProjectFactory({
@required Logger logger,
@required FileSystem fileSystem,
}) : _logger = logger,
_fileSystem = fileSystem;
final Logger _logger;
final FileSystem _fileSystem;
@visibleForTesting
final Map<String, FlutterProject> projects =
......@@ -34,14 +42,18 @@ class FlutterProjectFactory {
/// if `pubspec.yaml` or `example/pubspec.yaml` is invalid.
FlutterProject fromDirectory(Directory directory) {
assert(directory != null);
return projects.putIfAbsent(directory.path, /* ifAbsent */ () {
return projects.putIfAbsent(directory.path, () {
final FlutterManifest manifest = FlutterProject._readManifest(
directory.childFile(bundle.defaultManifestPath).path,
logger: _logger,
fileSystem: _fileSystem,
);
final FlutterManifest exampleManifest = FlutterProject._readManifest(
FlutterProject._exampleDirectory(directory)
.childFile(bundle.defaultManifestPath)
.path,
logger: _logger,
fileSystem: _fileSystem,
);
return FlutterProject(directory, manifest, exampleManifest);
});
......@@ -167,7 +179,7 @@ class FlutterProject {
FlutterProject get example => FlutterProject(
_exampleDirectory(directory),
_exampleManifest,
FlutterManifest.empty(),
FlutterManifest.empty(logger: globals.logger),
);
/// True if this project is a Flutter module project.
......@@ -187,13 +199,20 @@ class FlutterProject {
///
/// Completes with an empty [FlutterManifest], if the file does not exist.
/// Completes with a ToolExit on validation error.
static FlutterManifest _readManifest(String path) {
static FlutterManifest _readManifest(String path, {
@required Logger logger,
@required FileSystem fileSystem,
}) {
FlutterManifest manifest;
try {
manifest = FlutterManifest.createFromPath(path);
manifest = FlutterManifest.createFromPath(
path,
logger: logger,
fileSystem: fileSystem,
);
} on YamlException catch (e) {
globals.printStatus('Error detected in pubspec.yaml:', emphasis: true);
globals.printError('$e');
logger.printStatus('Error detected in pubspec.yaml:', emphasis: true);
logger.printError('$e');
}
if (manifest == null) {
throwToolExit('Please correct the pubspec.yaml file at $path');
......
......@@ -2,54 +2,60 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/flutter_manifest.dart';
import '../src/common.dart';
import '../src/context.dart';
import '../src/pubspec_schema.dart';
void main() {
setUpAll(() {
Cache.flutterRoot = getFlutterRoot();
});
group('FlutterManifest', () {
testUsingContext('is empty when the pubspec.yaml file is empty', () async {
final FlutterManifest flutterManifest = FlutterManifest.createFromString('');
expect(flutterManifest.isEmpty, true);
expect(flutterManifest.appName, '');
expect(flutterManifest.usesMaterialDesign, false);
expect(flutterManifest.fontsDescriptor, isEmpty);
expect(flutterManifest.fonts, isEmpty);
expect(flutterManifest.assets, isEmpty);
});
testWithoutContext('FlutterManifest is empty when the pubspec.yaml file is empty', () async {
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
'',
logger: logger,
);
test('has no fonts or assets when the "flutter" section is empty', () async {
const String manifest = '''
expect(flutterManifest.isEmpty, true);
expect(flutterManifest.appName, '');
expect(flutterManifest.usesMaterialDesign, false);
expect(flutterManifest.fontsDescriptor, isEmpty);
expect(flutterManifest.fonts, isEmpty);
expect(flutterManifest.assets, isEmpty);
});
testWithoutContext('FlutterManifest has no fonts or assets when the "flutter" section is empty', () async {
const String manifest = '''
name: test
dependencies:
flutter:
sdk: flutter
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
expect(flutterManifest, isNotNull);
expect(flutterManifest.isEmpty, false);
expect(flutterManifest.appName, 'test');
expect(flutterManifest.usesMaterialDesign, false);
expect(flutterManifest.fontsDescriptor, isEmpty);
expect(flutterManifest.fonts, isEmpty);
expect(flutterManifest.assets, isEmpty);
});
test('knows if material design is used', () async {
const String manifest = '''
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
expect(flutterManifest, isNotNull);
expect(flutterManifest.isEmpty, false);
expect(flutterManifest.appName, 'test');
expect(flutterManifest.usesMaterialDesign, false);
expect(flutterManifest.fontsDescriptor, isEmpty);
expect(flutterManifest.fonts, isEmpty);
expect(flutterManifest.assets, isEmpty);
});
testWithoutContext('FlutterManifest knows if material design is used', () async {
const String manifest = '''
name: test
dependencies:
flutter:
......@@ -57,12 +63,17 @@ dependencies:
flutter:
uses-material-design: true
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
expect(flutterManifest.usesMaterialDesign, true);
});
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
test('has two assets', () async {
const String manifest = '''
expect(flutterManifest.usesMaterialDesign, true);
});
testWithoutContext('FlutterManifest has two assets', () async {
const String manifest = '''
name: test
dependencies:
flutter:
......@@ -73,14 +84,20 @@ flutter:
- a/foo
- a/bar
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
expect(flutterManifest.assets.length, 2);
expect(flutterManifest.assets[0], Uri.parse('a/foo'));
expect(flutterManifest.assets[1], Uri.parse('a/bar'));
});
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
test('has one font family with one asset', () async {
const String manifest = '''
expect(flutterManifest.assets, <Uri>[
Uri.parse('a/foo'),
Uri.parse('a/bar'),
]);
});
testWithoutContext('FlutterManifest has one font family with one asset', () async {
const String manifest = '''
name: test
dependencies:
flutter:
......@@ -92,24 +109,29 @@ flutter:
fonts:
- asset: a/bar
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
final dynamic expectedFontsDescriptor = [{'fonts': [{'asset': 'a/bar'}], 'family': 'foo'}]; // ignore: always_specify_types
expect(flutterManifest.fontsDescriptor, expectedFontsDescriptor);
final List<Font> fonts = flutterManifest.fonts;
expect(fonts.length, 1);
final Font font = fonts[0];
final dynamic fooFontDescriptor = {'family': 'foo', 'fonts': [{'asset': 'a/bar'}]}; // ignore: always_specify_types
expect(font.descriptor, fooFontDescriptor);
expect(font.familyName, 'foo');
final List<FontAsset> assets = font.fontAssets;
expect(assets.length, 1);
final FontAsset fontAsset = assets[0];
expect(fontAsset.assetUri.path, 'a/bar');
expect(fontAsset.weight, isNull);
expect(fontAsset.style, isNull);
});
test('has one font family with a simple asset and one with weight', () async {
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
expect(flutterManifest.fonts, hasLength(1));
expect(flutterManifest.fonts.single, matchesFont(
familyName: 'foo',
descriptor: <String, Object>{
'family': 'foo',
'fonts': <Object>[
<String, Object>{'asset': 'a/bar'},
],
},
fontAssets: <Matcher>[
matchesFontAsset(assetUri: Uri.parse('a/bar')),
],
));
});
testWithoutContext('FlutterManifest has one font family with a simple asset '
'and one with weight', () async {
const String manifest = '''
name: test
dependencies:
......@@ -124,29 +146,32 @@ flutter:
- asset: a/bar
weight: 400
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
final dynamic expectedFontsDescriptor = [{'fonts': [{'asset': 'a/bar'}, {'weight': 400, 'asset': 'a/bar'}], 'family': 'foo'}]; // ignore: always_specify_types
expect(flutterManifest.fontsDescriptor, expectedFontsDescriptor);
final List<Font> fonts = flutterManifest.fonts;
expect(fonts.length, 1);
final Font font = fonts[0];
final dynamic fooFontDescriptor = {'family': 'foo', 'fonts': [{'asset': 'a/bar'}, {'weight': 400, 'asset': 'a/bar'}]}; // ignore: always_specify_types
expect(font.descriptor, fooFontDescriptor);
expect(font.familyName, 'foo');
final List<FontAsset> assets = font.fontAssets;
expect(assets.length, 2);
final FontAsset fontAsset0 = assets[0];
expect(fontAsset0.assetUri.path, 'a/bar');
expect(fontAsset0.weight, isNull);
expect(fontAsset0.style, isNull);
final FontAsset fontAsset1 = assets[1];
expect(fontAsset1.assetUri.path, 'a/bar');
expect(fontAsset1.weight, 400);
expect(fontAsset1.style, isNull);
});
test('has one font family with a simple asset and one with weight and style', () async {
const String manifest = '''
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
expect(flutterManifest.fonts, hasLength(1));
expect(flutterManifest.fonts.single, matchesFont(
familyName: 'foo',
descriptor: <String, Object>{
'family': 'foo',
'fonts': <Object>[
<String, Object>{'asset': 'a/bar'},
<String, Object>{'weight': 400, 'asset': 'a/bar'},
],
},
fontAssets: <Matcher>[
matchesFontAsset(assetUri: Uri.parse('a/bar')),
matchesFontAsset(assetUri: Uri.parse('a/bar'), weight: 400),
])
);
});
testWithoutContext('FlutterManifest has one font family with a simple asset '
'and one with weight and style', () {
const String manifest = '''
name: test
dependencies:
flutter:
......@@ -161,30 +186,32 @@ flutter:
weight: 400
style: italic
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
final dynamic expectedFontsDescriptor = [{'fonts': [{'asset': 'a/bar'}, {'style': 'italic', 'weight': 400, 'asset': 'a/bar'}], 'family': 'foo'}]; // ignore: always_specify_types
expect(flutterManifest.fontsDescriptor, expectedFontsDescriptor);
final List<Font> fonts = flutterManifest.fonts;
expect(fonts.length, 1);
final Font font = fonts[0];
final dynamic fooFontDescriptor = {'family': 'foo', 'fonts': [{'asset': 'a/bar'}, {'weight': 400, 'style': 'italic', 'asset': 'a/bar'}]}; // ignore: always_specify_types
expect(font.descriptor, fooFontDescriptor);
expect(font.familyName, 'foo');
final List<FontAsset> assets = font.fontAssets;
expect(assets.length, 2);
final FontAsset fontAsset0 = assets[0];
expect(fontAsset0.assetUri.path, 'a/bar');
expect(fontAsset0.weight, isNull);
expect(fontAsset0.style, isNull);
final FontAsset fontAsset1 = assets[1];
expect(fontAsset1.assetUri.path, 'a/bar');
expect(fontAsset1.weight, 400);
expect(fontAsset1.style, 'italic');
});
test('has two font families, each with one simple asset and one with weight and style', () async {
const String manifest = '''
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
expect(flutterManifest.fonts, hasLength(1));
expect(flutterManifest.fonts.single, matchesFont(
familyName: 'foo',
descriptor: <String, Object>{
'family': 'foo',
'fonts': <Object>[
<String, Object>{'asset': 'a/bar'},
<String, Object>{'weight': 400, 'style': 'italic', 'asset': 'a/bar'},
],
},
fontAssets: <Matcher>[
matchesFontAsset(assetUri: Uri.parse('a/bar')),
matchesFontAsset(assetUri: Uri.parse('a/bar'), weight: 400, style: 'italic'),
],
));
});
testWithoutContext('FlutterManifest has two font families, each with one '
'simple asset and one with weight and style', () {
const String manifest = '''
name: test
dependencies:
flutter:
......@@ -205,48 +232,95 @@ flutter:
asset: a/baz
style: italic
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
final dynamic expectedFontsDescriptor = <dynamic>[
{'fonts': [{'asset': 'a/bar'}, {'style': 'italic', 'weight': 400, 'asset': 'a/bar'}], 'family': 'foo'}, // ignore: always_specify_types
{'fonts': [{'asset': 'a/baz'}, {'style': 'italic', 'weight': 400, 'asset': 'a/baz'}], 'family': 'bar'}, // ignore: always_specify_types
];
expect(flutterManifest.fontsDescriptor, expectedFontsDescriptor);
final List<Font> fonts = flutterManifest.fonts;
expect(fonts.length, 2);
final Font fooFont = fonts[0];
final dynamic fooFontDescriptor = {'family': 'foo', 'fonts': [{'asset': 'a/bar'}, {'weight': 400, 'style': 'italic', 'asset': 'a/bar'}]}; // ignore: always_specify_types
expect(fooFont.descriptor, fooFontDescriptor);
expect(fooFont.familyName, 'foo');
final List<FontAsset> fooAassets = fooFont.fontAssets;
expect(fooAassets.length, 2);
final FontAsset fooFontAsset0 = fooAassets[0];
expect(fooFontAsset0.assetUri.path, 'a/bar');
expect(fooFontAsset0.weight, isNull);
expect(fooFontAsset0.style, isNull);
final FontAsset fooFontAsset1 = fooAassets[1];
expect(fooFontAsset1.assetUri.path, 'a/bar');
expect(fooFontAsset1.weight, 400);
expect(fooFontAsset1.style, 'italic');
final Font barFont = fonts[1];
const String fontDescriptor = '{family: bar, fonts: [{asset: a/baz}, {weight: 400, style: italic, asset: a/baz}]}'; // ignore: always_specify_types
expect(barFont.descriptor.toString(), fontDescriptor);
expect(barFont.familyName, 'bar');
final List<FontAsset> barAssets = barFont.fontAssets;
expect(barAssets.length, 2);
final FontAsset barFontAsset0 = barAssets[0];
expect(barFontAsset0.assetUri.path, 'a/baz');
expect(barFontAsset0.weight, isNull);
expect(barFontAsset0.style, isNull);
final FontAsset barFontAsset1 = barAssets[1];
expect(barFontAsset1.assetUri.path, 'a/baz');
expect(barFontAsset1.weight, 400);
expect(barFontAsset1.style, 'italic');
});
testUsingContext('has only one of two font families when one declaration is missing the "family" option', () async {
const String manifest = '''
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
expect(flutterManifest.fonts, hasLength(2));
expect(flutterManifest.fonts, containsAll(<Matcher>[
matchesFont(
familyName: 'foo',
descriptor: <String, Object>{
'family': 'foo',
'fonts': <Object>[
<String, Object>{'asset': 'a/bar'},
<String, Object>{'weight': 400, 'style': 'italic', 'asset': 'a/bar'},
],
},
fontAssets: <Matcher>[
matchesFontAsset(assetUri: Uri.parse('a/bar')),
matchesFontAsset(assetUri: Uri.parse('a/bar'), weight: 400, style: 'italic'),
],
),
matchesFont(
familyName: 'bar',
descriptor: <String, Object>{
'family': 'bar',
'fonts': <Object>[
<String, Object>{'asset': 'a/baz'},
<String, Object>{'weight': 400, 'style': 'italic', 'asset': 'a/baz'},
],
},
fontAssets: <Matcher>[
matchesFontAsset(assetUri: Uri.parse('a/baz')),
matchesFontAsset(assetUri: Uri.parse('a/baz'), weight: 400, style: 'italic'),
],
),
]));
});
testWithoutContext('FlutterManifest.fontsDescriptor combines descriptors from '
'individual fonts', () {
const String manifest = '''
name: test
dependencies:
flutter:
sdk: flutter
flutter:
uses-material-design: true
fonts:
- family: foo
fonts:
- asset: a/bar
- asset: a/bar
weight: 400
style: italic
- family: bar
fonts:
- asset: a/baz
- weight: 400
asset: a/baz
style: italic
''';
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
expect(flutterManifest.fontsDescriptor, <Object>[
<String, Object>{
'family': 'foo',
'fonts': <Object>[
<String, Object>{'asset': 'a/bar'},
<String, Object>{'weight': 400, 'style': 'italic', 'asset': 'a/bar'},
],
},
<String, Object>{
'family': 'bar',
'fonts': <Object>[
<String, Object>{'asset': 'a/baz'},
<String, Object>{'weight': 400, 'style': 'italic', 'asset': 'a/baz'},
],
},
]);
});
testWithoutContext('FlutterManifest has only one of two font families when '
'one declaration is missing the "family" option', () async {
const String manifest = '''
name: test
dependencies:
flutter:
......@@ -266,30 +340,34 @@ flutter:
weight: 400
style: italic
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
final dynamic expectedFontsDescriptor = [{'fonts': [{'asset': 'a/bar'}, {'style': 'italic', 'weight': 400, 'asset': 'a/bar'}], 'family': 'foo'}]; // ignore: always_specify_types
expect(flutterManifest.fontsDescriptor, expectedFontsDescriptor);
final List<Font> fonts = flutterManifest.fonts;
expect(fonts.length, 1);
final Font fooFont = fonts[0];
final dynamic fooFontDescriptor = {'family': 'foo', 'fonts': [{'asset': 'a/bar'}, {'weight': 400, 'style': 'italic', 'asset': 'a/bar'}]}; // ignore: always_specify_types
expect(fooFont.descriptor, fooFontDescriptor);
expect(fooFont.familyName, 'foo');
final List<FontAsset> fooAassets = fooFont.fontAssets;
expect(fooAassets.length, 2);
final FontAsset fooFontAsset0 = fooAassets[0];
expect(fooFontAsset0.assetUri.path, 'a/bar');
expect(fooFontAsset0.weight, isNull);
expect(fooFontAsset0.style, isNull);
final FontAsset fooFontAsset1 = fooAassets[1];
expect(fooFontAsset1.assetUri.path, 'a/bar');
expect(fooFontAsset1.weight, 400);
expect(fooFontAsset1.style, 'italic');
});
testUsingContext('has only one of two font families when one declaration is missing the "fonts" option', () async {
const String manifest = '''
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
expect(flutterManifest.fonts, hasLength(1));
expect(flutterManifest.fonts, containsAll(<Matcher>[
matchesFont(
familyName: 'foo',
descriptor: <String, Object>{
'family': 'foo',
'fonts': <Object>[
<String, Object>{'asset': 'a/bar'},
<String, Object>{'weight': 400, 'style': 'italic', 'asset': 'a/bar'},
],
},
fontAssets: <Matcher>[
matchesFontAsset(assetUri: Uri.parse('a/bar')),
matchesFontAsset(assetUri: Uri.parse('a/bar'), weight: 400, style: 'italic'),
],
),
]));
});
testWithoutContext('FlutterManifest has only one of two font families when '
'one declaration is missing the "fonts" option', () async {
const String manifest = '''
name: test
dependencies:
flutter:
......@@ -305,29 +383,34 @@ flutter:
style: italic
- family: bar
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
final dynamic expectedFontsDescriptor = [{'fonts': [{'asset': 'a/bar'}, {'style': 'italic', 'weight': 400, 'asset': 'a/bar'}], 'family': 'foo'}]; // ignore: always_specify_types
expect(flutterManifest.fontsDescriptor, expectedFontsDescriptor);
final List<Font> fonts = flutterManifest.fonts;
expect(fonts.length, 1);
final Font fooFont = fonts[0];
final dynamic fooFontDescriptor = {'family': 'foo', 'fonts': [{'asset': 'a/bar'}, {'weight': 400, 'style': 'italic', 'asset': 'a/bar'}]}; // ignore: always_specify_types
expect(fooFont.descriptor, fooFontDescriptor);
expect(fooFont.familyName, 'foo');
final List<FontAsset> fooAassets = fooFont.fontAssets;
expect(fooAassets.length, 2);
final FontAsset fooFontAsset0 = fooAassets[0];
expect(fooFontAsset0.assetUri.path, 'a/bar');
expect(fooFontAsset0.weight, isNull);
expect(fooFontAsset0.style, isNull);
final FontAsset fooFontAsset1 = fooAassets[1];
expect(fooFontAsset1.assetUri.path, 'a/bar');
expect(fooFontAsset1.weight, 400);
expect(fooFontAsset1.style, 'italic');
});
testUsingContext('has no font family when declaration is missing the "asset" option', () async {
const String manifest = '''
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
expect(flutterManifest.fonts, hasLength(1));
expect(flutterManifest.fonts, containsAll(<Matcher>[
matchesFont(
familyName: 'foo',
descriptor: <String, Object>{
'family': 'foo',
'fonts': <Object>[
<String, Object>{'asset': 'a/bar'},
<String, Object>{'weight': 400, 'style': 'italic', 'asset': 'a/bar'},
],
},
fontAssets: <Matcher>[
matchesFontAsset(assetUri: Uri.parse('a/bar')),
matchesFontAsset(assetUri: Uri.parse('a/bar'), weight: 400, style: 'italic'),
],
),
]));
});
testWithoutContext('FlutterManifest has no font family when declaration is '
'missing the "asset" option', () async {
const String manifest = '''
name: test
dependencies:
flutter:
......@@ -339,57 +422,76 @@ flutter:
fonts:
- weight: 400
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
expect(flutterManifest.fontsDescriptor, <dynamic>[]);
final List<Font> fonts = flutterManifest.fonts;
expect(fonts.length, 0);
});
expect(flutterManifest.fontsDescriptor, isEmpty);
expect(flutterManifest.fonts, isEmpty);
});
test('allows a blank flutter section', () async {
const String manifest = '''
testWithoutContext('FlutterManifest allows a blank flutter section', () {
const String manifest = '''
name: test
dependencies:
flutter:
sdk: flutter
flutter:
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
expect(flutterManifest.isEmpty, false);
expect(flutterManifest.isModule, false);
expect(flutterManifest.isPlugin, false);
expect(flutterManifest.androidPackage, null);
expect(flutterManifest.usesAndroidX, false);
});
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
test('allows a module declaration', () async {
const String manifest = '''
expect(flutterManifest.isEmpty, false);
expect(flutterManifest.isModule, false);
expect(flutterManifest.isPlugin, false);
expect(flutterManifest.androidPackage, null);
expect(flutterManifest.usesAndroidX, false);
});
testWithoutContext('FlutterManifest allows a module declaration', () {
const String manifest = '''
name: test
flutter:
module:
androidPackage: com.example
androidX: true
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
expect(flutterManifest.isModule, true);
expect(flutterManifest.androidPackage, 'com.example');
expect(flutterManifest.usesAndroidX, true);
});
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
test('allows a legacy plugin declaration', () async {
const String manifest = '''
expect(flutterManifest.isModule, true);
expect(flutterManifest.androidPackage, 'com.example');
expect(flutterManifest.usesAndroidX, true);
});
testWithoutContext('FlutterManifest allows a legacy plugin declaration', () {
const String manifest = '''
name: test
flutter:
plugin:
androidPackage: com.example
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
expect(flutterManifest.isPlugin, true);
expect(flutterManifest.androidPackage, 'com.example');
});
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
test('allows a multi-plat plugin declaration with android only', () async {
const String manifest = '''
expect(flutterManifest.isPlugin, true);
expect(flutterManifest.androidPackage, 'com.example');
});
testWithoutContext('FlutterManifest allows a multi-plat plugin declaration '
'with android only', () {
const String manifest = '''
name: test
flutter:
plugin:
......@@ -398,13 +500,19 @@ flutter:
package: com.example
pluginClass: TestPlugin
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
expect(flutterManifest.isPlugin, true);
expect(flutterManifest.androidPackage, 'com.example');
});
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
test('allows a multi-plat plugin declaration with ios only', () async {
const String manifest = '''
expect(flutterManifest.isPlugin, true);
expect(flutterManifest.androidPackage, 'com.example');
});
testWithoutContext('FlutterManifest allows a multi-plat plugin declaration '
'with ios only', () {
const String manifest = '''
name: test
flutter:
plugin:
......@@ -412,37 +520,35 @@ flutter:
ios:
pluginClass: HelloPlugin
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
expect(flutterManifest.isPlugin, true);
expect(flutterManifest.androidPackage, isNull);
});
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
testUsingContext('handles an invalid plugin declaration', () async {
const String manifest = '''
expect(flutterManifest.isPlugin, true);
expect(flutterManifest.androidPackage, isNull);
});
testUsingContext('FlutterManifest handles an invalid plugin declaration', () {
const String manifest = '''
name: test
flutter:
plugin:
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
expect(flutterManifest, null);
expect(testLogger.errorText, contains('Expected "plugin" to be an object, but got null'));
});
Future<void> checkManifestVersion({
String manifest,
String expectedAppVersion,
String expectedBuildName,
String expectedBuildNumber,
}) async {
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
expect(flutterManifest.appVersion, expectedAppVersion);
expect(flutterManifest.buildName, expectedBuildName);
expect(flutterManifest.buildNumber, expectedBuildNumber);
}
test('parses major.minor.patch+build version clause 1', () async {
const String manifest = '''
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
expect(flutterManifest, null);
expect(logger.errorText,
contains('Expected "plugin" to be an object, but got null'));
});
testWithoutContext('FlutterManifest parses major.minor.patch+build version clause 1', () {
const String manifest = '''
name: test
version: 1.0.0+2
dependencies:
......@@ -450,16 +556,21 @@ dependencies:
sdk: flutter
flutter:
''';
await checkManifestVersion(
manifest: manifest,
expectedAppVersion: '1.0.0+2',
expectedBuildName: '1.0.0',
expectedBuildNumber: '2',
);
});
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
test('parses major.minor.patch with no build version', () async {
const String manifest = '''
expect(flutterManifest, matchesManifest(
appVersion: '1.0.0+2',
buildName: '1.0.0',
buildNumber: '2'),
);
});
testWithoutContext('FlutterManifest parses major.minor.patch with no build version', () {
const String manifest = '''
name: test
version: 0.0.1
dependencies:
......@@ -467,16 +578,21 @@ dependencies:
sdk: flutter
flutter:
''';
await checkManifestVersion(
manifest: manifest,
expectedAppVersion: '0.0.1',
expectedBuildName: '0.0.1',
expectedBuildNumber: null,
);
});
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
test('parses major.minor.patch+build version clause 2', () async {
const String manifest = '''
expect(flutterManifest, matchesManifest(
appVersion: '0.0.1',
buildName: '0.0.1',
buildNumber: null),
);
});
testWithoutContext('FlutterManifest parses major.minor.patch+build version clause 2', () {
const String manifest = '''
name: test
version: 1.0.0-beta+exp.sha.5114f85
dependencies:
......@@ -484,16 +600,21 @@ dependencies:
sdk: flutter
flutter:
''';
await checkManifestVersion(
manifest: manifest,
expectedAppVersion: '1.0.0-beta+exp.sha.5114f85',
expectedBuildName: '1.0.0-beta',
expectedBuildNumber: 'exp.sha.5114f85',
);
});
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
test('parses major.minor+build version clause', () async {
const String manifest = '''
expect(flutterManifest, matchesManifest(
appVersion: '1.0.0-beta+exp.sha.5114f85',
buildName: '1.0.0-beta',
buildNumber: 'exp.sha.5114f85'),
);
});
testWithoutContext('FlutterManifest parses major.minor+build version clause', () {
const String manifest = '''
name: test
version: 1.0+2
dependencies:
......@@ -501,16 +622,21 @@ dependencies:
sdk: flutter
flutter:
''';
await checkManifestVersion(
manifest: manifest,
expectedAppVersion: '1.0+2',
expectedBuildName: '1.0',
expectedBuildNumber: '2',
);
});
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
test('parses empty version clause', () async {
const String manifest = '''
expect(flutterManifest, matchesManifest(
appVersion: '1.0+2',
buildName: '1.0',
buildNumber: '2'),
);
});
testWithoutContext('FlutterManifest parses empty version clause', () {
const String manifest = '''
name: test
version:
dependencies:
......@@ -518,33 +644,43 @@ dependencies:
sdk: flutter
flutter:
''';
await checkManifestVersion(
manifest: manifest,
expectedAppVersion: null,
expectedBuildName: null,
expectedBuildNumber: null,
);
});
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
test('parses no version clause', () async {
const String manifest = '''
expect(flutterManifest, matchesManifest(
appVersion: null,
buildName: null,
buildNumber: null),
);
});
testWithoutContext('FlutterManifest parses no version clause', () {
const String manifest = '''
name: test
dependencies:
flutter:
sdk: flutter
flutter:
''';
await checkManifestVersion(
manifest: manifest,
expectedAppVersion: null,
expectedBuildName: null,
expectedBuildNumber: null,
);
});
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
expect(flutterManifest, matchesManifest(
appVersion: null,
buildName: null,
buildNumber: null),
);
});
// Regression test for https://github.com/flutter/flutter/issues/31764
testUsingContext('Returns proper error when font detail is malformed', () async {
const String manifest = '''
testWithoutContext('FlutterManifest returns proper error when font detail is malformed', () {
const String manifest = '''
name: test
dependencies:
flutter:
......@@ -555,14 +691,20 @@ flutter:
fonts:
-asset: a/bar
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
expect(flutterManifest, null);
expect(testLogger.errorText, contains('Expected "fonts" to either be null or a list.'));
});
expect(flutterManifest, null);
expect(logger.errorText,
contains('Expected "fonts" to either be null or a list.'));
});
testUsingContext('Returns proper error when font detail is not a list of maps', () async {
const String manifest = '''
testWithoutContext('FlutterManifest returns proper error when font detail is '
'not a list of maps', () {
const String manifest = '''
name: test
dependencies:
flutter:
......@@ -573,14 +715,20 @@ flutter:
fonts:
- asset
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
expect(flutterManifest, null);
expect(testLogger.errorText, contains('Expected "fonts" to be a list of maps.'));
});
expect(flutterManifest, null);
expect(logger.errorText,
contains('Expected "fonts" to be a list of maps.'));
});
testUsingContext('Returns proper error when font is a map instead of a list', () async {
const String manifest = '''
testWithoutContext('FlutterManifest returns proper error when font is a map '
'instead of a list', () {
const String manifest = '''
name: test
dependencies:
flutter:
......@@ -591,14 +739,19 @@ flutter:
fonts:
-asset: a/bar
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
expect(flutterManifest, null);
expect(testLogger.errorText, contains('Expected "fonts" to be a list'));
});
expect(flutterManifest, null);
expect(logger.errorText, contains('Expected "fonts" to be a list'));
});
testUsingContext('Returns proper error when second font family is invalid', () async {
const String manifest = '''
testWithoutContext('FlutterManifest returns proper error when second font '
'family is invalid', () {
const String manifest = '''
name: test
dependencies:
flutter:
......@@ -611,13 +764,18 @@ flutter:
- asset: a/bar
- string
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
expect(flutterManifest, null);
expect(testLogger.errorText, contains('Expected a map.'));
});
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
testUsingContext('Does not crash on empty entry', () async {
const String manifest = '''
expect(flutterManifest, null);
expect(logger.errorText, contains('Expected a map.'));
});
testWithoutContext('FlutterManifest does not crash on empty entry', () {
const String manifest = '''
name: test
dependencies:
flutter:
......@@ -628,15 +786,19 @@ flutter:
- lib/gallery/example_code.dart
-
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
final List<Uri> assets = flutterManifest.assets;
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
final List<Uri> assets = flutterManifest.assets;
expect(testLogger.errorText, contains('Asset manifest contains a null or empty uri.'));
expect(assets.length, 1);
});
expect(logger.errorText, contains('Asset manifest contains a null or empty uri.'));
expect(assets, hasLength(1));
});
testUsingContext('Special characters in asset URIs', () async {
const String manifest = '''
testWithoutContext('FlutterManifest handles special characters in asset URIs', () {
const String manifest = '''
name: test
dependencies:
flutter:
......@@ -648,17 +810,24 @@ flutter:
- lib/gallery/abc?xyz
- lib/gallery/aaa bbb
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
final List<Uri> assets = flutterManifest.assets;
expect(assets.length, 3);
expect(assets[0].path, 'lib/gallery/abc%23xyz');
expect(assets[1].path, 'lib/gallery/abc%3Fxyz');
expect(assets[2].path, 'lib/gallery/aaa%20bbb');
});
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
final List<Uri> assets = flutterManifest.assets;
expect(assets, hasLength(3));
expect(assets, <Uri>[
Uri.parse('lib/gallery/abc%23xyz'),
Uri.parse('lib/gallery/abc%3Fxyz'),
Uri.parse('lib/gallery/aaa%20bbb'),
]);
});
testUsingContext('Returns proper error when flutter is a list instead of a map', () async {
const String manifest = '''
testWithoutContext('FlutterManifest returns proper error when flutter is a '
'list instead of a map', () {
const String manifest = '''
name: test
dependencies:
flutter:
......@@ -666,64 +835,92 @@ dependencies:
flutter:
- uses-material-design: true
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
manifest,
logger: logger,
);
expect(flutterManifest, null);
expect(testLogger.errorText, contains('Expected "flutter" section to be an object or null, but got [{uses-material-design: true}].'));
});
expect(flutterManifest, null);
expect(logger.errorText,
contains(
'Expected "flutter" section to be an object or null, but got '
'[{uses-material-design: true}].',
),
);
});
group('FlutterManifest with MemoryFileSystem', () {
Future<void> assertSchemaIsReadable() async {
const String manifest = '''
testWithoutContext('FlutterManifest can parse manifest on posix filesystem', () {
const String manifest = '''
name: test
dependencies:
flutter:
sdk: flutter
flutter:
''';
final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
expect(flutterManifest.isEmpty, false);
}
void testUsingContextAndFs(
String description,
FileSystem filesystem,
dynamic testMethod(),
) {
testUsingContext(
description,
() async {
writeEmptySchemaFile(filesystem);
testMethod();
},
overrides: <Type, Generator>{
FileSystem: () => filesystem,
ProcessManager: () => FakeProcessManager.any(),
},
);
}
testUsingContext('Validate manifest on original fs', () {
assertSchemaIsReadable();
});
testUsingContextAndFs(
'Validate manifest on Posix FS',
MemoryFileSystem(style: FileSystemStyle.posix),
() {
assertSchemaIsReadable();
},
final FileSystem fileSystem = MemoryFileSystem.test();
fileSystem.file('pubspec.yaml').writeAsStringSync(manifest);
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromPath(
'pubspec.yaml',
fileSystem: fileSystem,
logger: logger,
);
testUsingContextAndFs(
'Validate manifest on Windows FS',
MemoryFileSystem(style: FileSystemStyle.windows),
() {
assertSchemaIsReadable();
},
expect(flutterManifest.isEmpty, false);
});
testWithoutContext('FlutterManifest can parse manifest on windows filesystem', () {
const String manifest = '''
name: test
dependencies:
flutter:
sdk: flutter
flutter:
''';
final FileSystem fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows);
fileSystem.file('pubspec.yaml').writeAsStringSync(manifest);
final BufferLogger logger = BufferLogger.test();
final FlutterManifest flutterManifest = FlutterManifest.createFromPath(
'pubspec.yaml',
fileSystem: fileSystem,
logger: logger,
);
expect(flutterManifest.isEmpty, false);
});
}
Matcher matchesManifest({
String appVersion,
String buildName,
String buildNumber,
}) {
return isA<FlutterManifest>()
.having((FlutterManifest manifest) => manifest.appVersion, 'appVersion', appVersion)
.having((FlutterManifest manifest) => manifest.buildName, 'buildName', buildName)
.having((FlutterManifest manifest) => manifest.buildNumber, 'buildNumber', buildNumber);
}
Matcher matchesFontAsset({
Uri assetUri,
int weight,
String style,
}) {
return isA<FontAsset>()
.having((FontAsset fontAsset) => fontAsset.assetUri, 'assetUri', assetUri)
.having((FontAsset fontAsset) => fontAsset.weight, 'weight', weight)
.having((FontAsset fontAsset) => fontAsset.style, 'style', style);
}
Matcher matchesFont({
Map<String, Object> descriptor,
String familyName,
List<Matcher> fontAssets,
}) {
return isA<Font>()
.having((Font font) => font.descriptor, 'descriptor', descriptor)
.having((Font font) => font.familyName, 'familyName', familyName)
.having((Font font) => font.fontAssets, 'fontAssets', containsAll(fontAssets));
}
......@@ -160,7 +160,10 @@ void main() {
fileSystem: fs,
logger: logger,
client: SuccessShortenURLFakeHttpClient(),
flutterProjectFactory: FlutterProjectFactory(),
flutterProjectFactory: FlutterProjectFactory(
fileSystem: fs,
logger: logger,
),
);
expect(
await creator.toolCrashIssueTemplateGitHubURL(command, error, stackTrace, doctorText),
......@@ -176,7 +179,10 @@ void main() {
fileSystem: fs,
logger: logger,
client: FakeHttpClient(),
flutterProjectFactory: FlutterProjectFactory(),
flutterProjectFactory: FlutterProjectFactory(
fileSystem: fs,
logger: logger,
),
);
expect(
await creator.toolCrashIssueTemplateGitHubURL(command, error, stackTrace, doctorText),
......@@ -199,7 +205,10 @@ void main() {
fileSystem: fs,
logger: logger,
client: FakeHttpClient(),
flutterProjectFactory: FlutterProjectFactory(),
flutterProjectFactory: FlutterProjectFactory(
fileSystem: fs,
logger: logger,
),
);
final Directory projectDirectory = fs.currentDirectory;
......
......@@ -8,6 +8,7 @@ import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/flutter_manifest.dart';
......@@ -23,6 +24,10 @@ import '../src/context.dart';
import '../src/testbed.dart';
void main() {
// TODO(jonahwilliams): remove once FlutterProject is fully refactored.
// this is safe since no tests have expectations on the test logger.
final BufferLogger logger = BufferLogger.test();
group('Project', () {
group('construction', () {
testInMemory('fails on null directory', () async {
......@@ -154,8 +159,8 @@ void main() {
testInMemory('does nothing, if project is not created', () async {
final FlutterProject project = FlutterProject(
globals.fs.directory('not_created'),
FlutterManifest.empty(),
FlutterManifest.empty(),
FlutterManifest.empty(logger: logger),
FlutterManifest.empty(logger: logger),
);
await project.ensureReadyForPlatformSpecificTooling();
expectNotExists(project.directory);
......@@ -197,7 +202,10 @@ void main() {
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
FlutterProjectFactory: () => FlutterProjectFactory(),
FlutterProjectFactory: () => FlutterProjectFactory(
logger: logger,
fileSystem: globals.fs,
),
});
testUsingContext('generates Xcode configuration for macOS', () async {
final FlutterProject project = await someProject();
......@@ -208,7 +216,10 @@ void main() {
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
FlutterProjectFactory: () => FlutterProjectFactory(),
FlutterProjectFactory: () => FlutterProjectFactory(
logger: logger,
fileSystem: globals.fs,
),
});
testUsingContext('injects plugins for Linux', () async {
final FlutterProject project = await someProject();
......@@ -220,7 +231,10 @@ void main() {
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
FlutterProjectFactory: () => FlutterProjectFactory(),
FlutterProjectFactory: () => FlutterProjectFactory(
logger: logger,
fileSystem: globals.fs,
),
});
testUsingContext('injects plugins for Windows', () async {
final FlutterProject project = await someProject();
......@@ -246,7 +260,10 @@ EndGlobal''');
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
FlutterProjectFactory: () => FlutterProjectFactory(),
FlutterProjectFactory: () => FlutterProjectFactory(
logger: logger,
fileSystem: globals.fs,
),
});
testInMemory('creates Android library in module', () async {
final FlutterProject project = await aModuleProject();
......@@ -312,7 +329,10 @@ EndGlobal''');
setUp(() {
fs = MemoryFileSystem();
mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
flutterProjectFactory = FlutterProjectFactory();
flutterProjectFactory = FlutterProjectFactory(
logger: logger,
fileSystem: fs,
);
});
testInMemory('default host app language', () async {
......@@ -348,7 +368,10 @@ apply plugin: 'kotlin-android'
fs = MemoryFileSystem();
mockPlistUtils = MockPlistUtils();
mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
flutterProjectFactory = FlutterProjectFactory();
flutterProjectFactory = FlutterProjectFactory(
fileSystem: fs,
logger: logger,
);
});
void testWithMocks(String description, Future<void> testMethod()) {
......@@ -521,8 +544,12 @@ apply plugin: 'kotlin-android'
FlutterProjectFactory flutterProjectFactory;
setUp(() {
testbed = Testbed();
flutterProjectFactory = FlutterProjectFactory();
testbed = Testbed(setup: () {
flutterProjectFactory = FlutterProjectFactory(
fileSystem: globals.fs,
logger: globals.logger,
);
});
});
test('Handles asking for builders from an invalid pubspec', () => testbed.run(() {
......@@ -646,7 +673,10 @@ void testInMemory(String description, Future<void> testMethod()) {
packagesFile.createSync(recursive: true);
packagesFile.writeAsStringSync('flutter_template_images:${dummyTemplateImagesDirectory.uri}');
final FlutterProjectFactory flutterProjectFactory = FlutterProjectFactory();
final FlutterProjectFactory flutterProjectFactory = FlutterProjectFactory(
fileSystem: testFileSystem,
logger: globals.logger ?? BufferLogger.test(),
);
testUsingContext(
description,
......
......@@ -4,6 +4,7 @@
import 'package:meta/meta.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/android/android_builder.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/build_info.dart';
......@@ -39,7 +40,11 @@ class FakeAndroidBuilder implements AndroidBuilder {
/// within [directoryOverride].
class FakeFlutterProjectFactory extends FlutterProjectFactory {
FakeFlutterProjectFactory(this.directoryOverride) :
assert(directoryOverride != null);
assert(directoryOverride != null),
super(
fileSystem: globals.fs,
logger: globals.logger,
);
final Directory directoryOverride;
......
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