mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
387 lines
13 KiB
Dart
387 lines
13 KiB
Dart
// 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.
|
|
|
|
// @dart = 2.8
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:file/memory.dart';
|
|
import 'package:flutter_tools/src/android/android_device.dart';
|
|
import 'package:flutter_tools/src/application_package.dart';
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
|
import 'package:flutter_tools/src/base/logger.dart';
|
|
import 'package:flutter_tools/src/build_info.dart';
|
|
import 'package:flutter_tools/src/commands/daemon.dart';
|
|
import 'package:flutter_tools/src/daemon.dart';
|
|
import 'package:flutter_tools/src/device.dart';
|
|
import 'package:flutter_tools/src/proxied_devices/devices.dart';
|
|
import 'package:flutter_tools/src/vmservice.dart';
|
|
import 'package:test/fake.dart';
|
|
|
|
import '../../src/common.dart';
|
|
import '../../src/context.dart';
|
|
import '../../src/fake_devices.dart';
|
|
|
|
void main() {
|
|
Daemon daemon;
|
|
NotifyingLogger notifyingLogger;
|
|
BufferLogger bufferLogger;
|
|
FakeAndroidDevice fakeDevice;
|
|
|
|
FakeApplicationPackageFactory applicationPackageFactory;
|
|
MemoryFileSystem memoryFileSystem;
|
|
FakeProcessManager fakeProcessManager;
|
|
|
|
group('ProxiedDevices', () {
|
|
DaemonConnection serverDaemonConnection;
|
|
DaemonConnection clientDaemonConnection;
|
|
setUp(() {
|
|
bufferLogger = BufferLogger.test();
|
|
notifyingLogger = NotifyingLogger(verbose: false, parent: bufferLogger);
|
|
final FakeDaemonStreams serverDaemonStreams = FakeDaemonStreams();
|
|
serverDaemonConnection = DaemonConnection(
|
|
daemonStreams: serverDaemonStreams,
|
|
logger: bufferLogger,
|
|
);
|
|
final FakeDaemonStreams clientDaemonStreams = FakeDaemonStreams();
|
|
clientDaemonConnection = DaemonConnection(
|
|
daemonStreams: clientDaemonStreams,
|
|
logger: bufferLogger,
|
|
);
|
|
|
|
serverDaemonStreams.inputs.addStream(clientDaemonStreams.outputs.stream);
|
|
clientDaemonStreams.inputs.addStream(serverDaemonStreams.outputs.stream);
|
|
|
|
applicationPackageFactory = FakeApplicationPackageFactory();
|
|
memoryFileSystem = MemoryFileSystem();
|
|
fakeProcessManager = FakeProcessManager.empty();
|
|
});
|
|
|
|
tearDown(() async {
|
|
if (daemon != null) {
|
|
return daemon.shutdown();
|
|
}
|
|
notifyingLogger.dispose();
|
|
await serverDaemonConnection.dispose();
|
|
await clientDaemonConnection.dispose();
|
|
});
|
|
|
|
testUsingContext('can list devices', () async {
|
|
daemon = Daemon(
|
|
serverDaemonConnection,
|
|
notifyingLogger: notifyingLogger,
|
|
);
|
|
fakeDevice = FakeAndroidDevice();
|
|
final FakePollingDeviceDiscovery discoverer = FakePollingDeviceDiscovery();
|
|
daemon.deviceDomain.addDeviceDiscoverer(discoverer);
|
|
discoverer.addDevice(fakeDevice);
|
|
|
|
final ProxiedDevices proxiedDevices = ProxiedDevices(clientDaemonConnection, logger: bufferLogger);
|
|
|
|
final List<Device> devices = await proxiedDevices.discoverDevices();
|
|
expect(devices, hasLength(1));
|
|
final Device device = devices[0];
|
|
expect(device.id, fakeDevice.id);
|
|
expect(device.name, 'Proxied ${fakeDevice.name}');
|
|
expect(await device.targetPlatform, await fakeDevice.targetPlatform);
|
|
expect(await device.isLocalEmulator, await fakeDevice.isLocalEmulator);
|
|
});
|
|
|
|
testUsingContext('calls supportsRuntimeMode', () async {
|
|
daemon = Daemon(
|
|
serverDaemonConnection,
|
|
notifyingLogger: notifyingLogger,
|
|
);
|
|
fakeDevice = FakeAndroidDevice();
|
|
final FakePollingDeviceDiscovery discoverer = FakePollingDeviceDiscovery();
|
|
daemon.deviceDomain.addDeviceDiscoverer(discoverer);
|
|
discoverer.addDevice(fakeDevice);
|
|
|
|
final ProxiedDevices proxiedDevices = ProxiedDevices(clientDaemonConnection, logger: bufferLogger);
|
|
|
|
final List<Device> devices = await proxiedDevices.devices;
|
|
expect(devices, hasLength(1));
|
|
final Device device = devices[0];
|
|
final bool supportsRuntimeMode = await device.supportsRuntimeMode(BuildMode.release);
|
|
expect(fakeDevice.supportsRuntimeModeCalledBuildMode, BuildMode.release);
|
|
expect(supportsRuntimeMode, true);
|
|
});
|
|
|
|
testUsingContext('redirects logs', () async {
|
|
daemon = Daemon(
|
|
serverDaemonConnection,
|
|
notifyingLogger: notifyingLogger,
|
|
);
|
|
fakeDevice = FakeAndroidDevice();
|
|
final FakePollingDeviceDiscovery discoverer = FakePollingDeviceDiscovery();
|
|
daemon.deviceDomain.addDeviceDiscoverer(discoverer);
|
|
discoverer.addDevice(fakeDevice);
|
|
|
|
final ProxiedDevices proxiedDevices = ProxiedDevices(clientDaemonConnection, logger: bufferLogger);
|
|
|
|
final FakeDeviceLogReader fakeLogReader = FakeDeviceLogReader();
|
|
fakeDevice.logReader = fakeLogReader;
|
|
|
|
final List<Device> devices = await proxiedDevices.devices;
|
|
expect(devices, hasLength(1));
|
|
final Device device = devices[0];
|
|
final DeviceLogReader logReader = await device.getLogReader();
|
|
fakeLogReader.logLinesController.add('Some log line');
|
|
|
|
final String receivedLogLine = await logReader.logLines.first;
|
|
expect(receivedLogLine, 'Some log line');
|
|
|
|
// Now try to stop the log reader
|
|
expect(fakeLogReader.disposeCalled, false);
|
|
logReader.dispose();
|
|
await pumpEventQueue();
|
|
expect(fakeLogReader.disposeCalled, true);
|
|
});
|
|
testUsingContext('starts and stops app', () async {
|
|
daemon = Daemon(
|
|
serverDaemonConnection,
|
|
notifyingLogger: notifyingLogger,
|
|
);
|
|
fakeDevice = FakeAndroidDevice();
|
|
final FakePollingDeviceDiscovery discoverer = FakePollingDeviceDiscovery();
|
|
daemon.deviceDomain.addDeviceDiscoverer(discoverer);
|
|
discoverer.addDevice(fakeDevice);
|
|
|
|
final ProxiedDevices proxiedDevices = ProxiedDevices(clientDaemonConnection, logger: bufferLogger);
|
|
final FakePrebuiltApplicationPackage prebuiltApplicationPackage = FakePrebuiltApplicationPackage();
|
|
final File dummyApplicationBinary = memoryFileSystem.file('/directory/dummy_file');
|
|
dummyApplicationBinary.parent.createSync();
|
|
dummyApplicationBinary.writeAsStringSync('dummy content');
|
|
prebuiltApplicationPackage.applicationPackage = dummyApplicationBinary;
|
|
|
|
final List<Device> devices = await proxiedDevices.devices;
|
|
expect(devices, hasLength(1));
|
|
final Device device = devices[0];
|
|
|
|
// Now try to start the app
|
|
final FakeApplicationPackage applicationPackage = FakeApplicationPackage();
|
|
applicationPackageFactory.applicationPackage = applicationPackage;
|
|
|
|
final Uri observatoryUri = Uri.parse('http://127.0.0.1:12345/observatory');
|
|
fakeDevice.launchResult = LaunchResult.succeeded(observatoryUri: observatoryUri);
|
|
|
|
final LaunchResult launchResult = await device.startApp(
|
|
prebuiltApplicationPackage,
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
);
|
|
|
|
expect(launchResult.started, true);
|
|
// The returned observatoryUri was a forwarded port, so we cannot compare them directly.
|
|
expect(launchResult.observatoryUri.path, observatoryUri.path);
|
|
|
|
expect(applicationPackageFactory.applicationBinaryRequested.readAsStringSync(), 'dummy content');
|
|
expect(applicationPackageFactory.platformRequested, TargetPlatform.android_arm);
|
|
|
|
expect(fakeDevice.startAppPackage, applicationPackage);
|
|
|
|
// Now try to stop the app
|
|
final bool stopAppResult = await device.stopApp(prebuiltApplicationPackage);
|
|
expect(fakeDevice.stopAppPackage, applicationPackage);
|
|
expect(stopAppResult, true);
|
|
}, overrides: <Type, Generator>{
|
|
ApplicationPackageFactory: () => applicationPackageFactory,
|
|
FileSystem: () => memoryFileSystem,
|
|
ProcessManager: () => fakeProcessManager,
|
|
});
|
|
|
|
testUsingContext('takes screenshot', () async {
|
|
daemon = Daemon(
|
|
serverDaemonConnection,
|
|
notifyingLogger: notifyingLogger,
|
|
);
|
|
fakeDevice = FakeAndroidDevice();
|
|
final FakePollingDeviceDiscovery discoverer = FakePollingDeviceDiscovery();
|
|
daemon.deviceDomain.addDeviceDiscoverer(discoverer);
|
|
discoverer.addDevice(fakeDevice);
|
|
|
|
final ProxiedDevices proxiedDevices = ProxiedDevices(clientDaemonConnection, logger: bufferLogger);
|
|
|
|
final List<Device> devices = await proxiedDevices.devices;
|
|
expect(devices, hasLength(1));
|
|
final Device device = devices[0];
|
|
|
|
final List<int> screenshot = <int>[1,2,3,4,5];
|
|
fakeDevice.screenshot = screenshot;
|
|
|
|
final File screenshotOutputFile = memoryFileSystem.file('screenshot_file');
|
|
await device.takeScreenshot(screenshotOutputFile);
|
|
|
|
expect(await screenshotOutputFile.readAsBytes(), screenshot);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => memoryFileSystem,
|
|
ProcessManager: () => fakeProcessManager,
|
|
});
|
|
});
|
|
}
|
|
|
|
class FakeDaemonStreams implements DaemonStreams {
|
|
final StreamController<DaemonMessage> inputs = StreamController<DaemonMessage>();
|
|
final StreamController<DaemonMessage> outputs = StreamController<DaemonMessage>();
|
|
|
|
@override
|
|
Stream<DaemonMessage> get inputStream {
|
|
return inputs.stream;
|
|
}
|
|
|
|
@override
|
|
void send(Map<String, dynamic> message, [ List<int> binary ]) {
|
|
outputs.add(DaemonMessage(message, binary != null ? Stream<List<int>>.value(binary) : null));
|
|
}
|
|
|
|
@override
|
|
Future<void> dispose() async {
|
|
await inputs.close();
|
|
// In some tests, outputs have no listeners. We don't wait for outputs to close.
|
|
unawaited(outputs.close());
|
|
}
|
|
}
|
|
|
|
// Unfortunately Device, despite not being immutable, has an `operator ==`.
|
|
// Until we fix that, we have to also ignore related lints here.
|
|
// ignore: avoid_implementing_value_types
|
|
class FakeAndroidDevice extends Fake implements AndroidDevice {
|
|
@override
|
|
final String id = 'device';
|
|
|
|
@override
|
|
final String name = 'device';
|
|
|
|
@override
|
|
Future<String> get emulatorId async => 'device';
|
|
|
|
@override
|
|
Future<TargetPlatform> get targetPlatform async => TargetPlatform.android_arm;
|
|
|
|
@override
|
|
Future<bool> get isLocalEmulator async => false;
|
|
|
|
@override
|
|
final Category category = Category.mobile;
|
|
|
|
@override
|
|
final PlatformType platformType = PlatformType.android;
|
|
|
|
@override
|
|
final bool ephemeral = false;
|
|
|
|
@override
|
|
Future<String> get sdkNameAndVersion async => 'Android 12';
|
|
|
|
@override
|
|
bool get supportsHotReload => true;
|
|
|
|
@override
|
|
bool get supportsHotRestart => true;
|
|
|
|
@override
|
|
bool get supportsScreenshot => true;
|
|
|
|
@override
|
|
bool get supportsFastStart => true;
|
|
|
|
@override
|
|
bool get supportsFlutterExit => true;
|
|
|
|
@override
|
|
Future<bool> get supportsHardwareRendering async => true;
|
|
|
|
@override
|
|
bool get supportsStartPaused => true;
|
|
|
|
BuildMode supportsRuntimeModeCalledBuildMode;
|
|
@override
|
|
Future<bool> supportsRuntimeMode(BuildMode buildMode) async {
|
|
supportsRuntimeModeCalledBuildMode = buildMode;
|
|
return true;
|
|
}
|
|
|
|
DeviceLogReader logReader;
|
|
@override
|
|
FutureOr<DeviceLogReader> getLogReader({
|
|
covariant ApplicationPackage app,
|
|
bool includePastLogs = false,
|
|
}) => logReader;
|
|
|
|
ApplicationPackage startAppPackage;
|
|
LaunchResult launchResult;
|
|
@override
|
|
Future<LaunchResult> startApp(
|
|
ApplicationPackage package, {
|
|
String mainPath,
|
|
String route,
|
|
DebuggingOptions debuggingOptions,
|
|
Map<String, Object> platformArgs = const <String, Object>{},
|
|
bool prebuiltApplication = false,
|
|
bool ipv6 = false,
|
|
String userIdentifier,
|
|
}) async {
|
|
startAppPackage = package;
|
|
return launchResult;
|
|
}
|
|
|
|
ApplicationPackage stopAppPackage;
|
|
@override
|
|
Future<bool> stopApp(
|
|
ApplicationPackage app, {
|
|
String userIdentifier,
|
|
}) async {
|
|
stopAppPackage = app;
|
|
return true;
|
|
}
|
|
|
|
List<int> screenshot;
|
|
@override
|
|
Future<void> takeScreenshot(File outputFile) {
|
|
return outputFile.writeAsBytes(screenshot);
|
|
}
|
|
}
|
|
|
|
class FakeDeviceLogReader implements DeviceLogReader {
|
|
final StreamController<String> logLinesController = StreamController<String>();
|
|
bool disposeCalled = false;
|
|
|
|
@override
|
|
int appPid;
|
|
|
|
@override
|
|
FlutterVmService connectedVMService;
|
|
|
|
@override
|
|
void dispose() {
|
|
disposeCalled = true;
|
|
}
|
|
|
|
@override
|
|
Stream<String> get logLines => logLinesController.stream;
|
|
|
|
@override
|
|
String get name => 'device';
|
|
|
|
}
|
|
|
|
class FakeApplicationPackageFactory implements ApplicationPackageFactory {
|
|
TargetPlatform platformRequested;
|
|
File applicationBinaryRequested;
|
|
ApplicationPackage applicationPackage;
|
|
|
|
@override
|
|
Future<ApplicationPackage> getPackageForPlatform(TargetPlatform platform, {BuildInfo buildInfo, File applicationBinary}) async {
|
|
platformRequested = platform;
|
|
applicationBinaryRequested = applicationBinary;
|
|
return applicationPackage;
|
|
}
|
|
}
|
|
|
|
class FakeApplicationPackage extends Fake implements ApplicationPackage {}
|
|
|
|
class FakePrebuiltApplicationPackage extends Fake implements PrebuiltApplicationPackage {
|
|
@override
|
|
File applicationPackage;
|
|
}
|