Unverified Commit 55502fc3 authored by chunhtai's avatar chunhtai Committed by GitHub

Add vmservice for android build options (#123034)

https://github.com/flutter/flutter/issues/120408
parent 42fb0b23
......@@ -695,6 +695,28 @@ class FlutterPlugin implements Plugin<Project> {
}
}
// Add a task that can be called on flutter projects that prints the available build variants
// in gradle.
//
// This task prints variants in this format:
//
// BuildVariant: debug
// BuildVariant: release
// BuildVariant: profile
//
// Format of the output of this task is used by `AndroidProject.getBuildVariants`.
private static void addTaskForPrintBuildVariants(Project project) {
// Warning: The name of this task is used by `AndroidProject.getBuildVariants`.
project.tasks.register("printBuildVariants") {
description "Prints out all build variants for this Android project"
doLast {
project.android.applicationVariants.all { variant ->
println "BuildVariant: ${variant.name}";
}
}
}
}
/**
* Returns a Flutter build mode suitable for the specified Android buildType.
*
......@@ -867,7 +889,9 @@ class FlutterPlugin implements Plugin<Project> {
if (project.hasProperty('validate-deferred-components')) {
validateDeferredComponentsValue = project.property('validate-deferred-components').toBoolean()
}
addTaskForJavaVersion(project)
addTaskForPrintBuildVariants(project)
def targetPlatforms = getTargetPlatforms()
def addFlutterDeps = { variant ->
if (shouldSplitPerAbi()) {
......
......@@ -39,4 +39,7 @@ abstract class AndroidBuilder {
bool deferredComponentsEnabled = false,
bool configOnly = false,
});
/// Returns a list of available build variant from the Android project.
Future<List<String>> getBuildVariants({required FlutterProject project});
}
......@@ -40,7 +40,7 @@ class AndroidSdk {
reinitialize();
}
static const String _javaHomeEnvironmentVariable = 'JAVA_HOME';
static const String javaHomeEnvironmentVariable = 'JAVA_HOME';
static const String _javaExecutable = 'java';
/// The Android SDK root directory.
......@@ -462,10 +462,44 @@ class AndroidSdk {
return versionString.split('_').first;
}
/// Finds the java binary that is used for all operations across the tool.
/// A value that would be appropriate to use as JAVA_HOME.
///
/// First try Java bundled with Android Studio, then sniff JAVA_HOME, then fallback to PATH.
/// This method considers jdk in the following order:
/// * the JDK bundled with Android Studio, if one is found;
/// * the JAVA_HOME in the ambient environment, if set;
String? get javaHome {
return findJavaHome(
androidStudio: globals.androidStudio,
fileSystem: globals.fs,
operatingSystemUtils: globals.os,
platform: globals.platform,
);
}
static String? findJavaHome({
required AndroidStudio? androidStudio,
required FileSystem fileSystem,
required OperatingSystemUtils operatingSystemUtils,
required Platform platform,
}) {
if (androidStudio?.javaPath != null) {
globals.printTrace("Using Android Studio's java.");
return androidStudio!.javaPath!;
}
final String? javaHomeEnv = platform.environment[javaHomeEnvironmentVariable];
if (javaHomeEnv != null) {
globals.printTrace('Using JAVA_HOME from environment valuables.');
return javaHomeEnv;
}
return null;
}
/// Finds the java binary that is used for all operations across the tool.
///
/// This comes from [findJavaHome] if that method returns non-null;
/// otherwise, it gets from searching PATH.
// TODO(andrewkolos): To prevent confusion when debugging Android-related
// issues (see https://github.com/flutter/flutter/issues/122609 for an example),
// this logic should be consistently followed by any Java-dependent operation
......@@ -479,17 +513,15 @@ class AndroidSdk {
required OperatingSystemUtils operatingSystemUtils,
required Platform platform,
}) {
if (androidStudio?.javaPath != null) {
globals.printTrace("Using Android Studio's java.");
return fileSystem.path.join(androidStudio!.javaPath!, 'bin', 'java');
}
final String? javaHome = findJavaHome(
androidStudio: androidStudio,
fileSystem: fileSystem,
operatingSystemUtils: operatingSystemUtils,
platform: platform,
);
final String? javaHomeEnv =
platform.environment[_javaHomeEnvironmentVariable];
if (javaHomeEnv != null) {
// Trust JAVA_HOME.
globals.printTrace('Using JAVA_HOME.');
return fileSystem.path.join(javaHomeEnv, 'bin', 'java');
if (javaHome != null) {
return fileSystem.path.join(javaHome, 'bin', 'java');
}
// Fallback to PATH based lookup.
......@@ -528,8 +560,8 @@ class AndroidSdk {
);
if (javaBinary != null && globals.platform.environment['PATH'] != null) {
_sdkManagerEnv!['PATH'] = globals.fs.path.dirname(javaBinary) +
globals.os.pathVarSeparator +
globals.platform.environment['PATH']!;
globals.os.pathVarSeparator +
globals.platform.environment['PATH']!;
}
}
return _sdkManagerEnv!;
......
......@@ -26,15 +26,28 @@ import '../build_info.dart';
import '../cache.dart';
import '../convert.dart';
import '../flutter_manifest.dart';
import '../globals.dart' as globals;
import '../project.dart';
import '../reporting/reporting.dart';
import 'android_builder.dart';
import 'android_studio.dart';
import 'android_sdk.dart';
import 'gradle_errors.dart';
import 'gradle_utils.dart';
import 'migrations/top_level_gradle_build_file_migration.dart';
import 'multidex.dart';
/// The regex to grab variant names from printVariants gradle task
///
/// The task is defined in flutter/packages/flutter_tools/gradle/flutter.gradle.
///
/// The expected output from the task should be similar to:
///
/// BuildVariant: debug
/// BuildVariant: release
/// BuildVariant: profile
final RegExp _kBuildVariantRegex = RegExp('^BuildVariant: (?<$_kBuildVariantRegexGroupName>.*)\$');
const String _kBuildVariantRegexGroupName = 'variant';
/// The directory where the APK artifact is generated.
Directory getApkDirectory(FlutterProject project) {
return project.isModule
......@@ -397,13 +410,14 @@ class AndroidGradleBuilder implements AndroidBuilder {
..start();
int exitCode = 1;
try {
final String? javaHome = globals.androidSdk?.javaHome;
exitCode = await _processUtils.stream(
command,
workingDirectory: project.android.hostAppGradleRoot.path,
allowReentrantFlutter: true,
environment: <String, String>{
if (javaPath != null)
'JAVA_HOME': javaPath!,
if (javaHome != null)
AndroidSdk.javaHomeEnvironmentVariable: javaHome,
},
mapFunction: consumeLog,
);
......@@ -666,13 +680,14 @@ class AndroidGradleBuilder implements AndroidBuilder {
..start();
RunResult result;
try {
final String? javaHome = globals.androidSdk?.javaHome;
result = await _processUtils.run(
command,
workingDirectory: project.android.hostAppGradleRoot.path,
allowReentrantFlutter: true,
environment: <String, String>{
if (javaPath != null)
'JAVA_HOME': javaPath!,
if (javaHome != null)
AndroidSdk.javaHomeEnvironmentVariable: javaHome,
},
);
} finally {
......@@ -702,6 +717,51 @@ class AndroidGradleBuilder implements AndroidBuilder {
color: TerminalColor.green,
);
}
@override
Future<List<String>> getBuildVariants({required FlutterProject project}) async {
final Status status = _logger.startProgress(
"Running Gradle task 'printBuildVariants'...",
);
final List<String> command = <String>[
_gradleUtils.getExecutable(project),
'-q', // suppresses gradle output.
'printBuildVariants',
];
final Stopwatch sw = Stopwatch()
..start();
RunResult result;
try {
final String? javaHome = globals.androidSdk?.javaHome;
result = await _processUtils.run(
command,
workingDirectory: project.android.hostAppGradleRoot.path,
allowReentrantFlutter: true,
environment: <String, String>{
if (javaHome != null)
AndroidSdk.javaHomeEnvironmentVariable: javaHome,
},
);
} finally {
status.stop();
}
_usage.sendTiming('print', 'android build variants', sw.elapsed);
if (result.exitCode != 0) {
_logger.printStatus(result.stdout, wrap: false);
_logger.printError(result.stderr, wrap: false);
return const <String>[];
}
final List<String> options = <String>[];
for (final String line in LineSplitter.split(result.stdout)) {
final RegExpMatch? match = _kBuildVariantRegex.firstMatch(line);
if (match != null) {
options.add(match.namedGroup(_kBuildVariantRegexGroupName)!);
}
}
return options;
}
}
/// Prints how to consume the AAR from a host app.
......
......@@ -11,6 +11,7 @@ import '../base/terminal.dart';
import '../globals.dart' as globals;
import '../project.dart';
import '../reporting/reporting.dart';
import 'android_sdk.dart';
import 'android_studio.dart';
import 'gradle_utils.dart';
import 'multidex.dart';
......@@ -379,7 +380,7 @@ final GradleHandledError flavorUndefinedHandler = GradleHandledError(
workingDirectory: project.android.hostAppGradleRoot.path,
environment: <String, String>{
if (javaPath != null)
'JAVA_HOME': javaPath!,
AndroidSdk.javaHomeEnvironmentVariable: javaPath!,
},
);
// Extract build types and product flavors.
......
......@@ -7,6 +7,7 @@ import 'dart:async';
import 'package:meta/meta.dart';
import 'package:package_config/package_config.dart';
import 'android/android_sdk.dart';
import 'android/android_studio.dart';
import 'base/common.dart';
import 'base/error_handling_io.dart';
......@@ -425,7 +426,7 @@ class AndroidMavenArtifacts extends ArtifactSet {
],
environment: <String, String>{
if (javaPath != null)
'JAVA_HOME': javaPath!,
AndroidSdk.javaHomeEnvironmentVariable: javaPath!,
},
);
if (processResult.exitCode != 0) {
......
......@@ -7,6 +7,7 @@ import 'package:xml/xml.dart';
import 'package:yaml/yaml.dart';
import '../src/convert.dart';
import 'android/android_builder.dart';
import 'android/gradle_utils.dart' as gradle;
import 'base/common.dart';
import 'base/error_handling_io.dart';
......@@ -474,6 +475,13 @@ class AndroidProject extends FlutterProjectPlatform {
/// Returns true if the current version of the Gradle plugin is supported.
late final bool isSupportedVersion = _computeSupportedVersion();
Future<List<String>> getBuildVariants() async {
if (!existsSync() || androidBuilder == null) {
return const <String>[];
}
return androidBuilder!.getBuildVariants(project: parent);
}
bool _computeSupportedVersion() {
final FileSystem fileSystem = hostAppGradleRoot.fileSystem;
final File plugin = hostAppGradleRoot.childFile(
......
......@@ -31,6 +31,17 @@ const String kScreenshotMethod = '_flutter.screenshot';
const String kRenderFrameWithRasterStatsMethod = '_flutter.renderFrameWithRasterStats';
const String kReloadAssetFonts = '_flutter.reloadAssetFonts';
const String kFlutterToolAlias = 'Flutter Tools';
const String kReloadSourcesServiceName = 'reloadSources';
const String kHotRestartServiceName = 'hotRestart';
const String kFlutterVersionServiceName = 'flutterVersion';
const String kCompileExpressionServiceName = 'compileExpression';
const String kFlutterMemoryInfoServiceName = 'flutterMemoryInfo';
const String kFlutterGetSkSLServiceName = 'flutterGetSkSL';
const String kFlutterGetIOSBuildOptionsServiceName = 'flutterGetIOSBuildOptions';
const String kFlutterGetAndroidBuildVariantsServiceName = 'flutterGetAndroidBuildVariants';
/// The error response code from an unrecoverable compilation failure.
const int kIsolateReloadBarred = 1005;
......@@ -196,7 +207,7 @@ Future<vm_service.VmService> setUpVmService({
// all at the end of this method.
final List<Future<vm_service.Success?>> registrationRequests = <Future<vm_service.Success?>>[];
if (reloadSources != null) {
vmService.registerServiceCallback('reloadSources', (Map<String, Object?> params) async {
vmService.registerServiceCallback(kReloadSourcesServiceName, (Map<String, Object?> params) async {
final String isolateId = _validateRpcStringParam('reloadSources', params, 'isolateId');
final bool force = _validateRpcBoolParam('reloadSources', params, 'force');
final bool pause = _validateRpcBoolParam('reloadSources', params, 'pause');
......@@ -209,11 +220,11 @@ Future<vm_service.VmService> setUpVmService({
},
};
});
registrationRequests.add(vmService.registerService('reloadSources', 'Flutter Tools'));
registrationRequests.add(vmService.registerService(kReloadSourcesServiceName, kFlutterToolAlias));
}
if (restart != null) {
vmService.registerServiceCallback('hotRestart', (Map<String, Object?> params) async {
vmService.registerServiceCallback(kHotRestartServiceName, (Map<String, Object?> params) async {
final bool pause = _validateRpcBoolParam('compileExpression', params, 'pause');
await restart(pause: pause);
return <String, Object>{
......@@ -222,10 +233,10 @@ Future<vm_service.VmService> setUpVmService({
},
};
});
registrationRequests.add(vmService.registerService('hotRestart', 'Flutter Tools'));
registrationRequests.add(vmService.registerService(kHotRestartServiceName, kFlutterToolAlias));
}
vmService.registerServiceCallback('flutterVersion', (Map<String, Object?> params) async {
vmService.registerServiceCallback(kFlutterVersionServiceName, (Map<String, Object?> params) async {
final FlutterVersion version = context.get<FlutterVersion>() ?? FlutterVersion();
final Map<String, Object> versionJson = version.toJson();
versionJson['frameworkRevisionShort'] = version.frameworkRevisionShort;
......@@ -237,10 +248,10 @@ Future<vm_service.VmService> setUpVmService({
},
};
});
registrationRequests.add(vmService.registerService('flutterVersion', 'Flutter Tools'));
registrationRequests.add(vmService.registerService(kFlutterVersionServiceName, kFlutterToolAlias));
if (compileExpression != null) {
vmService.registerServiceCallback('compileExpression', (Map<String, Object?> params) async {
vmService.registerServiceCallback(kCompileExpressionServiceName, (Map<String, Object?> params) async {
final String isolateId = _validateRpcStringParam('compileExpression', params, 'isolateId');
final String expression = _validateRpcStringParam('compileExpression', params, 'expression');
final List<String> definitions = List<String>.from(params['definitions']! as List<Object?>);
......@@ -257,10 +268,10 @@ Future<vm_service.VmService> setUpVmService({
'result': <String, String>{'kernelBytes': kernelBytesBase64},
};
});
registrationRequests.add(vmService.registerService('compileExpression', 'Flutter Tools'));
registrationRequests.add(vmService.registerService(kCompileExpressionServiceName, kFlutterToolAlias));
}
if (device != null) {
vmService.registerServiceCallback('flutterMemoryInfo', (Map<String, Object?> params) async {
vmService.registerServiceCallback(kFlutterMemoryInfoServiceName, (Map<String, Object?> params) async {
final MemoryInfo result = await device.queryMemoryInfo();
return <String, Object>{
'result': <String, Object>{
......@@ -269,10 +280,10 @@ Future<vm_service.VmService> setUpVmService({
},
};
});
registrationRequests.add(vmService.registerService('flutterMemoryInfo', 'Flutter Tools'));
registrationRequests.add(vmService.registerService(kFlutterMemoryInfoServiceName, kFlutterToolAlias));
}
if (skSLMethod != null) {
vmService.registerServiceCallback('flutterGetSkSL', (Map<String, Object?> params) async {
vmService.registerServiceCallback(kFlutterGetSkSLServiceName, (Map<String, Object?> params) async {
final String? filename = await skSLMethod();
if (filename == null) {
return <String, Object>{
......@@ -288,11 +299,11 @@ Future<vm_service.VmService> setUpVmService({
},
};
});
registrationRequests.add(vmService.registerService('flutterGetSkSL', 'Flutter Tools'));
registrationRequests.add(vmService.registerService(kFlutterGetSkSLServiceName, kFlutterToolAlias));
}
if (flutterProject != null) {
vmService.registerServiceCallback('flutterGetIOSBuildOptions', (Map<String, Object?> params) async {
vmService.registerServiceCallback(kFlutterGetIOSBuildOptionsServiceName, (Map<String, Object?> params) async {
final XcodeProjectInfo? info = await flutterProject.ios.projectInfo();
if (info == null) {
return <String, Object>{
......@@ -311,7 +322,20 @@ Future<vm_service.VmService> setUpVmService({
};
});
registrationRequests.add(
vmService.registerService('flutterGetIOSBuildOptions', 'Flutter Tools'),
vmService.registerService(kFlutterGetIOSBuildOptionsServiceName, kFlutterToolAlias),
);
vmService.registerServiceCallback(kFlutterGetAndroidBuildVariantsServiceName, (Map<String, Object?> params) async {
final List<String> options = await flutterProject.android.getBuildVariants();
return <String, Object>{
'result': <String, Object>{
kResultType: kResultTypeSuccess,
'variants': options,
},
};
});
registrationRequests.add(
vmService.registerService(kFlutterGetAndroidBuildVariantsServiceName, kFlutterToolAlias),
);
}
......
......@@ -446,6 +446,9 @@ class FakeAndroidSdk extends Fake implements AndroidSdk {
@override
final Directory directory;
@override
String? get javaHome => 'java';
}
class FakeAndroidStudio extends Fake implements AndroidStudio {
......
......@@ -230,4 +230,7 @@ class FakeAndroidSdk extends Fake implements AndroidSdk {
@override
final Directory directory;
@override
String? get javaHome => 'java';
}
......@@ -787,6 +787,68 @@ android {
expect(androidApk?.id, 'com.example.foo');
});
testUsingContext('can call custom gradle task getBuildOptions and parse the result', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
artifacts: Artifacts.test(),
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
'gradlew',
'-q',
'printBuildVariants',
],
stdout: '''
BuildVariant: freeDebug
BuildVariant: paidDebug
BuildVariant: freeRelease
BuildVariant: paidRelease
BuildVariant: freeProfile
BuildVariant: paidProfile
''',
));
final List<String> actual = await builder.getBuildVariants(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
);
expect(actual, <String>['freeDebug', 'paidDebug', 'freeRelease', 'paidRelease', 'freeProfile', 'paidProfile']);
}, overrides: <Type, Generator>{
AndroidStudio: () => FakeAndroidStudio(),
});
testUsingContext('getBuildOptions returns empty list if gradle returns error', () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
artifacts: Artifacts.test(),
usage: testUsage,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
);
processManager.addCommand(const FakeCommand(
command: <String>[
'gradlew',
'-q',
'printBuildVariants',
],
stderr: '''
Gradle Crashed
''',
exitCode: 1,
));
final List<String> actual = await builder.getBuildVariants(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
);
expect(actual, const <String>[]);
}, overrides: <Type, Generator>{
AndroidStudio: () => FakeAndroidStudio(),
});
testUsingContext("doesn't indicate how to consume an AAR when printHowToConsumeAar is false", () async {
final AndroidGradleBuilder builder = AndroidGradleBuilder(
logger: logger,
......
......@@ -387,7 +387,7 @@ void main() {
ProcessManager: () => FakeProcessManager.any(),
OperatingSystemUtils: () => FakeOperatingSystemUtilsWithJava(),
Platform: () => FakePlatform(environment: <String, String>{
'JAVA_HOME': 'java-home-path',
AndroidSdk.javaHomeEnvironmentVariable: 'java-home-path',
}),
Config: () => Config,
AndroidStudio: () => FakeAndroidStudioWithoutJdk(),
......
......@@ -553,7 +553,7 @@ Review licenses that have not been accepted (y/N)?
androidStudio: null,
fileSystem: fileSystem,
logger: logger,
platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me', 'JAVA_HOME': 'home/java'},
platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me', AndroidSdk.javaHomeEnvironmentVariable: 'home/java'},
processManager: processManager,
userMessages: UserMessages(),
).validate();
......@@ -576,7 +576,7 @@ Review licenses that have not been accepted (y/N)?
androidStudio: null,
fileSystem: fileSystem,
logger: logger,
platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me', 'JAVA_HOME': 'home/java'},
platform: FakePlatform()..environment = <String, String>{'HOME': '/home/me', AndroidSdk.javaHomeEnvironmentVariable: 'home/java'},
processManager: processManager,
userMessages: UserMessages(),
).validate();
......
......@@ -589,7 +589,7 @@ void main() {
})!.toJson(),
),
const FakeVmServiceRequest(
method: 'reloadSources',
method: kReloadSourcesServiceName,
args: <String, Object>{
'isolateId': '1',
'pause': false,
......@@ -649,7 +649,7 @@ void main() {
})!.toJson(),
),
const FakeVmServiceRequest(
method: 'reloadSources',
method: kReloadSourcesServiceName,
args: <String, Object>{
'isolateId': '1',
'pause': false,
......@@ -713,7 +713,7 @@ void main() {
})!.toJson(),
),
const FakeVmServiceRequest(
method: 'reloadSources',
method: kReloadSourcesServiceName,
args: <String, Object>{
'isolateId': '1',
'pause': false,
......@@ -776,7 +776,7 @@ void main() {
jsonResponse: fakeVM.toJson(),
),
const FakeVmServiceRequest(
method: 'reloadSources',
method: kReloadSourcesServiceName,
args: <String, Object>{
'isolateId': '1',
'pause': false,
......@@ -866,7 +866,7 @@ void main() {
jsonResponse: fakeVM.toJson(),
),
const FakeVmServiceRequest(
method: 'reloadSources',
method: kReloadSourcesServiceName,
args: <String, Object>{
'isolateId': '1',
'pause': false,
......
......@@ -63,20 +63,24 @@ const List<VmServiceExpectation> kAttachIsolateExpectations =
'streamId': 'Isolate',
}),
FakeVmServiceRequest(method: 'registerService', args: <String, Object>{
'service': 'reloadSources',
'alias': 'Flutter Tools',
'service': kReloadSourcesServiceName,
'alias': kFlutterToolAlias,
}),
FakeVmServiceRequest(method: 'registerService', args: <String, Object>{
'service': 'flutterVersion',
'alias': 'Flutter Tools',
'service': kFlutterVersionServiceName,
'alias': kFlutterToolAlias,
}),
FakeVmServiceRequest(method: 'registerService', args: <String, Object>{
'service': 'flutterMemoryInfo',
'alias': 'Flutter Tools',
'service': kFlutterMemoryInfoServiceName,
'alias': kFlutterToolAlias,
}),
FakeVmServiceRequest(method: 'registerService', args: <String, Object>{
'service': 'flutterGetIOSBuildOptions',
'alias': 'Flutter Tools',
'service': kFlutterGetIOSBuildOptionsServiceName,
'alias': kFlutterToolAlias,
}),
FakeVmServiceRequest(method: 'registerService', args: <String, Object>{
'service': kFlutterGetAndroidBuildVariantsServiceName,
'alias': kFlutterToolAlias,
}),
FakeVmServiceRequest(
method: 'streamListen',
......@@ -611,7 +615,7 @@ void main() {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'hotRestart',
method: kHotRestartServiceName,
jsonResponse: <String, Object>{
'type': 'Success',
}),
......@@ -684,7 +688,7 @@ void main() {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'hotRestart',
method: kHotRestartServiceName,
jsonResponse: <String, Object>{
'type': 'Success',
}),
......@@ -883,7 +887,7 @@ void main() {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'hotRestart',
method: kHotRestartServiceName,
jsonResponse: <String, Object>{
'type': 'Failed',
},
......@@ -910,7 +914,7 @@ void main() {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
...kAttachExpectations,
const FakeVmServiceRequest(
method: 'hotRestart',
method: kHotRestartServiceName,
// Failed response,
errorCode: RPCErrorCodes.kInternalError,
),
......
......@@ -70,7 +70,7 @@ void main() {
vmService: mockVMService,
);
expect(mockVMService.services, containsPair('reloadSources', 'Flutter Tools'));
expect(mockVMService.services, containsPair(kReloadSourcesServiceName, kFlutterToolAlias));
});
testWithoutContext('VM Service registers flutterMemoryInfo service', () async {
......@@ -82,20 +82,29 @@ void main() {
vmService: mockVMService,
);
expect(mockVMService.services, containsPair('flutterMemoryInfo', 'Flutter Tools'));
expect(mockVMService.services, containsPair(kFlutterMemoryInfoServiceName, kFlutterToolAlias));
});
testWithoutContext('VmService registers flutterGetIOSBuildOptions service', () async {
final MockVMService mockVMService = MockVMService();
final FlutterProject mockedVMService = MockFlutterProject(
mockedIos: MockIosProject(),
final FlutterProject mockedFlutterProject = MockFlutterProject();
await setUpVmService(
flutterProject: mockedFlutterProject,
vmService: mockVMService,
);
expect(mockVMService.services, containsPair(kFlutterGetIOSBuildOptionsServiceName, kFlutterToolAlias));
});
testWithoutContext('VmService registers flutterGetAndroidBuildVariants service', () async {
final MockVMService mockVMService = MockVMService();
final FlutterProject mockedFlutterProject = MockFlutterProject();
await setUpVmService(
flutterProject: mockedVMService,
flutterProject: mockedFlutterProject,
vmService: mockVMService,
);
expect(mockVMService.services, containsPair('flutterGetIOSBuildOptions', 'Flutter Tools'));
expect(mockVMService.services, containsPair(kFlutterGetAndroidBuildVariantsServiceName, kFlutterToolAlias));
});
testWithoutContext('VM Service registers flutterGetSkSL service', () async {
......@@ -105,7 +114,7 @@ void main() {
vmService: mockVMService,
);
expect(mockVMService.services, containsPair('flutterGetSkSL', 'Flutter Tools'));
expect(mockVMService.services, containsPair(kFlutterGetSkSLServiceName, kFlutterToolAlias));
});
testWithoutContext('VM Service throws tool exit on service registration failure.', () async {
......@@ -144,7 +153,7 @@ void main() {
vmService: mockVMService,
);
expect(mockVMService.services, containsPair('flutterVersion', 'Flutter Tools'));
expect(mockVMService.services, containsPair(kFlutterVersionServiceName, kFlutterToolAlias));
});
testUsingContext('VM Service prints messages for connection failures', () {
......@@ -287,14 +296,14 @@ void main() {
<String>['scheme1', 'scheme2'],
MockLogger(),
);
final FlutterProject mockedVMService = MockFlutterProject(
final FlutterProject mockedFlutterProject = MockFlutterProject(
mockedIos: MockIosProject(mockedInfo: expectedProjectInfo),
);
await setUpVmService(
flutterProject: mockedVMService,
flutterProject: mockedFlutterProject,
vmService: vmService
);
final vm_service.ServiceCallback cb = vmService.serviceCallBacks['flutterGetIOSBuildOptions']!;
final vm_service.ServiceCallback cb = vmService.serviceCallBacks[kFlutterGetIOSBuildOptionsServiceName]!;
final Map<String, dynamic> response = await cb(<String, dynamic>{});
final Map<String, dynamic> result = response['result']! as Map<String, dynamic>;
......@@ -304,16 +313,34 @@ void main() {
expect(result['schemes'], expectedProjectInfo.schemes);
});
testWithoutContext('VmService forward flutterGetAndroidBuildVariants request and response correctly', () async {
final MockVMService vmService = MockVMService();
final List<String> expectedOptions = <String>['debug', 'release', 'profile'];
final FlutterProject mockedFlutterProject = MockFlutterProject(
mockedAndroid: MockAndroidProject(mockedOptions: expectedOptions),
);
await setUpVmService(
flutterProject: mockedFlutterProject,
vmService: vmService
);
final vm_service.ServiceCallback cb = vmService.serviceCallBacks[kFlutterGetAndroidBuildVariantsServiceName]!;
final Map<String, dynamic> response = await cb(<String, dynamic>{});
final Map<String, dynamic> result = response['result']! as Map<String, dynamic>;
expect(result[kResultType], kResultTypeSuccess);
expect(result['variants'], expectedOptions);
});
testWithoutContext('VmService forward flutterGetIOSBuildOptions request and response correctly when no iOS project', () async {
final MockVMService vmService = MockVMService();
final FlutterProject mockedVMService = MockFlutterProject(
final FlutterProject mockedFlutterProject = MockFlutterProject(
mockedIos: MockIosProject(),
);
await setUpVmService(
flutterProject: mockedVMService,
flutterProject: mockedFlutterProject,
vmService: vmService
);
final vm_service.ServiceCallback cb = vmService.serviceCallBacks['flutterGetIOSBuildOptions']!;
final vm_service.ServiceCallback cb = vmService.serviceCallBacks[kFlutterGetIOSBuildOptionsServiceName]!;
final Map<String, dynamic> response = await cb(<String, dynamic>{});
final Map<String, dynamic> result = response['result']! as Map<String, dynamic>;
......@@ -912,11 +939,16 @@ void main() {
class MockFlutterProject extends Fake implements FlutterProject {
MockFlutterProject({
required IosProject mockedIos
}) : ios = mockedIos;
IosProject? mockedIos,
AndroidProject? mockedAndroid,
}) : ios = mockedIos ?? MockIosProject(),
android = mockedAndroid ?? MockAndroidProject();
@override
final IosProject ios;
@override
final AndroidProject android;
}
class MockIosProject extends Fake implements IosProject {
......@@ -928,6 +960,15 @@ class MockIosProject extends Fake implements IosProject {
Future<XcodeProjectInfo?> projectInfo() async => mockedInfo;
}
class MockAndroidProject extends Fake implements AndroidProject {
MockAndroidProject({this.mockedOptions = const <String>[]});
final List<String> mockedOptions;
@override
Future<List<String>> getBuildVariants() async => mockedOptions;
}
class MockLogger extends Fake implements Logger { }
class MockVMService extends Fake implements vm_service.VmService {
......
// 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 'dart:convert';
import 'package:collection/collection.dart';
import 'package:file/file.dart';
import 'package:flutter_tools/src/android/gradle_utils.dart'
show getGradlewFileName;
import 'package:flutter_tools/src/base/io.dart';
import '../src/common.dart';
import 'test_utils.dart';
void main() {
late Directory tempDir;
setUp(() async {
tempDir = createResolvedTempDirectorySync('run_test.');
});
tearDown(() async {
tryToDelete(tempDir);
});
testWithoutContext(
'gradle task exists named printBuildVariants that prints build variants', () async {
// Create a new flutter project.
final String flutterBin =
fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
ProcessResult result = await processManager.run(<String>[
flutterBin,
'create',
tempDir.path,
'--project-name=testapp',
], workingDirectory: tempDir.path);
expect(result.exitCode, 0);
// Ensure that gradle files exists from templates.
result = await processManager.run(<String>[
flutterBin,
'build',
'apk',
'--config-only',
], workingDirectory: tempDir.path);
expect(result.exitCode, 0);
final Directory androidApp = tempDir.childDirectory('android');
result = await processManager.run(<String>[
'.${platform.pathSeparator}${getGradlewFileName(platform)}',
...getLocalEngineArguments(),
'-q', // quiet output.
'printBuildVariants',
], workingDirectory: androidApp.path);
// Verify that gradlew has a javaVersion task.
expect(result.exitCode, 0);
// Verify the format is a number on its own line.
const List<String> expectedLines = <String>[
'BuildVariant: debug',
'BuildVariant: release',
'BuildVariant: profile',
];
final List<String> actualLines = LineSplitter.split(result.stdout.toString()).toList();
expect(const ListEquality<String>().equals(actualLines, expectedLines), isTrue);
});
}
......@@ -36,6 +36,9 @@ class FakeAndroidBuilder implements AndroidBuilder {
bool deferredComponentsEnabled = false,
bool configOnly = false,
}) async {}
@override
Future<List<String>> getBuildVariants({required FlutterProject project}) async => const <String>[];
}
/// Creates a [FlutterProject] in a directory named [flutter_project]
......
......@@ -6,6 +6,7 @@ import 'dart:async';
import 'package:file/file.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/vmservice.dart';
import 'package:vm_service/vm_service.dart';
import 'package:vm_service/vm_service_io.dart';
......@@ -96,7 +97,7 @@ Future<void> validateFlutterVersion(VmService client) async {
client.onEvent('Service'),
emitsThrough(predicate((Event e) {
if (e.kind == EventKind.kServiceRegistered &&
e.service == 'flutterVersion') {
e.service == kFlutterVersionServiceName) {
method = e.method;
return true;
}
......
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