Unverified Commit 67a529b8 authored by Ben Konyi's avatar Ben Konyi Committed by GitHub

Added --dart-flags option to flutter run (#33924)

parent d3c864de
......@@ -474,6 +474,8 @@ class AndroidDevice extends Device {
cmd.addAll(<String>['--ez', 'start-paused', 'true']);
if (debuggingOptions.disableServiceAuthCodes)
cmd.addAll(<String>['--ez', 'disable-service-auth-codes', 'true']);
if (debuggingOptions.dartFlags.isNotEmpty)
cmd.addAll(<String>['--es', 'dart-flags', debuggingOptions.dartFlags]);
if (debuggingOptions.useTestFonts)
cmd.addAll(<String>['--ez', 'use-test-fonts', 'true']);
if (debuggingOptions.verboseSystemLogs) {
......
......@@ -4,6 +4,8 @@
import 'dart:async';
import 'package:args/command_runner.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/time.dart';
......@@ -130,6 +132,16 @@ class RunCommand extends RunCommandBase {
defaultsTo: true,
help: 'If necessary, build the app before running.',
)
..addOption('dart-flags',
hide: !verboseHelp,
help: 'Pass a list of comma separated flags to the Dart instance at '
'application startup. Flags passed through this option must be '
'present on the whitelist defined within the Flutter engine. If '
'a non-whitelisted flag is encountered, the process will be '
'terminated immediately.\n\n'
'This flag is not available on the stable channel and is only '
'applied in debug and profile modes. This option should only '
'be used for experiments and should not be used by typical users.')
..addOption('use-application-binary',
hide: !verboseHelp,
help: 'Specify a pre-built application binary to use when running.',
......@@ -294,6 +306,7 @@ class RunCommand extends RunCommandBase {
buildInfo,
startPaused: argResults['start-paused'],
disableServiceAuthCodes: argResults['disable-service-auth-codes'],
dartFlags: argResults['dart-flags'],
useTestFonts: argResults['use-test-fonts'],
enableSoftwareRendering: argResults['enable-software-rendering'],
skiaDeterministicRendering: argResults['skia-deterministic-rendering'],
......@@ -350,6 +363,11 @@ class RunCommand extends RunCommandBase {
);
}
if (argResults['dart-flags'] != null && FlutterVersion.instance.isStable) {
throw UsageException('--dart-flags is not available on the stable '
'channel.', null);
}
for (Device device in devices) {
if (await device.isLocalEmulator) {
if (await device.supportsHardwareRendering) {
......
......@@ -377,6 +377,7 @@ class DebuggingOptions {
this.buildInfo, {
this.startPaused = false,
this.disableServiceAuthCodes = false,
this.dartFlags = '',
this.enableSoftwareRendering = false,
this.skiaDeterministicRendering = false,
this.traceSkia = false,
......@@ -391,6 +392,7 @@ class DebuggingOptions {
: debuggingEnabled = false,
useTestFonts = false,
startPaused = false,
dartFlags = '',
disableServiceAuthCodes = false,
enableSoftwareRendering = false,
skiaDeterministicRendering = false,
......@@ -404,6 +406,7 @@ class DebuggingOptions {
final BuildInfo buildInfo;
final bool startPaused;
final String dartFlags;
final bool disableServiceAuthCodes;
final bool enableSoftwareRendering;
final bool skiaDeterministicRendering;
......
......@@ -283,6 +283,11 @@ class IOSDevice extends Device {
if (debuggingOptions.disableServiceAuthCodes)
launchArguments.add('--disable-service-auth-codes');
if (debuggingOptions.dartFlags.isNotEmpty) {
final String dartFlags = debuggingOptions.dartFlags;
launchArguments.add('--dart-flags="$dartFlags"');
}
if (debuggingOptions.useTestFonts)
launchArguments.add('--use-test-fonts');
......
......@@ -2,11 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:args/command_runner.dart';
import 'package:flutter_tools/src/application_package.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/run.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:flutter_tools/src/version.dart';
import 'package:mockito/mockito.dart';
import '../src/common.dart';
......@@ -14,9 +18,20 @@ import '../src/context.dart';
import '../src/mocks.dart';
void main() {
final MockDeviceManager mockDeviceManager = MockDeviceManager();
group('run', () {
MockApplicationPackageFactory mockApplicationPackageFactory;
MockDeviceManager mockDeviceManager;
MockFlutterVersion mockStableFlutterVersion;
MockFlutterVersion mockUnstableFlutterVersion;
setUpAll(() {
Cache.disableLocking();
mockApplicationPackageFactory = MockApplicationPackageFactory();
mockDeviceManager = MockDeviceManager();
mockStableFlutterVersion = MockFlutterVersion(isStable: true);
mockUnstableFlutterVersion = MockFlutterVersion(isStable: false);
});
testUsingContext('fails when target not found', () async {
final RunCommand command = RunCommand();
applyMocksToCommand(command);
......@@ -28,6 +43,75 @@ void main() {
}
});
testUsingContext('dart-flags option is not available on stable channel', () async {
when(mockDeviceManager.getDevices()).thenAnswer((Invocation invocation) {
return Stream<Device>.fromIterable(<Device>[
FakeDevice(),
]);
});
final RunCommand command = TestRunCommand();
final List<String> args = <String> [
'run',
'--dart-flags', '"--observe"',
'--no-hot',
];
// Stable branch.
try {
await createTestCommandRunner(command).run(args);
fail('Expect exception');
// ignore: unused_catch_clause
} on UsageException catch(e) {
// Not available while on stable branch.
}
}, overrides: <Type, Generator>{
DeviceManager: () => mockDeviceManager,
FlutterVersion: () => mockStableFlutterVersion,
});
testUsingContext('dart-flags option is only populated for debug and profile modes', () async {
when(mockDeviceManager.getDevices()).thenAnswer((Invocation invocation) {
return Stream<Device>.fromIterable(<Device>[
FakeDevice(),
]);
});
final RunCommand command = TestRunCommand();
final List<String> args = <String> [
'run',
'--dart-flags', '"--observe"',
'--no-hot',
];
// Debug mode
try {
await createTestCommandRunner(command).run(args);
fail('Expect exception');
} on ToolExit catch (e) {
expect(e.exitCode ?? 1, 1);
}
// Profile mode
try {
await createTestCommandRunner(command).run(<String>[...args, '--profile']);
fail('Expect exception');
} on ToolExit catch (e) {
expect(e.exitCode ?? 1, 1);
}
// Release mode
try {
await createTestCommandRunner(command).run(<String>[...args, '--release']);
fail('Expect exception');
} on ToolExit catch (e) {
expect(e.exitCode ?? 1, 1);
}
}, overrides: <Type, Generator>{
ApplicationPackageFactory: () => mockApplicationPackageFactory,
DeviceManager: () => mockDeviceManager,
FlutterVersion: () => mockUnstableFlutterVersion,
});
testUsingContext('should only request artifacts corresponding to connected devices', () async {
when(mockDeviceManager.getDevices()).thenAnswer((Invocation invocation) {
return Stream<Device>.fromIterable(<Device>[
......@@ -89,3 +173,65 @@ class MockDevice extends Mock implements Device {
@override
Future<TargetPlatform> get targetPlatform async => _targetPlatform;
}
class TestRunCommand extends RunCommand {
@override
// ignore: must_call_super
Future<void> validateCommand() async {
devices = await deviceManager.getDevices().toList();
}
}
class MockStableFlutterVersion extends MockFlutterVersion {
@override
bool get isStable => true;
}
class FakeDevice extends Fake implements Device {
final TargetPlatform _targetPlatform = TargetPlatform.ios;
void _throwToolExit(int code) => throwToolExit(null, exitCode: code);
@override
Future<bool> get isLocalEmulator => Future<bool>.value(false);
@override
bool get supportsHotReload => false;
@override
DeviceLogReader getLogReader({ ApplicationPackage app }) {
return MockDeviceLogReader();
}
@override
String get name => 'FakeDevice';
@override
Future<TargetPlatform> get targetPlatform async => _targetPlatform;
@override
Future<LaunchResult> startApp(
ApplicationPackage package, {
String mainPath,
String route,
DebuggingOptions debuggingOptions,
Map<String, dynamic> platformArgs,
bool prebuiltApplication = false,
bool usesTerminalUi = true,
bool ipv6 = false,
}) async {
final String dartFlags = debuggingOptions.dartFlags;
if (debuggingOptions.buildInfo.isRelease) {
if (dartFlags.isNotEmpty) {
_throwToolExit(-1);
}
_throwToolExit(1);
} else {
if (dartFlags.isEmpty) {
_throwToolExit(-1);
}
_throwToolExit(1);
}
return null;
}
}
......@@ -332,8 +332,12 @@ class MockXcodeProjectInterpreter implements XcodeProjectInterpreter {
}
class MockFlutterVersion extends Mock implements FlutterVersion {
MockFlutterVersion({bool isStable = false}) : _isStable = isStable;
final bool _isStable;
@override
bool get isStable => false;
bool get isStable => _isStable;
}
class MockClock extends Mock implements SystemClock {}
......
......@@ -37,6 +37,18 @@ class MockApplicationPackageStore extends ApplicationPackageStore {
);
}
class MockApplicationPackageFactory extends Mock implements ApplicationPackageFactory {
final MockApplicationPackageStore _store = MockApplicationPackageStore();
@override
Future<ApplicationPackage> getPackageForPlatform(
TargetPlatform platform, {
File applicationBinary,
}) async {
return _store.getPackageForPlatform(platform);
}
}
/// An SDK installation with several SDK levels (19, 22, 23).
class MockAndroidSdk extends Mock implements AndroidSdk {
static Directory createSdkDirectory({
......
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