mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Add ktlint test for generated files from templates (#167378)
Creates a new test in analyze.dart that will generate kotlin files from .kt.tmpl and .kts.tmpl files and then run ktlint on them. Fixes: #166497 ## Pre-launch Checklist - [X] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [X] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [X] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [X] I signed the [CLA]. - [X] I listed at least one issue that this PR fixes in the description above. - [X] I updated/added relevant documentation (doc comments with `///`). - [X] I added new tests to check the change I am making, or this PR is [test-exempt]. - [X] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [X] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
parent
66ecc88655
commit
c74cd04dbb
@ -167,6 +167,9 @@ Future<void> run(List<String> arguments) async {
|
||||
printProgress('Lint Kotlin files...');
|
||||
await lintKotlinFiles(flutterRoot);
|
||||
|
||||
printProgress('Lint generated Kotlin files from templates...');
|
||||
await lintKotlinTemplatedFiles(flutterRoot);
|
||||
|
||||
// Ensure that all package dependencies are in sync.
|
||||
printProgress('Package dependencies...');
|
||||
await runCommand(flutter, <String>[
|
||||
@ -2460,6 +2463,66 @@ Future<void> verifyTabooDocumentation(String workingDirectory, {int minimumMatch
|
||||
}
|
||||
}
|
||||
|
||||
final Map<String, String> _kKotlinTemplateKeys = <String, String>{
|
||||
'androidIdentifier': 'dummyPackage',
|
||||
'pluginClass': 'PluginClass',
|
||||
'projectName': 'dummy',
|
||||
'agpVersion': '0.0.0.1',
|
||||
'kotlinVersion': '0.0.0.1',
|
||||
};
|
||||
|
||||
final String _kKotlinTemplateRelativePath = path.join('packages', 'flutter_tools', 'templates');
|
||||
|
||||
const List<String> _kKotlinExtList = <String>['.kt.tmpl', '.kts.tmpl'];
|
||||
const String _kKotlinTmplExt = '.tmpl';
|
||||
final RegExp _kKotlinTemplatePattern = RegExp(r'{{(.*?)}}');
|
||||
|
||||
/// Copy kotlin template files from [_kKotlinTemplateRelativePath] into a system tmp folder
|
||||
/// then replace template values with values from [_kKotlinTemplateKeys] or "'dummy'" if an
|
||||
/// unknown key is found. Then run ktlint on the tmp folder to check for lint errors in the
|
||||
/// generated Kotlin files.
|
||||
Future<void> lintKotlinTemplatedFiles(String workingDirectory) async {
|
||||
final String templatePath = path.join(workingDirectory, _kKotlinTemplateRelativePath);
|
||||
final Iterable<File> files = Directory(templatePath)
|
||||
.listSync(recursive: true)
|
||||
.toList()
|
||||
.whereType<File>()
|
||||
.where((File file) => _kKotlinExtList.contains(path.extension(file.path, 2)));
|
||||
|
||||
if (files.isEmpty) {
|
||||
foundError(<String>['No Kotlin template files found']);
|
||||
return;
|
||||
}
|
||||
|
||||
final Directory tempDir = Directory.systemTemp.createTempSync('template_output');
|
||||
for (final File templateFile in files) {
|
||||
final String inputContent = await templateFile.readAsString();
|
||||
final String modifiedContent = inputContent.replaceAllMapped(
|
||||
_kKotlinTemplatePattern,
|
||||
(Match match) => _kKotlinTemplateKeys[match[1]] ?? 'dummy',
|
||||
);
|
||||
|
||||
String outputFilename = path.basename(templateFile.path);
|
||||
outputFilename = outputFilename.substring(
|
||||
0,
|
||||
outputFilename.length - _kKotlinTmplExt.length,
|
||||
); // Remove '.tmpl' from file path
|
||||
|
||||
// Ensure the first letter of the generated class is uppercase (instead of pluginClass)
|
||||
outputFilename = outputFilename.substring(0, 1).toUpperCase() + outputFilename.substring(1);
|
||||
|
||||
final String relativePath = path.dirname(path.relative(templateFile.path, from: templatePath));
|
||||
final String outputDir = path.join(tempDir.path, relativePath);
|
||||
await Directory(outputDir).create(recursive: true);
|
||||
final String outputFile = path.join(outputDir, outputFilename);
|
||||
final File output = File(outputFile);
|
||||
await output.writeAsString(modifiedContent);
|
||||
}
|
||||
return lintKotlinFiles(tempDir.path).whenComplete(() {
|
||||
tempDir.deleteSync(recursive: true);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> lintKotlinFiles(String workingDirectory) async {
|
||||
const String baselineRelativePath = 'dev/bots/test/analyze-test-input/ktlint-baseline.xml';
|
||||
const String editorConfigRelativePath = 'dev/bots/test/analyze-test-input/.editorconfig';
|
||||
|
@ -5,7 +5,10 @@ allprojects {
|
||||
}
|
||||
}
|
||||
|
||||
val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
|
||||
val newBuildDir: Directory =
|
||||
rootProject.layout.buildDirectory
|
||||
.dir("../../build")
|
||||
.get()
|
||||
rootProject.layout.buildDirectory.value(newBuildDir)
|
||||
|
||||
subprojects {
|
||||
|
@ -5,7 +5,10 @@ allprojects {
|
||||
}
|
||||
}
|
||||
|
||||
val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
|
||||
val newBuildDir: Directory =
|
||||
rootProject.layout.buildDirectory
|
||||
.dir("../../build")
|
||||
.get()
|
||||
rootProject.layout.buildDirectory.value(newBuildDir)
|
||||
|
||||
subprojects {
|
||||
|
@ -1,11 +1,12 @@
|
||||
pluginManagement {
|
||||
val flutterSdkPath = run {
|
||||
val properties = java.util.Properties()
|
||||
file("local.properties").inputStream().use { properties.load(it) }
|
||||
val flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
|
||||
flutterSdkPath
|
||||
}
|
||||
val flutterSdkPath =
|
||||
run {
|
||||
val properties = java.util.Properties()
|
||||
file("local.properties").inputStream().use { properties.load(it) }
|
||||
val flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
|
||||
flutterSdkPath
|
||||
}
|
||||
|
||||
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||
|
||||
|
@ -7,27 +7,32 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||
import io.flutter.plugin.common.MethodChannel.Result
|
||||
|
||||
/** {{pluginClass}} */
|
||||
class {{pluginClass}}: FlutterPlugin, MethodCallHandler {
|
||||
/// The MethodChannel that will the communication between Flutter and native Android
|
||||
///
|
||||
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
|
||||
/// when the Flutter Engine is detached from the Activity
|
||||
private lateinit var channel : MethodChannel
|
||||
class {{pluginClass}} :
|
||||
FlutterPlugin,
|
||||
MethodCallHandler {
|
||||
// The MethodChannel that will the communication between Flutter and native Android
|
||||
//
|
||||
// This local reference serves to register the plugin with the Flutter Engine and unregister it
|
||||
// when the Flutter Engine is detached from the Activity
|
||||
private lateinit var channel: MethodChannel
|
||||
|
||||
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "{{projectName}}")
|
||||
channel.setMethodCallHandler(this)
|
||||
}
|
||||
|
||||
override fun onMethodCall(call: MethodCall, result: Result) {
|
||||
if (call.method == "getPlatformVersion") {
|
||||
result.success("Android ${android.os.Build.VERSION.RELEASE}")
|
||||
} else {
|
||||
result.notImplemented()
|
||||
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "{{projectName}}")
|
||||
channel.setMethodCallHandler(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel.setMethodCallHandler(null)
|
||||
}
|
||||
override fun onMethodCall(
|
||||
call: MethodCall,
|
||||
result: Result
|
||||
) {
|
||||
if (call.method == "getPlatformVersion") {
|
||||
result.success("Android ${android.os.Build.VERSION.RELEASE}")
|
||||
} else {
|
||||
result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel.setMethodCallHandler(null)
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ package {{androidIdentifier}}
|
||||
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import kotlin.test.Test
|
||||
import org.mockito.Mockito
|
||||
import kotlin.test.Test
|
||||
|
||||
/*
|
||||
* This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation.
|
||||
@ -14,14 +14,14 @@ import org.mockito.Mockito
|
||||
*/
|
||||
|
||||
internal class {{pluginClass}}Test {
|
||||
@Test
|
||||
fun onMethodCall_getPlatformVersion_returnsExpectedValue() {
|
||||
val plugin = {{pluginClass}}()
|
||||
@Test
|
||||
fun onMethodCall_getPlatformVersion_returnsExpectedValue() {
|
||||
val plugin = {{pluginClass}}()
|
||||
|
||||
val call = MethodCall("getPlatformVersion", null)
|
||||
val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java)
|
||||
plugin.onMethodCall(call, mockResult)
|
||||
val call = MethodCall("getPlatformVersion", null)
|
||||
val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java)
|
||||
plugin.onMethodCall(call, mockResult)
|
||||
|
||||
Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE)
|
||||
}
|
||||
Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user