Unverified Commit 5aad12ca authored by Jackson Gardner's avatar Jackson Gardner Committed by GitHub

Generate syntax for plugin registration that works both with and without null safety. (#109480)

parent 6e66f064
...@@ -567,6 +567,7 @@ const String _dartPluginRegistryTemplate = ''' ...@@ -567,6 +567,7 @@ const String _dartPluginRegistryTemplate = '''
// Generated file. Do not edit. // Generated file. Do not edit.
// //
// @dart = 2.13
// ignore_for_file: type=lint // ignore_for_file: type=lint
{{#methodChannelPlugins}} {{#methodChannelPlugins}}
......
...@@ -44,8 +44,8 @@ void main() { ...@@ -44,8 +44,8 @@ void main() {
testUsingContext('generated plugin registrant passes analysis', () async { testUsingContext('generated plugin registrant passes analysis', () async {
await _createProject(projectDir, <String>[]); await _createProject(projectDir, <String>[]);
// We need a dependency so the plugin registrant is not completely empty. // We need a dependency so the plugin registrant is not completely empty.
await _addDependency(projectDir, 'shared_preferences', await _editPubspecFile(projectDir, _addDependencyEditor('shared_preferences',
version: '^2.0.0'); version: '^2.0.0'));
// The plugin registrant is created on build... // The plugin registrant is created on build...
await _buildWebProject(projectDir); await _buildWebProject(projectDir);
...@@ -62,6 +62,7 @@ void main() { ...@@ -62,6 +62,7 @@ void main() {
// Ensure the contents match what we expect for a non-empty plugin registrant. // Ensure the contents match what we expect for a non-empty plugin registrant.
final String contents = registrant.readAsStringSync(); final String contents = registrant.readAsStringSync();
expect(contents, contains('// @dart = 2.13'));
expect(contents, contains("import 'package:shared_preferences_web/shared_preferences_web.dart';")); expect(contents, contains("import 'package:shared_preferences_web/shared_preferences_web.dart';"));
expect(contents, contains('void registerPlugins([final Registrar? pluginRegistrar]) {')); expect(contents, contains('void registerPlugins([final Registrar? pluginRegistrar]) {'));
expect(contents, contains('SharedPreferencesPlugin.registerWith(registrar);')); expect(contents, contains('SharedPreferencesPlugin.registerWith(registrar);'));
...@@ -77,6 +78,54 @@ void main() { ...@@ -77,6 +78,54 @@ void main() {
), ),
}); });
testUsingContext('generated plugin registrant passes analysis without null safety', () async {
await _createProject(projectDir, <String>[]);
// We need a dependency so the plugin registrant is not completely empty.
await _editPubspecFile(projectDir,
_composeEditors(<PubspecEditor>[
_addDependencyEditor('shared_preferences', version: '^2.0.0'),
// This turns null safety off
_setDartSDKVersionEditor('>=2.11.0 <3.0.0'),
]));
// The generated main.dart file has a bunch of stuff that is invalid without null safety, so
// replace it with a no-op dummy main file. We aren't testing it in this scenario anyway.
await _replaceMainFile(projectDir, 'void main() {}');
// The plugin registrant is created on build...
await _buildWebProject(projectDir);
// Find the web_plugin_registrant, now that it lives outside "lib":
final Directory buildDir = projectDir
.childDirectory('.dart_tool/flutter_build')
.listSync()
.firstWhere((FileSystemEntity entity) => entity is Directory) as Directory;
// Ensure the file exists, and passes analysis.
final File registrant = buildDir.childFile('web_plugin_registrant.dart');
expect(registrant, exists);
await _analyzeEntity(registrant);
// Ensure the contents match what we expect for a non-empty plugin registrant.
final String contents = registrant.readAsStringSync();
expect(contents, contains('// @dart = 2.13'));
expect(contents, contains("import 'package:shared_preferences_web/shared_preferences_web.dart';"));
expect(contents, contains('void registerPlugins([final Registrar? pluginRegistrar]) {'));
expect(contents, contains('SharedPreferencesPlugin.registerWith(registrar);'));
expect(contents, contains('registrar.registerMessageHandler();'));
}, overrides: <Type, Generator>{
Pub: () => Pub(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
usage: globals.flutterUsage,
botDetector: globals.botDetector,
platform: globals.platform,
),
});
testUsingContext('(no-op) generated plugin registrant passes analysis', () async { testUsingContext('(no-op) generated plugin registrant passes analysis', () async {
await _createProject(projectDir, <String>[]); await _createProject(projectDir, <String>[]);
// No dependencies on web plugins this time! // No dependencies on web plugins this time!
...@@ -110,8 +159,8 @@ void main() { ...@@ -110,8 +159,8 @@ void main() {
// See: https://github.com/dart-lang/dart-services/pull/874 // See: https://github.com/dart-lang/dart-services/pull/874
testUsingContext('generated plugin registrant for dartpad is created on pub get', () async { testUsingContext('generated plugin registrant for dartpad is created on pub get', () async {
await _createProject(projectDir, <String>[]); await _createProject(projectDir, <String>[]);
await _addDependency(projectDir, 'shared_preferences', await _editPubspecFile(projectDir,
version: '^2.0.0'); _addDependencyEditor('shared_preferences', version: '^2.0.0'));
// The plugin registrant for dartpad is created on flutter pub get. // The plugin registrant for dartpad is created on flutter pub get.
await _doFlutterPubGet(projectDir); await _doFlutterPubGet(projectDir);
...@@ -154,10 +203,12 @@ void main() { ...@@ -154,10 +203,12 @@ void main() {
// With the above lint rule added, we want to ensure that the `generated_plugin_registrant.dart` // With the above lint rule added, we want to ensure that the `generated_plugin_registrant.dart`
// file does not fail analysis (this is a regression test - an ignore was // file does not fail analysis (this is a regression test - an ignore was
// added to cover this case). // added to cover this case).
await _addDependency( await _editPubspecFile(
projectDir, projectDir,
'test_web_plugin_with_a_purposefully_extremely_long_package_name', _addDependencyEditor(
path: '../test_plugin', 'test_web_plugin_with_a_purposefully_extremely_long_package_name',
path: '../test_plugin',
)
); );
// The plugin registrant is only created after a build... // The plugin registrant is only created after a build...
await _buildWebProject(projectDir); await _buildWebProject(projectDir);
...@@ -255,32 +306,76 @@ Future<void> _createProject(Directory dir, List<String> createArgs) async { ...@@ -255,32 +306,76 @@ Future<void> _createProject(Directory dir, List<String> createArgs) async {
]); ]);
} }
Future<void> _addDependency( typedef PubspecEditor = void Function(List<String> pubSpecContents);
Future<void> _editPubspecFile(
Directory projectDir, Directory projectDir,
String package, { PubspecEditor editor,
String? version, ) async {
String? path, final File pubspecYaml = projectDir.childFile('pubspec.yaml');
}) async { expect(pubspecYaml, exists);
final List<String> lines = await pubspecYaml.readAsLines();
editor(lines);
await pubspecYaml.writeAsString(lines.join('\n'));
}
Future<void> _replaceMainFile(Directory projectDir, String fileContents) async {
final File mainFile = projectDir.childDirectory('lib').childFile('main.dart');
await mainFile.writeAsString(fileContents);
}
PubspecEditor _addDependencyEditor(String packageToAdd, {String? version, String? path}) {
assert(version != null || path != null, assert(version != null || path != null,
'Need to define a source for the package.'); 'Need to define a source for the package.');
assert(version == null || path == null, assert(version == null || path == null,
'Cannot only load a package from path or from Pub, not both.'); 'Cannot only load a package from path or from Pub, not both.');
void editor(List<String> lines) {
for (int i = 0; i < lines.length; i++) {
final String line = lines[i];
if (line.startsWith('dependencies:')) {
lines.insert(
i + 1,
' $packageToAdd: ${version ?? '\n'
' path: $path'}');
break;
}
}
}
return editor;
}
final File pubspecYaml = projectDir.childFile('pubspec.yaml'); PubspecEditor _setDartSDKVersionEditor(String version) {
expect(pubspecYaml, exists); void editor(List<String> lines) {
for (int i = 0; i < lines.length; i++) {
final String line = lines[i];
if (line.startsWith('environment:')) {
for (i++; i < lines.length; i++) {
final String innerLine = lines[i];
final String sdkLine = " sdk: '$version'";
if(innerLine.isNotEmpty && !innerLine.startsWith(' ')) {
lines.insert(i, sdkLine);
break;
}
if(innerLine.startsWith(' sdk:')) {
lines[i] = sdkLine;
break;
}
}
break;
}
}
}
return editor;
}
final List<String> lines = await pubspecYaml.readAsLines(); PubspecEditor _composeEditors(Iterable<PubspecEditor> editors) {
for (int i = 0; i < lines.length; i++) { void composedEditor(List<String> lines) {
final String line = lines[i]; for (final PubspecEditor editor in editors) {
if (line.startsWith('dependencies:')) { editor(lines);
lines.insert(
i + 1,
' $package: ${version ?? '\n'
' path: $path'}');
break;
} }
} }
await pubspecYaml.writeAsString(lines.join('\n')); return composedEditor;
} }
Future<void> _addAnalysisOptions( Future<void> _addAnalysisOptions(
......
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