Unverified Commit f111a823 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] remove some globals from flutter_tester device (#60787)

Remove globals from flutter_tester device and cleanup test case. Not completely gone since the Kernel Builder will still use them, but a good incremental improvement.
parent 846418b6
...@@ -51,7 +51,7 @@ class ApplicationPackageFactory { ...@@ -51,7 +51,7 @@ class ApplicationPackageFactory {
? await IOSApp.fromIosProject(FlutterProject.current().ios, buildInfo) ? await IOSApp.fromIosProject(FlutterProject.current().ios, buildInfo)
: IOSApp.fromPrebuiltApp(applicationBinary); : IOSApp.fromPrebuiltApp(applicationBinary);
case TargetPlatform.tester: case TargetPlatform.tester:
return FlutterTesterApp.fromCurrentDirectory(); return FlutterTesterApp.fromCurrentDirectory(globals.fs);
case TargetPlatform.darwin_x64: case TargetPlatform.darwin_x64:
return applicationBinary == null return applicationBinary == null
? MacOSApp.fromMacOSProject(FlutterProject.current().macos) ? MacOSApp.fromMacOSProject(FlutterProject.current().macos)
......
...@@ -5,12 +5,13 @@ ...@@ -5,12 +5,13 @@
import 'dart:async'; import 'dart:async';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:process/process.dart';
import '../application_package.dart'; import '../application_package.dart';
import '../artifacts.dart'; import '../artifacts.dart';
import '../base/common.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/io.dart'; import '../base/io.dart';
import '../base/logger.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../bundle.dart'; import '../bundle.dart';
import '../convert.dart'; import '../convert.dart';
...@@ -21,8 +22,8 @@ import '../protocol_discovery.dart'; ...@@ -21,8 +22,8 @@ import '../protocol_discovery.dart';
import '../version.dart'; import '../version.dart';
class FlutterTesterApp extends ApplicationPackage { class FlutterTesterApp extends ApplicationPackage {
factory FlutterTesterApp.fromCurrentDirectory() { factory FlutterTesterApp.fromCurrentDirectory(FileSystem fileSystem) {
return FlutterTesterApp._(globals.fs.currentDirectory); return FlutterTesterApp._(fileSystem.currentDirectory);
} }
FlutterTesterApp._(Directory directory) FlutterTesterApp._(Directory directory)
...@@ -38,17 +39,42 @@ class FlutterTesterApp extends ApplicationPackage { ...@@ -38,17 +39,42 @@ class FlutterTesterApp extends ApplicationPackage {
File get packagesFile => _directory.childFile('.packages'); File get packagesFile => _directory.childFile('.packages');
} }
/// The device interface for running on the flutter_tester shell.
///
/// Normally this is only used as the runner for `flutter test`, but it can
/// also be used as a regular device when `--show-test-device` is provided
/// to the flutter command.
// TODO(scheglov): This device does not currently work with full restarts. // TODO(scheglov): This device does not currently work with full restarts.
class FlutterTesterDevice extends Device { class FlutterTesterDevice extends Device {
FlutterTesterDevice(String deviceId) : super( FlutterTesterDevice(String deviceId, {
@required ProcessManager processManager,
@required FlutterVersion flutterVersion,
@required Logger logger,
@required String buildDirectory,
@required FileSystem fileSystem,
@required Artifacts artifacts,
}) : _processManager = processManager,
_flutterVersion = flutterVersion,
_logger = logger,
_buildDirectory = buildDirectory,
_fileSystem = fileSystem,
_artifacts = artifacts,
super(
deviceId, deviceId,
platformType: null, platformType: null,
category: null, category: null,
ephemeral: false, ephemeral: false,
); );
final ProcessManager _processManager;
final FlutterVersion _flutterVersion;
final Logger _logger;
final String _buildDirectory;
final FileSystem _fileSystem;
final Artifacts _artifacts;
Process _process; Process _process;
final DevicePortForwarder _portForwarder = _NoopPortForwarder(); final DevicePortForwarder _portForwarder = const NoOpDevicePortForwarder();
@override @override
Future<bool> get isLocalEmulator async => false; Future<bool> get isLocalEmulator async => false;
...@@ -64,7 +90,7 @@ class FlutterTesterDevice extends Device { ...@@ -64,7 +90,7 @@ class FlutterTesterDevice extends Device {
@override @override
Future<String> get sdkNameAndVersion async { Future<String> get sdkNameAndVersion async {
final FlutterVersion flutterVersion = globals.flutterVersion; final FlutterVersion flutterVersion = _flutterVersion;
return 'Flutter ${flutterVersion.frameworkRevisionShort}'; return 'Flutter ${flutterVersion.frameworkRevisionShort}';
} }
...@@ -106,9 +132,6 @@ class FlutterTesterDevice extends Device { ...@@ -106,9 +132,6 @@ class FlutterTesterDevice extends Device {
@override @override
bool isSupported() => true; bool isSupported() => true;
bool _isRunning = false;
bool get isRunning => _isRunning;
@override @override
Future<LaunchResult> startApp( Future<LaunchResult> startApp(
ApplicationPackage package, { ApplicationPackage package, {
...@@ -121,42 +144,17 @@ class FlutterTesterDevice extends Device { ...@@ -121,42 +144,17 @@ class FlutterTesterDevice extends Device {
String userIdentifier, String userIdentifier,
}) async { }) async {
final BuildInfo buildInfo = debuggingOptions.buildInfo; final BuildInfo buildInfo = debuggingOptions.buildInfo;
if (!buildInfo.isDebug) { if (!buildInfo.isDebug) {
globals.printError('This device only supports debug mode.'); _logger.printError('This device only supports debug mode.');
return LaunchResult.failed(); return LaunchResult.failed();
} }
final String shellPath = globals.artifacts.getArtifactPath(Artifact.flutterTester); final String assetDirPath = _fileSystem.path.join(_buildDirectory, 'flutter_assets');
if (!globals.fs.isFileSync(shellPath)) {
throwToolExit('Cannot find Flutter shell at $shellPath');
}
final List<String> command = <String>[
shellPath,
'--run-forever',
'--non-interactive',
'--enable-dart-profiling',
'--packages=${debuggingOptions.buildInfo.packagesPath}',
];
if (debuggingOptions.debuggingEnabled) {
if (debuggingOptions.startPaused) {
command.add('--start-paused');
}
if (debuggingOptions.disableServiceAuthCodes) {
command.add('--disable-service-auth-codes');
}
if (debuggingOptions.hasObservatoryPort) {
command.add('--observatory-port=${debuggingOptions.hostVmServicePort}');
}
}
// Build assets and perform initial compilation.
final String assetDirPath = getAssetBuildDirectory();
final String applicationKernelFilePath = getKernelPathForTransformerOptions( final String applicationKernelFilePath = getKernelPathForTransformerOptions(
globals.fs.path.join(getBuildDirectory(), 'flutter-tester-app.dill'), _fileSystem.path.join(_buildDirectory, 'flutter-tester-app.dill'),
trackWidgetCreation: buildInfo.trackWidgetCreation, trackWidgetCreation: buildInfo.trackWidgetCreation,
); );
// Build assets and perform initial compilation.
await BundleBuilder().build( await BundleBuilder().build(
buildInfo: buildInfo, buildInfo: buildInfo,
mainPath: mainPath, mainPath: mainPath,
...@@ -167,34 +165,39 @@ class FlutterTesterDevice extends Device { ...@@ -167,34 +165,39 @@ class FlutterTesterDevice extends Device {
platform: getTargetPlatformForName(getNameForHostPlatform(getCurrentHostPlatform())), platform: getTargetPlatformForName(getNameForHostPlatform(getCurrentHostPlatform())),
treeShakeIcons: buildInfo.treeShakeIcons, treeShakeIcons: buildInfo.treeShakeIcons,
); );
command.add('--flutter-assets-dir=$assetDirPath');
command.add(applicationKernelFilePath); final List<String> command = <String>[
_artifacts.getArtifactPath(Artifact.flutterTester),
'--run-forever',
'--non-interactive',
'--enable-dart-profiling',
'--packages=${debuggingOptions.buildInfo.packagesPath}',
'--flutter-assets-dir=$assetDirPath',
if (debuggingOptions.startPaused)
'--start-paused',
if (debuggingOptions.disableServiceAuthCodes)
'--disable-service-auth-codes',
if (debuggingOptions.hasObservatoryPort)
'--observatory-port=${debuggingOptions.hostVmServicePort}',
applicationKernelFilePath
];
ProtocolDiscovery observatoryDiscovery; ProtocolDiscovery observatoryDiscovery;
try { try {
globals.printTrace(command.join(' ')); _logger.printTrace(command.join(' '));
_process = await _processManager.start(command,
_isRunning = true;
_process = await globals.processManager.start(command,
environment: <String, String>{ environment: <String, String>{
'FLUTTER_TEST': 'true', 'FLUTTER_TEST': 'true',
}, },
); );
// Setting a bool can't fail in the callback.
unawaited(_process.exitCode.then<void>((_) => _isRunning = false));
_process.stdout _process.stdout
.transform<String>(utf8.decoder) .transform<String>(utf8.decoder)
.transform<String>(const LineSplitter()) .transform<String>(const LineSplitter())
.listen((String line) { .listen(_logReader.addLine);
_logReader.addLine(line);
});
_process.stderr _process.stderr
.transform<String>(utf8.decoder) .transform<String>(utf8.decoder)
.transform<String>(const LineSplitter()) .transform<String>(const LineSplitter())
.listen((String line) { .listen(_logReader.addLine);
_logReader.addLine(line);
});
if (!debuggingOptions.debuggingEnabled) { if (!debuggingOptions.debuggingEnabled) {
return LaunchResult.succeeded(); return LaunchResult.succeeded();
...@@ -211,12 +214,12 @@ class FlutterTesterDevice extends Device { ...@@ -211,12 +214,12 @@ class FlutterTesterDevice extends Device {
if (observatoryUri != null) { if (observatoryUri != null) {
return LaunchResult.succeeded(observatoryUri: observatoryUri); return LaunchResult.succeeded(observatoryUri: observatoryUri);
} }
globals.printError( _logger.printError(
'Failed to launch $package: ' 'Failed to launch $package: '
'The log reader failed unexpectedly.', 'The log reader failed unexpectedly.',
); );
} on Exception catch (error) { } on Exception catch (error) {
globals.printError('Failed to launch $package: $error'); _logger.printError('Failed to launch $package: $error');
} finally { } finally {
await observatoryDiscovery?.cancel(); await observatoryDiscovery?.cancel();
} }
...@@ -256,8 +259,15 @@ class FlutterTesterDevices extends PollingDeviceDiscovery { ...@@ -256,8 +259,15 @@ class FlutterTesterDevices extends PollingDeviceDiscovery {
static bool showFlutterTesterDevice = false; static bool showFlutterTesterDevice = false;
final FlutterTesterDevice _testerDevice = final FlutterTesterDevice _testerDevice = FlutterTesterDevice(
FlutterTesterDevice(kTesterDeviceId); kTesterDeviceId,
fileSystem: globals.fs,
artifacts: globals.artifacts,
processManager: globals.processManager,
buildDirectory: getBuildDirectory(),
logger: globals.logger,
flutterVersion: globals.flutterVersion,
);
@override @override
bool get canListAnything => true; bool get canListAnything => true;
...@@ -289,24 +299,3 @@ class _FlutterTesterDeviceLogReader extends DeviceLogReader { ...@@ -289,24 +299,3 @@ class _FlutterTesterDeviceLogReader extends DeviceLogReader {
@override @override
void dispose() {} void dispose() {}
} }
/// A fake port forwarder that doesn't do anything. Used by flutter tester
/// where the VM is running on the same machine and does not need ports forwarding.
class _NoopPortForwarder extends DevicePortForwarder {
@override
Future<int> forward(int devicePort, { int hostPort }) {
if (hostPort != null && hostPort != devicePort) {
throw 'Forwarding to a different port is not supported by flutter tester';
}
return Future<int>.value(devicePort);
}
@override
List<ForwardedPort> get forwardedPorts => <ForwardedPort>[];
@override
Future<void> unforward(ForwardedPort forwardedPort) async { }
@override
Future<void> dispose() async { }
}
...@@ -7,6 +7,7 @@ import 'dart:async'; ...@@ -7,6 +7,7 @@ import 'dart:async';
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/build_system.dart';
...@@ -23,22 +24,18 @@ void main() { ...@@ -23,22 +24,18 @@ void main() {
MemoryFileSystem fileSystem; MemoryFileSystem fileSystem;
setUp(() { setUp(() {
fileSystem = MemoryFileSystem(); fileSystem = MemoryFileSystem.test();
}); });
group('FlutterTesterApp', () { testWithoutContext('FlutterTesterApp can be created from the current directory', () async {
testUsingContext('fromCurrentDirectory', () async {
const String projectPath = '/home/my/projects/my_project'; const String projectPath = '/home/my/projects/my_project';
await fileSystem.directory(projectPath).create(recursive: true); await fileSystem.directory(projectPath).create(recursive: true);
fileSystem.currentDirectory = projectPath; fileSystem.currentDirectory = projectPath;
final FlutterTesterApp app = FlutterTesterApp.fromCurrentDirectory(); final FlutterTesterApp app = FlutterTesterApp.fromCurrentDirectory(fileSystem);
expect(app.name, 'my_project'); expect(app.name, 'my_project');
expect(app.packagesFile.path, fileSystem.path.join(projectPath, '.packages')); expect(app.packagesFile.path, fileSystem.path.join(projectPath, '.packages'));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
}); });
group('FlutterTesterDevices', () { group('FlutterTesterDevices', () {
...@@ -74,41 +71,11 @@ void main() { ...@@ -74,41 +71,11 @@ void main() {
expect(devices, hasLength(1)); expect(devices, hasLength(1));
}); });
}); });
group('startApp', () {
group('FlutterTesterDevice', () {
FlutterTesterDevice device; FlutterTesterDevice device;
List<String> logLines; List<String> logLines;
setUp(() {
device = FlutterTesterDevice('flutter-tester');
logLines = <String>[];
device.getLogReader().logLines.listen(logLines.add);
});
testUsingContext('getters', () async {
expect(device.id, 'flutter-tester');
expect(await device.isLocalEmulator, isFalse);
expect(device.name, 'Flutter test device');
expect(device.portForwarder, isNot(isNull));
expect(await device.targetPlatform, TargetPlatform.tester);
expect(await device.installApp(null), isTrue);
expect(await device.isAppInstalled(null), isFalse);
expect(await device.isLatestBuildInstalled(null), isFalse);
expect(await device.uninstallApp(null), isTrue);
expect(device.isSupported(), isTrue);
});
group('startApp', () {
String flutterRoot;
String flutterTesterPath;
String projectPath;
String mainPath; String mainPath;
MockArtifacts mockArtifacts;
MockProcessManager mockProcessManager; MockProcessManager mockProcessManager;
MockProcess mockProcess; MockProcess mockProcess;
MockBuildSystem mockBuildSystem; MockBuildSystem mockBuildSystem;
...@@ -117,55 +84,71 @@ void main() { ...@@ -117,55 +84,71 @@ void main() {
Platform: () => FakePlatform(operatingSystem: 'linux'), Platform: () => FakePlatform(operatingSystem: 'linux'),
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
Artifacts: () => mockArtifacts, Artifacts: () => Artifacts.test(),
BuildSystem: () => mockBuildSystem, BuildSystem: () => mockBuildSystem,
}; };
setUp(() { setUp(() {
mockBuildSystem = MockBuildSystem(); mockBuildSystem = MockBuildSystem();
flutterRoot = fileSystem.path.join('home', 'me', 'flutter');
flutterTesterPath = fileSystem.path.join(flutterRoot, 'bin', 'cache',
'artifacts', 'engine', 'linux-x64', 'flutter_tester');
final File flutterTesterFile = fileSystem.file(flutterTesterPath);
flutterTesterFile.parent.createSync(recursive: true);
flutterTesterFile.writeAsBytesSync(const <int>[]);
projectPath = fileSystem.path.join('home', 'me', 'hello');
mainPath = fileSystem.path.join(projectPath, 'lin', 'main.dart');
mockProcessManager = MockProcessManager(); mockProcessManager = MockProcessManager();
mockProcessManager.processFactory = mockProcessManager.processFactory =
(List<String> commands) => mockProcess; (List<String> commands) => mockProcess;
mockArtifacts = MockArtifacts();
final String artifactPath = fileSystem.path.join(flutterRoot, 'artifact');
fileSystem.file(artifactPath).createSync(recursive: true);
when(mockArtifacts.getArtifactPath(
any,
mode: anyNamed('mode')
)).thenReturn(artifactPath);
when(mockArtifacts.isLocalEngine)
.thenReturn(false);
when(mockBuildSystem.build( when(mockBuildSystem.build(
any, any,
any, any,
)).thenAnswer((_) async { )).thenAnswer((_) async {
fileSystem.file('$mainPath.dill').createSync(recursive: true);
return BuildResult(success: true); return BuildResult(success: true);
}); });
device = FlutterTesterDevice('flutter-tester',
fileSystem: fileSystem,
processManager: mockProcessManager,
artifacts: Artifacts.test(),
buildDirectory: 'build',
logger: BufferLogger.test(),
flutterVersion: MockFlutterVersion(),
);
logLines = <String>[];
device.getLogReader().logLines.listen(logLines.add);
}); });
testUsingContext('not debug', () async { testWithoutContext('default settings', () async {
final LaunchResult result = await device.startApp(null, expect(device.id, 'flutter-tester');
expect(await device.isLocalEmulator, isFalse);
expect(device.name, 'Flutter test device');
expect(device.portForwarder, isNot(isNull));
expect(await device.targetPlatform, TargetPlatform.tester);
expect(await device.installApp(null), isTrue);
expect(await device.isAppInstalled(null), isFalse);
expect(await device.isLatestBuildInstalled(null), isFalse);
expect(await device.uninstallApp(null), isTrue);
expect(device.isSupported(), isTrue);
});
testWithoutContext('does not accept profile, release, or jit-release builds', () async {
final LaunchResult releaseResult = await device.startApp(null,
mainPath: mainPath, mainPath: mainPath,
debuggingOptions: DebuggingOptions.disabled(const BuildInfo(BuildMode.release, null, treeShakeIcons: false))); debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
);
final LaunchResult profileResult = await device.startApp(null,
mainPath: mainPath,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.profile),
);
final LaunchResult jitReleaseResult = await device.startApp(null,
mainPath: mainPath,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.jitRelease),
);
expect(result.started, isFalse); expect(releaseResult.started, isFalse);
}, overrides: startOverrides); expect(profileResult.started, isFalse);
expect(jitReleaseResult.started, isFalse);
});
testUsingContext('start', () async { testUsingContext('performs a build and starts in debug mode', () async {
final FlutterTesterApp app = FlutterTesterApp.fromCurrentDirectory(fileSystem);
final Uri observatoryUri = Uri.parse('http://127.0.0.1:6666/'); final Uri observatoryUri = Uri.parse('http://127.0.0.1:6666/');
mockProcess = MockProcess(stdout: Stream<List<int>>.fromIterable(<List<int>>[ mockProcess = MockProcess(stdout: Stream<List<int>>.fromIterable(<List<int>>[
''' '''
...@@ -175,16 +158,16 @@ Hello! ...@@ -175,16 +158,16 @@ Hello!
.codeUnits, .codeUnits,
])); ]));
final LaunchResult result = await device.startApp(null, final LaunchResult result = await device.startApp(app,
mainPath: mainPath, mainPath: mainPath,
debuggingOptions: DebuggingOptions.enabled(const BuildInfo(BuildMode.debug, null, treeShakeIcons: false))); debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug)
);
expect(result.started, isTrue); expect(result.started, isTrue);
expect(result.observatoryUri, observatoryUri); expect(result.observatoryUri, observatoryUri);
expect(logLines.last, 'Hello!'); expect(logLines.last, 'Hello!');
}, overrides: startOverrides); }, overrides: startOverrides);
}); });
});
} }
class MockBuildSystem extends Mock implements BuildSystem {} class MockBuildSystem extends Mock implements BuildSystem {}
class MockArtifacts extends Mock implements Artifacts {}
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