flutter/packages/flutter_tools/test/general.shard/resident_runner_test.dart

267 lines
11 KiB
Dart
Raw Normal View History

// Copyright 2017 The Chromium 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:async';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/devfs.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/reporting/usage.dart';
import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/run_hot.dart';
import 'package:flutter_tools/src/vmservice.dart';
import 'package:json_rpc_2/json_rpc_2.dart';
import 'package:mockito/mockito.dart';
import '../src/common.dart';
import '../src/testbed.dart';
void main() {
group('ResidentRunner', () {
final Uri testUri = Uri.parse('foo://bar');
Testbed testbed;
MockFlutterDevice mockFlutterDevice;
MockVMService mockVMService;
MockDevFS mockDevFS;
MockFlutterView mockFlutterView;
ResidentRunner residentRunner;
MockDevice mockDevice;
MockIsolate mockIsolate;
setUp(() {
testbed = Testbed(setup: () {
residentRunner = HotRunner(
<FlutterDevice>[
mockFlutterDevice,
],
stayResident: false,
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
);
});
mockFlutterDevice = MockFlutterDevice();
mockDevice = MockDevice();
mockVMService = MockVMService();
mockDevFS = MockDevFS();
mockFlutterView = MockFlutterView();
mockIsolate = MockIsolate();
// DevFS Mocks
when(mockDevFS.lastCompiled).thenReturn(DateTime(2000));
when(mockDevFS.sources).thenReturn(<Uri>[]);
when(mockDevFS.destroy()).thenAnswer((Invocation invocation) async { });
// FlutterDevice Mocks.
when(mockFlutterDevice.updateDevFS(
// Intentionally provide empty list to match above mock.
invalidatedFiles: <Uri>[],
mainPath: anyNamed('mainPath'),
target: anyNamed('target'),
bundle: anyNamed('bundle'),
firstBuildTime: anyNamed('firstBuildTime'),
bundleFirstUpload: anyNamed('bundleFirstUpload'),
bundleDirty: anyNamed('bundleDirty'),
fullRestart: anyNamed('fullRestart'),
projectRootPath: anyNamed('projectRootPath'),
pathToReload: anyNamed('pathToReload'),
)).thenAnswer((Invocation invocation) async {
return UpdateFSReport(
success: true,
syncedBytes: 0,
invalidatedSourcesCount: 0,
);
});
when(mockFlutterDevice.devFS).thenReturn(mockDevFS);
when(mockFlutterDevice.views).thenReturn(<FlutterView>[
mockFlutterView
]);
when(mockFlutterDevice.device).thenReturn(mockDevice);
when(mockFlutterView.uiIsolate).thenReturn(mockIsolate);
when(mockFlutterDevice.stopEchoingDeviceLog()).thenAnswer((Invocation invocation) async { });
when(mockFlutterDevice.observatoryUris).thenReturn(<Uri>[
testUri,
]);
when(mockFlutterDevice.connect(
reloadSources: anyNamed('reloadSources'),
restart: anyNamed('restart'),
compileExpression: anyNamed('compileExpression')
)).thenAnswer((Invocation invocation) async { });
when(mockFlutterDevice.setupDevFS(any, any, packagesFilePath: anyNamed('packagesFilePath')))
.thenAnswer((Invocation invocation) async {
return testUri;
});
when(mockFlutterDevice.vmServices).thenReturn(<VMService>[
mockVMService,
]);
when(mockFlutterDevice.refreshViews()).thenAnswer((Invocation invocation) async { });
// VMService mocks.
when(mockVMService.wsAddress).thenReturn(testUri);
when(mockVMService.done).thenAnswer((Invocation invocation) {
final Completer<void> result = Completer<void>.sync();
return result.future;
});
when(mockIsolate.resume()).thenAnswer((Invocation invocation) {
return Future<Map<String, Object>>.value(null);
});
when(mockIsolate.flutterExit()).thenAnswer((Invocation invocation) {
return Future<Map<String, Object>>.value(null);
});
});
test('Can attach to device successfully', () => testbed.run(() async {
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync();
final Future<int> result = residentRunner.attach(
appStartedCompleter: onAppStart,
connectionInfoCompleter: onConnectionInfo,
);
final Future<DebugConnectionInfo> connectionInfo = onConnectionInfo.future;
expect(await result, 0);
verify(mockFlutterDevice.initLogReader()).called(1);
expect(onConnectionInfo.isCompleted, true);
expect((await connectionInfo).baseUri, 'foo://bar');
expect(onAppStart.isCompleted, true);
}));
test('Can handle an RPC exception from hot reload', () => testbed.run(() async {
when(mockDevice.sdkNameAndVersion).thenAnswer((Invocation invocation) async {
return 'Example';
});
when(mockDevice.targetPlatform).thenAnswer((Invocation invocation) async {
return TargetPlatform.android_arm;
});
when(mockDevice.isLocalEmulator).thenAnswer((Invocation invocation) async {
return false;
});
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync();
unawaited(residentRunner.attach(
appStartedCompleter: onAppStart,
connectionInfoCompleter: onConnectionInfo,
));
await onAppStart.future;
when(mockFlutterDevice.updateDevFS(
mainPath: anyNamed('mainPath'),
target: anyNamed('target'),
bundle: anyNamed('bundle'),
firstBuildTime: anyNamed('firstBuildTime'),
bundleFirstUpload: anyNamed('bundleFirstUpload'),
bundleDirty: anyNamed('bundleDirty'),
fullRestart: anyNamed('fullRestart'),
projectRootPath: anyNamed('projectRootPath'),
pathToReload: anyNamed('pathToReload'),
invalidatedFiles: anyNamed('invalidatedFiles'),
)).thenThrow(RpcException(666, 'something bad happened'));
final OperationResult result = await residentRunner.restart(fullRestart: false);
expect(result.fatal, true);
expect(result.code, 1);
verify(flutterUsage.sendEvent('hot', 'exception', parameters: <String, String>{
reloadExceptionTargetPlatform: getNameForTargetPlatform(TargetPlatform.android_arm),
reloadExceptionSdkName: 'Example',
reloadExceptionEmulator: 'false',
reloadExceptionFullRestart: 'false',
})).called(1);
}, overrides: <Type, Generator>{
Usage: () => MockUsage(),
}));
test('Can handle an RPC exception from hot restart', () => testbed.run(() async {
when(mockDevice.sdkNameAndVersion).thenAnswer((Invocation invocation) async {
return 'Example';
});
when(mockDevice.targetPlatform).thenAnswer((Invocation invocation) async {
return TargetPlatform.android_arm;
});
when(mockDevice.isLocalEmulator).thenAnswer((Invocation invocation) async {
return false;
});
when(mockDevice.supportsHotRestart).thenReturn(true);
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync();
unawaited(residentRunner.attach(
appStartedCompleter: onAppStart,
connectionInfoCompleter: onConnectionInfo,
));
await onAppStart.future;
when(mockFlutterDevice.updateDevFS(
mainPath: anyNamed('mainPath'),
target: anyNamed('target'),
bundle: anyNamed('bundle'),
firstBuildTime: anyNamed('firstBuildTime'),
bundleFirstUpload: anyNamed('bundleFirstUpload'),
bundleDirty: anyNamed('bundleDirty'),
fullRestart: anyNamed('fullRestart'),
projectRootPath: anyNamed('projectRootPath'),
pathToReload: anyNamed('pathToReload'),
invalidatedFiles: anyNamed('invalidatedFiles'),
)).thenThrow(RpcException(666, 'something bad happened'));
final OperationResult result = await residentRunner.restart(fullRestart: true);
expect(result.fatal, true);
expect(result.code, 1);
verify(flutterUsage.sendEvent('hot', 'exception', parameters: <String, String>{
reloadExceptionTargetPlatform: getNameForTargetPlatform(TargetPlatform.android_arm),
reloadExceptionSdkName: 'Example',
reloadExceptionEmulator: 'false',
reloadExceptionFullRestart: 'true',
})).called(1);
}, overrides: <Type, Generator>{
Usage: () => MockUsage(),
}));
group('FlutterDevice' , () {
test('Will not exit a paused isolate', () => testbed.run(() async {
final TestFlutterDevice flutterDevice = TestFlutterDevice(
mockDevice,
<FlutterView>[ mockFlutterView ],
);
final MockServiceEvent mockServiceEvent = MockServiceEvent();
when(mockServiceEvent.isPauseEvent).thenReturn(true);
when(mockIsolate.pauseEvent).thenReturn(mockServiceEvent);
when(mockDevice.supportsFlutterExit).thenReturn(true);
await flutterDevice.exitApps();
verifyNever(mockIsolate.flutterExit());
verify(mockDevice.stopApp(any)).called(1);
}));
test('Will exit an un-paused isolate', () => testbed.run(() async {
final TestFlutterDevice flutterDevice = TestFlutterDevice(
mockDevice,
<FlutterView> [mockFlutterView ],
);
final MockServiceEvent mockServiceEvent = MockServiceEvent();
when(mockServiceEvent.isPauseEvent).thenReturn(false);
when(mockIsolate.pauseEvent).thenReturn(mockServiceEvent);
when(mockDevice.supportsFlutterExit).thenReturn(true);
await flutterDevice.exitApps();
verify(mockIsolate.flutterExit()).called(1);
}));
});
});
}
class MockFlutterDevice extends Mock implements FlutterDevice {}
class MockFlutterView extends Mock implements FlutterView {}
class MockVMService extends Mock implements VMService {}
class MockDevFS extends Mock implements DevFS {}
class MockIsolate extends Mock implements Isolate {}
class MockDevice extends Mock implements Device {}
class MockUsage extends Mock implements Usage {}
class MockServiceEvent extends Mock implements ServiceEvent {}
class TestFlutterDevice extends FlutterDevice {
TestFlutterDevice(Device device, this.views)
: super(device, buildMode: BuildMode.debug, trackWidgetCreation: false);
@override
final List<FlutterView> views;
}