
* Update project.pbxproj files to say Flutter rather than Chromium Also, the templates now have an empty organization so that we don't cause people to give their apps a Flutter copyright. * Update the copyright notice checker to require a standard notice on all files * Update copyrights on Dart files. (This was a mechanical commit.) * Fix weird license headers on Dart files that deviate from our conventions; relicense Shrine. Some were already marked "The Flutter Authors", not clear why. Their dates have been normalized. Some were missing the blank line after the license. Some were randomly different in trivial ways for no apparent reason (e.g. missing the trailing period). * Clean up the copyrights in non-Dart files. (Manual edits.) Also, make sure templates don't have copyrights. * Fix some more ORGANIZATIONNAMEs
681 lines
27 KiB
Dart
681 lines
27 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.
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:file/memory.dart';
|
|
import 'package:flutter_tools/src/artifacts.dart';
|
|
import 'package:flutter_tools/src/base/common.dart';
|
|
import 'package:flutter_tools/src/base/context.dart';
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
|
import 'package:flutter_tools/src/base/io.dart' as io;
|
|
import 'package:flutter_tools/src/build_info.dart';
|
|
import 'package:flutter_tools/src/compile.dart';
|
|
import 'package:flutter_tools/src/devfs.dart';
|
|
import 'package:flutter_tools/src/device.dart';
|
|
import 'package:flutter_tools/src/features.dart';
|
|
import 'package:flutter_tools/src/globals.dart';
|
|
import 'package:flutter_tools/src/project.dart';
|
|
import 'package:flutter_tools/src/reporting/reporting.dart';
|
|
import 'package:flutter_tools/src/resident_runner.dart';
|
|
import 'package:flutter_tools/src/run_cold.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/context.dart';
|
|
import '../src/testbed.dart';
|
|
|
|
void main() {
|
|
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: () {
|
|
fs.file(fs.path.join('build', 'app.dill'))
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('ABC');
|
|
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.baseUri).thenReturn(Uri());
|
|
when(mockDevFS.destroy()).thenAnswer((Invocation invocation) async { });
|
|
when(mockDevFS.assetPathsToEvict).thenReturn(<String>{});
|
|
// 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'),
|
|
dillOutputPath: anyNamed('dillOutputPath'),
|
|
)).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(mockFlutterView.runFromSource(any, any, any)).thenAnswer((Invocation invocation) async {});
|
|
when(mockFlutterDevice.stopEchoingDeviceLog()).thenAnswer((Invocation invocation) async { });
|
|
when(mockFlutterDevice.observatoryUris).thenAnswer((_) => Stream<Uri>.value(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.vmService).thenReturn(mockVMService);
|
|
when(mockFlutterDevice.refreshViews()).thenAnswer((Invocation invocation) async { });
|
|
when(mockFlutterDevice.reloadSources(any, pause: anyNamed('pause'))).thenReturn(<Future<Map<String, dynamic>>>[
|
|
Future<Map<String, dynamic>>.value(<String, dynamic>{
|
|
'type': 'ReloadReport',
|
|
'success': true,
|
|
'details': <String, dynamic>{
|
|
'loadedLibraryCount': 1,
|
|
'finalLibraryCount': 1,
|
|
'receivedLibraryCount': 1,
|
|
'receivedClassesCount': 1,
|
|
'receivedProceduresCount': 1,
|
|
},
|
|
}),
|
|
]);
|
|
// 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);
|
|
});
|
|
when(mockIsolate.reload()).thenAnswer((Invocation invocation) {
|
|
return Future<ServiceObject>.value(null);
|
|
});
|
|
});
|
|
|
|
test('ResidentRunner 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('ResidentRunner 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'),
|
|
dillOutputPath: anyNamed('dillOutputPath'),
|
|
)).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>{
|
|
cdKey(CustomDimensions.hotEventTargetPlatform):
|
|
getNameForTargetPlatform(TargetPlatform.android_arm),
|
|
cdKey(CustomDimensions.hotEventSdkName): 'Example',
|
|
cdKey(CustomDimensions.hotEventEmulator): 'false',
|
|
cdKey(CustomDimensions.hotEventFullRestart): 'false',
|
|
})).called(1);
|
|
}, overrides: <Type, Generator>{
|
|
Usage: () => MockUsage(),
|
|
}));
|
|
|
|
test('ResidentRunner copies dill file from build output into temp directory', () => testbed.run(() async {
|
|
expect(residentRunner.artifactDirectory.childFile('app.dill').readAsStringSync(), 'ABC');
|
|
}));
|
|
|
|
test('ResidentRunner can send target platform to analytics 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,
|
|
));
|
|
|
|
final OperationResult result = await residentRunner.restart(fullRestart: false);
|
|
expect(result.fatal, false);
|
|
expect(result.code, 0);
|
|
expect(verify(flutterUsage.sendEvent('hot', 'reload',
|
|
parameters: captureAnyNamed('parameters'))).captured[0],
|
|
containsPair(cdKey(CustomDimensions.hotEventTargetPlatform),
|
|
getNameForTargetPlatform(TargetPlatform.android_arm)),
|
|
);
|
|
}, overrides: <Type, Generator>{
|
|
Usage: () => MockUsage(),
|
|
}));
|
|
|
|
test('ResidentRunner can send target platform to analytics from full 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,
|
|
));
|
|
|
|
final OperationResult result = await residentRunner.restart(fullRestart: true);
|
|
expect(result.fatal, false);
|
|
expect(result.code, 0);
|
|
expect(verify(flutterUsage.sendEvent('hot', 'restart',
|
|
parameters: captureAnyNamed('parameters'))).captured[0],
|
|
containsPair(cdKey(CustomDimensions.hotEventTargetPlatform),
|
|
getNameForTargetPlatform(TargetPlatform.android_arm)),
|
|
);
|
|
}, overrides: <Type, Generator>{
|
|
Usage: () => MockUsage(),
|
|
}));
|
|
|
|
test('ResidentRunner 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'),
|
|
dillOutputPath: anyNamed('dillOutputPath'),
|
|
)).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>{
|
|
cdKey(CustomDimensions.hotEventTargetPlatform):
|
|
getNameForTargetPlatform(TargetPlatform.android_arm),
|
|
cdKey(CustomDimensions.hotEventSdkName): 'Example',
|
|
cdKey(CustomDimensions.hotEventEmulator): 'false',
|
|
cdKey(CustomDimensions.hotEventFullRestart): 'true',
|
|
})).called(1);
|
|
}, overrides: <Type, Generator>{
|
|
Usage: () => MockUsage(),
|
|
}));
|
|
|
|
test('ResidentRunner uses temp directory when there is no output dill path', () => testbed.run(() {
|
|
expect(residentRunner.artifactDirectory.path, contains('flutter_tool.'));
|
|
|
|
final ResidentRunner otherRunner = HotRunner(
|
|
<FlutterDevice>[
|
|
mockFlutterDevice,
|
|
],
|
|
stayResident: false,
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
|
|
dillOutputPath: fs.path.join('foobar', 'app.dill'),
|
|
);
|
|
expect(otherRunner.artifactDirectory.path, contains('foobar'));
|
|
}));
|
|
|
|
test('ResidentRunner printHelpDetails', () => testbed.run(() {
|
|
when(mockDevice.supportsHotRestart).thenReturn(true);
|
|
when(mockDevice.supportsScreenshot).thenReturn(true);
|
|
|
|
residentRunner.printHelp(details: true);
|
|
|
|
// supports service protocol
|
|
expect(residentRunner.supportsServiceProtocol, true);
|
|
expect(testLogger.statusText, contains('"w"'));
|
|
expect(testLogger.statusText, contains('"t"'));
|
|
expect(testLogger.statusText, contains('"P"'));
|
|
expect(testLogger.statusText, contains('"a"'));
|
|
// isRunningDebug
|
|
expect(residentRunner.isRunningDebug, true);
|
|
expect(testLogger.statusText, contains('"L"'));
|
|
expect(testLogger.statusText, contains('"S"'));
|
|
expect(testLogger.statusText, contains('"U"'));
|
|
expect(testLogger.statusText, contains('"i"'));
|
|
expect(testLogger.statusText, contains('"p"'));
|
|
expect(testLogger.statusText, contains('"o"'));
|
|
expect(testLogger.statusText, contains('"z"'));
|
|
// screenshot
|
|
expect(testLogger.statusText, contains('"s"'));
|
|
}));
|
|
|
|
test('ResidentRunner can take screenshot on debug device', () => testbed.run(() async {
|
|
when(mockDevice.supportsScreenshot).thenReturn(true);
|
|
when(mockDevice.takeScreenshot(any))
|
|
.thenAnswer((Invocation invocation) async {
|
|
final File file = invocation.positionalArguments.first as File;
|
|
file.writeAsBytesSync(List<int>.generate(1024, (int i) => i));
|
|
});
|
|
|
|
await residentRunner.screenshot(mockFlutterDevice);
|
|
|
|
// disables debug banner.
|
|
verify(mockIsolate.flutterDebugAllowBanner(false)).called(1);
|
|
// Enables debug banner.
|
|
verify(mockIsolate.flutterDebugAllowBanner(true)).called(1);
|
|
expect(testLogger.statusText, contains('1kB'));
|
|
}));
|
|
|
|
test('ResidentRunner bails taking screenshot on debug device if debugAllowBanner throws pre', () => testbed.run(() async {
|
|
when(mockDevice.supportsScreenshot).thenReturn(true);
|
|
when(mockIsolate.flutterDebugAllowBanner(false)).thenThrow(Exception());
|
|
|
|
await residentRunner.screenshot(mockFlutterDevice);
|
|
|
|
expect(testLogger.errorText, contains('Error'));
|
|
}));
|
|
|
|
test('ResidentRunner bails taking screenshot on debug device if debugAllowBanner throws post', () => testbed.run(() async {
|
|
when(mockDevice.supportsScreenshot).thenReturn(true);
|
|
when(mockIsolate.flutterDebugAllowBanner(true)).thenThrow(Exception());
|
|
|
|
await residentRunner.screenshot(mockFlutterDevice);
|
|
|
|
expect(testLogger.errorText, contains('Error'));
|
|
}));
|
|
|
|
test('ResidentRunner bails taking screenshot on debug device if takeScreenshot throws', () => testbed.run(() async {
|
|
when(mockDevice.supportsScreenshot).thenReturn(true);
|
|
when(mockDevice.takeScreenshot(any)).thenThrow(Exception());
|
|
|
|
await residentRunner.screenshot(mockFlutterDevice);
|
|
|
|
expect(testLogger.errorText, contains('Error'));
|
|
}));
|
|
|
|
test('ResidentRunner can\'t take screenshot on device without support', () => testbed.run(() {
|
|
when(mockDevice.supportsScreenshot).thenReturn(false);
|
|
|
|
expect(() => residentRunner.screenshot(mockFlutterDevice),
|
|
throwsA(isInstanceOf<AssertionError>()));
|
|
}));
|
|
|
|
test('ResidentRunner does not toggle banner in non-debug mode', () => testbed.run(() async {
|
|
residentRunner = HotRunner(
|
|
<FlutterDevice>[
|
|
mockFlutterDevice,
|
|
],
|
|
stayResident: false,
|
|
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
|
|
);
|
|
when(mockDevice.supportsScreenshot).thenReturn(true);
|
|
when(mockDevice.takeScreenshot(any))
|
|
.thenAnswer((Invocation invocation) async {
|
|
final File file = invocation.positionalArguments.first as File;
|
|
file.writeAsBytesSync(List<int>.generate(1024, (int i) => i));
|
|
});
|
|
|
|
await residentRunner.screenshot(mockFlutterDevice);
|
|
|
|
// doesn't disabled debug banner.
|
|
verifyNever(mockIsolate.flutterDebugAllowBanner(false));
|
|
// doesn't enable debug banner.
|
|
verifyNever(mockIsolate.flutterDebugAllowBanner(true));
|
|
expect(testLogger.statusText, contains('1kB'));
|
|
}));
|
|
|
|
test('FlutterDevice 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('FlutterDevice 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);
|
|
}));
|
|
|
|
test('ResidentRunner refreshViews calls flutter device', () => testbed.run(() async {
|
|
await residentRunner.refreshViews();
|
|
|
|
verify(mockFlutterDevice.refreshViews()).called(1);
|
|
}));
|
|
|
|
test('ResidentRunner debugDumpApp calls flutter device', () => testbed.run(() async {
|
|
await residentRunner.debugDumpApp();
|
|
|
|
verify(mockFlutterDevice.refreshViews()).called(1);
|
|
verify(mockFlutterDevice.debugDumpApp()).called(1);
|
|
}));
|
|
|
|
test('ResidentRunner debugDumpRenderTree calls flutter device', () => testbed.run(() async {
|
|
await residentRunner.debugDumpRenderTree();
|
|
|
|
verify(mockFlutterDevice.refreshViews()).called(1);
|
|
verify(mockFlutterDevice.debugDumpRenderTree()).called(1);
|
|
}));
|
|
|
|
test('ResidentRunner debugDumpLayerTree calls flutter device', () => testbed.run(() async {
|
|
await residentRunner.debugDumpLayerTree();
|
|
|
|
verify(mockFlutterDevice.refreshViews()).called(1);
|
|
verify(mockFlutterDevice.debugDumpLayerTree()).called(1);
|
|
}));
|
|
|
|
test('ResidentRunner debugDumpSemanticsTreeInTraversalOrder calls flutter device', () => testbed.run(() async {
|
|
await residentRunner.debugDumpSemanticsTreeInTraversalOrder();
|
|
|
|
verify(mockFlutterDevice.refreshViews()).called(1);
|
|
verify(mockFlutterDevice.debugDumpSemanticsTreeInTraversalOrder()).called(1);
|
|
}));
|
|
|
|
test('ResidentRunner debugDumpSemanticsTreeInInverseHitTestOrder calls flutter device', () => testbed.run(() async {
|
|
await residentRunner.debugDumpSemanticsTreeInInverseHitTestOrder();
|
|
|
|
verify(mockFlutterDevice.refreshViews()).called(1);
|
|
verify(mockFlutterDevice.debugDumpSemanticsTreeInInverseHitTestOrder()).called(1);
|
|
}));
|
|
|
|
test('ResidentRunner debugToggleDebugPaintSizeEnabled calls flutter device', () => testbed.run(() async {
|
|
await residentRunner.debugToggleDebugPaintSizeEnabled();
|
|
|
|
verify(mockFlutterDevice.refreshViews()).called(1);
|
|
verify(mockFlutterDevice.toggleDebugPaintSizeEnabled()).called(1);
|
|
}));
|
|
|
|
test('ResidentRunner debugToggleDebugCheckElevationsEnabled calls flutter device', () => testbed.run(() async {
|
|
await residentRunner.debugToggleDebugCheckElevationsEnabled();
|
|
|
|
verify(mockFlutterDevice.refreshViews()).called(1);
|
|
verify(mockFlutterDevice.toggleDebugCheckElevationsEnabled()).called(1);
|
|
}));
|
|
|
|
test('ResidentRunner debugTogglePerformanceOverlayOverride calls flutter device', () => testbed.run(()async {
|
|
await residentRunner.debugTogglePerformanceOverlayOverride();
|
|
|
|
verify(mockFlutterDevice.refreshViews()).called(1);
|
|
verify(mockFlutterDevice.debugTogglePerformanceOverlayOverride()).called(1);
|
|
}));
|
|
|
|
test('ResidentRunner debugToggleWidgetInspector calls flutter device', () => testbed.run(() async {
|
|
await residentRunner.debugToggleWidgetInspector();
|
|
|
|
verify(mockFlutterDevice.refreshViews()).called(1);
|
|
verify(mockFlutterDevice.toggleWidgetInspector()).called(1);
|
|
}));
|
|
|
|
test('ResidentRunner debugToggleProfileWidgetBuilds calls flutter device', () => testbed.run(() async {
|
|
await residentRunner.debugToggleProfileWidgetBuilds();
|
|
|
|
verify(mockFlutterDevice.refreshViews()).called(1);
|
|
verify(mockFlutterDevice.toggleProfileWidgetBuilds()).called(1);
|
|
}));
|
|
|
|
test('HotRunner writes vm service file when providing debugging option', () => testbed.run(() async {
|
|
fs.file(fs.path.join('lib', 'main.dart')).createSync(recursive: true);
|
|
residentRunner = HotRunner(
|
|
<FlutterDevice>[
|
|
mockFlutterDevice,
|
|
],
|
|
stayResident: false,
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug, vmserviceOutFile: 'foo'),
|
|
);
|
|
when(mockFlutterDevice.runHot(
|
|
hotRunner: anyNamed('hotRunner'),
|
|
route: anyNamed('route'),
|
|
)).thenAnswer((Invocation invocation) async {
|
|
return 0;
|
|
});
|
|
await residentRunner.run();
|
|
|
|
expect(await fs.file('foo').readAsString(), testUri.toString());
|
|
}));
|
|
|
|
test('HotRunner handles failure to write vmservice file', () => testbed.run(() async {
|
|
fs.file(fs.path.join('lib', 'main.dart')).createSync(recursive: true);
|
|
residentRunner = HotRunner(
|
|
<FlutterDevice>[
|
|
mockFlutterDevice,
|
|
],
|
|
stayResident: false,
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug, vmserviceOutFile: 'foo'),
|
|
);
|
|
when(mockFlutterDevice.runHot(
|
|
hotRunner: anyNamed('hotRunner'),
|
|
route: anyNamed('route'),
|
|
)).thenAnswer((Invocation invocation) async {
|
|
return 0;
|
|
});
|
|
await residentRunner.run();
|
|
|
|
expect(testLogger.errorText, contains('Failed to write vmservice-out-file at foo'));
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => ThrowingForwardingFileSystem(MemoryFileSystem()),
|
|
}));
|
|
|
|
|
|
test('ColdRunner writes vm service file when providing debugging option', () => testbed.run(() async {
|
|
fs.file(fs.path.join('lib', 'main.dart')).createSync(recursive: true);
|
|
residentRunner = ColdRunner(
|
|
<FlutterDevice>[
|
|
mockFlutterDevice,
|
|
],
|
|
stayResident: false,
|
|
debuggingOptions: DebuggingOptions.enabled(BuildInfo.profile, vmserviceOutFile: 'foo'),
|
|
);
|
|
when(mockFlutterDevice.runCold(
|
|
coldRunner: anyNamed('coldRunner'),
|
|
route: anyNamed('route'),
|
|
)).thenAnswer((Invocation invocation) async {
|
|
return 0;
|
|
});
|
|
await residentRunner.run();
|
|
|
|
expect(await fs.file('foo').readAsString(), testUri.toString());
|
|
}));
|
|
|
|
test('FlutterDevice uses dartdevc configuration when targeting web', () => testbed.run(() async {
|
|
final MockDevice mockDevice = MockDevice();
|
|
when(mockDevice.targetPlatform).thenAnswer((Invocation invocation) async {
|
|
return TargetPlatform.web_javascript;
|
|
});
|
|
|
|
final DefaultResidentCompiler residentCompiler = (await FlutterDevice.create(
|
|
mockDevice,
|
|
buildMode: BuildMode.debug,
|
|
flutterProject: FlutterProject.current(),
|
|
target: null,
|
|
trackWidgetCreation: true,
|
|
)).generator as DefaultResidentCompiler;
|
|
|
|
expect(residentCompiler.targetModel, TargetModel.dartdevc);
|
|
expect(residentCompiler.sdkRoot,
|
|
artifacts.getArtifactPath(Artifact.flutterWebSdk, mode: BuildMode.debug) + '/');
|
|
expect(
|
|
residentCompiler.platformDill,
|
|
fs.file(artifacts.getArtifactPath(Artifact.webPlatformKernelDill, mode: BuildMode.debug))
|
|
.absolute.uri.toString(),
|
|
);
|
|
}, overrides: <Type, Generator>{
|
|
FeatureFlags: () => TestFeatureFlags(isWebIncrementalCompilerEnabled: true),
|
|
}));
|
|
|
|
test('connect sets up log reader', () => testbed.run(() async {
|
|
final MockDevice mockDevice = MockDevice();
|
|
final MockDeviceLogReader mockLogReader = MockDeviceLogReader();
|
|
when(mockDevice.getLogReader(app: anyNamed('app'))).thenReturn(mockLogReader);
|
|
|
|
final TestFlutterDevice flutterDevice = TestFlutterDevice(
|
|
mockDevice,
|
|
<FlutterView>[],
|
|
observatoryUris: Stream<Uri>.value(testUri),
|
|
);
|
|
|
|
await flutterDevice.connect();
|
|
verify(mockLogReader.connectedVMService = mockVMService);
|
|
}, overrides: <Type, Generator>{
|
|
VMServiceConnector: () => (Uri httpUri, {
|
|
ReloadSources reloadSources,
|
|
Restart restart,
|
|
CompileExpression compileExpression,
|
|
io.CompressionOptions compression,
|
|
Device device,
|
|
}) async => mockVMService,
|
|
}));
|
|
}
|
|
|
|
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 MockDeviceLogReader extends Mock implements DeviceLogReader {}
|
|
class MockUsage extends Mock implements Usage {}
|
|
class MockProcessManager extends Mock implements ProcessManager {}
|
|
class MockServiceEvent extends Mock implements ServiceEvent {}
|
|
class TestFlutterDevice extends FlutterDevice {
|
|
TestFlutterDevice(Device device, this.views, { Stream<Uri> observatoryUris })
|
|
: super(device, buildMode: BuildMode.debug, trackWidgetCreation: false) {
|
|
_observatoryUris = observatoryUris;
|
|
}
|
|
|
|
@override
|
|
final List<FlutterView> views;
|
|
|
|
@override
|
|
Stream<Uri> get observatoryUris => _observatoryUris;
|
|
Stream<Uri> _observatoryUris;
|
|
}
|
|
|
|
class ThrowingForwardingFileSystem extends ForwardingFileSystem {
|
|
ThrowingForwardingFileSystem(FileSystem delegate) : super(delegate);
|
|
|
|
@override
|
|
File file(dynamic path) {
|
|
if (path == 'foo') {
|
|
throw const FileSystemException();
|
|
}
|
|
return delegate.file(path);
|
|
}
|
|
}
|