| // Copyright 2019 The Flutter Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. |
| |
| import 'dart:async'; |
| import 'dart:convert'; |
| import 'dart:io'; |
| |
| import 'package:devtools_app/devtools_app.dart'; |
| // ignore: implementation_imports, required to separate V2 inspector imports. |
| import 'package:devtools_app/src/screens/inspector_v2/inspector_controller.dart' |
| as inspector_v2; |
| // ignore: implementation_imports, required to separate V2 inspector imports. |
| import 'package:devtools_app/src/shared/console/eval/inspector_tree_v2.dart' |
| as inspector_v2; |
| import 'package:devtools_app_shared/service.dart'; |
| import 'package:devtools_shared/devtools_shared.dart'; |
| import 'package:flutter/foundation.dart'; |
| import 'package:mockito/mockito.dart'; |
| import 'package:vm_service/vm_service.dart'; |
| |
| import 'generated_mocks_factories.dart'; |
| |
| class FakeInspectorService extends Fake implements InspectorService { |
| final pubRootDirectories = <String>{}; |
| @override |
| ObjectGroup createObjectGroup(String debugName) { |
| return ObjectGroup(debugName, this); |
| } |
| |
| @override |
| Future<bool> isWidgetTreeReady() async { |
| return false; |
| } |
| |
| @override |
| Future<List<String>?> getPubRootDirectories() { |
| return Future.value(pubRootDirectories.toList()); |
| } |
| |
| @override |
| Future<void> addPubRootDirectories(List<String> rootDirectories) { |
| pubRootDirectories.addAll(rootDirectories); |
| return Future<void>.value(); |
| } |
| |
| @override |
| Future<void> removePubRootDirectories(List<String> rootDirectories) { |
| pubRootDirectories.removeAll(rootDirectories); |
| return Future<void>.value(); |
| } |
| |
| @override |
| bool get useDaemonApi => true; |
| |
| @override |
| final clients = <InspectorServiceClient>{}; |
| |
| @override |
| void addClient(InspectorServiceClient client) { |
| clients.add(client); |
| } |
| |
| @override |
| void removeClient(InspectorServiceClient client) { |
| clients.remove(client); |
| } |
| |
| @override |
| bool get hoverEvalModeEnabledByDefault => true; |
| } |
| |
| class TestInspectorController extends Fake implements InspectorController { |
| InspectorService service = FakeInspectorService(); |
| |
| @override |
| ValueListenable<InspectorTreeNode?> get selectedNode => _selectedNode; |
| final _selectedNode = ValueNotifier<InspectorTreeNode?>(null); |
| |
| @override |
| void setSelectedNode(InspectorTreeNode? newSelection) { |
| _selectedNode.value = newSelection; |
| } |
| |
| @override |
| InspectorService get inspectorService => service; |
| } |
| |
| class TestInspectorV2Controller extends Fake |
| implements inspector_v2.InspectorController { |
| InspectorService service = FakeInspectorService(); |
| |
| @override |
| ValueListenable<inspector_v2.InspectorTreeNode?> get selectedNode => |
| _selectedNode; |
| final _selectedNode = ValueNotifier<inspector_v2.InspectorTreeNode?>(null); |
| |
| @override |
| RemoteDiagnosticsNode? get selectedDiagnostic => _selectedDiagnostic; |
| RemoteDiagnosticsNode? _selectedDiagnostic; |
| |
| @override |
| ValueListenable<inspector_v2.WidgetTreeNodeProperties> |
| get selectedNodeProperties => |
| ValueNotifier<inspector_v2.WidgetTreeNodeProperties>(( |
| widgetProperties: [], |
| renderProperties: [], |
| layoutProperties: null, |
| )); |
| |
| @override |
| void setSelectedNode(inspector_v2.InspectorTreeNode? newSelection) { |
| _selectedNode.value = newSelection; |
| } |
| |
| void setSelectedDiagnostic(RemoteDiagnosticsNode newSelection) { |
| _selectedDiagnostic = newSelection; |
| } |
| |
| @override |
| InspectorService get inspectorService => service; |
| } |
| |
| class FakeVM extends Fake implements VM { |
| FakeVM(); |
| |
| @override |
| Map<String, dynamic>? json = {'_FAKE_VM': true, '_currentRSS': 0}; |
| } |
| |
| class TestCodeViewController extends CodeViewController { |
| @override |
| ProgramExplorerController get programExplorerController => |
| _explorerController; |
| final _explorerController = createMockProgramExplorerControllerWithDefaults(); |
| } |
| |
| void mockWebVm(VM vm) { |
| when(vm.targetCPU).thenReturn('Web'); |
| when(vm.architectureBits).thenReturn(-1); |
| when(vm.operatingSystem).thenReturn('macos'); |
| } |
| |
| void mockConnectedApp( |
| ConnectedApp connectedApp, { |
| bool isFlutterApp = true, |
| bool isProfileBuild = false, |
| bool isWebApp = false, |
| String os = 'ios', |
| String flutterVersion = '2.10.0', |
| }) { |
| assert(!(!isFlutterApp && isProfileBuild)); |
| |
| // Dart VM. |
| when(connectedApp.isRunningOnDartVM).thenReturn(!isWebApp); |
| |
| // Flutter app. |
| when(connectedApp.isFlutterAppNow).thenReturn(isFlutterApp); |
| when(connectedApp.isFlutterApp).thenAnswer((_) => Future.value(isFlutterApp)); |
| when( |
| connectedApp.isFlutterNativeAppNow, |
| ).thenReturn(isFlutterApp && !isWebApp); |
| if (isFlutterApp) { |
| when(connectedApp.flutterVersionNow).thenReturn( |
| FlutterVersion.parse({ |
| 'type': 'Success', |
| 'frameworkVersion': flutterVersion, |
| 'channel': 'unknown', |
| 'repositoryUrl': 'unknown source', |
| 'frameworkRevision': '74432fa91c8ffbc555ffc2701309e8729380a012', |
| 'frameworkCommitDate': '2020-05-14 13:05:34 -0700', |
| 'engineRevision': 'ae2222f47e788070c09020311b573542b9706a78', |
| 'dartSdkVersion': '2.9.0 (build 2.9.0-8.0.dev d6fed1f624)', |
| 'frameworkRevisionShort': '74432fa91c', |
| 'engineRevisionShort': 'ae2222f47e', |
| }), |
| ); |
| } else { |
| when(connectedApp.flutterVersionNow).thenReturn(null); |
| } |
| |
| // Flutter web app. |
| when(connectedApp.isFlutterWebAppNow).thenReturn(isFlutterApp && isWebApp); |
| |
| // Web app. |
| when(connectedApp.isDartWebApp).thenAnswer((_) => Future.value(isWebApp)); |
| when(connectedApp.isDartWebAppNow).thenReturn(isWebApp); |
| |
| // CLI app. |
| final isCliApp = !isFlutterApp && !isWebApp; |
| when(connectedApp.isDartCliApp).thenAnswer((_) => Future.value(isCliApp)); |
| when(connectedApp.isDartCliAppNow).thenReturn(isCliApp); |
| |
| // Run mode. |
| when( |
| connectedApp.isProfileBuild, |
| ).thenAnswer((_) => Future.value(isProfileBuild)); |
| when(connectedApp.isProfileBuildNow).thenReturn(isProfileBuild); |
| when( |
| connectedApp.isDebugFlutterAppNow, |
| ).thenReturn(isFlutterApp && !isProfileBuild); |
| |
| // Operating system. |
| when(connectedApp.operatingSystem).thenReturn(os); |
| |
| // Initialized. |
| when(connectedApp.connectedAppInitialized).thenReturn(true); |
| when(connectedApp.initialized).thenReturn(Completer()..complete(true)); |
| } |
| |
| void mockFlutterVersion(ConnectedApp connectedApp, SemanticVersion version) { |
| when( |
| connectedApp.flutterVersionNow, |
| ).thenReturn(FlutterVersion.parse({'frameworkVersion': '$version'})); |
| when(connectedApp.connectedAppInitialized).thenReturn(true); |
| } |
| |
| final mockGrammar = Grammar.fromJson( |
| jsonDecode(''' |
| { |
| "name": "Dart", |
| "fileTypes": [ |
| "dart" |
| ], |
| "scopeName": "source.dart", |
| "patterns": [], |
| "repository": {} |
| } |
| '''), |
| ); |
| |
| final mockScriptRef = ScriptRef( |
| uri: 'package:gallery/main.dart', |
| id: 'test-script-long-lines', |
| ); |
| |
| final mockLargeScriptRef = ScriptRef( |
| uri: 'package:front_end/src/fasta/kernel/body_builder.dart', |
| id: 'test-large-script', |
| ); |
| |
| final mockEmptyScriptRef = ScriptRef( |
| uri: 'package:gallery/src/unknown.dart', |
| id: 'mock-script-no-source', |
| ); |
| |
| final mockScript = _loadScript('script.json'); |
| |
| final mockLargeScript = _loadScript('large_script.json'); |
| |
| final mockEmptyScript = Script( |
| uri: 'package:gallery/src/unknown.dart', |
| id: 'mock-script-no-source', |
| ); |
| |
| Script? _loadScript(String scriptName) { |
| final script = File('../devtools_test/lib/src/mocks/mock_data/$scriptName'); |
| return Script.parse(jsonDecode(script.readAsStringSync())); |
| } |
| |
| final mockSyntaxHighlighter = SyntaxHighlighter.withGrammar( |
| grammar: mockGrammar, |
| source: mockScript!.source, |
| ); |
| |
| const coverageHitLines = <int>{1, 3, 4, 7}; |
| |
| const coverageMissLines = <int>{2, 5}; |
| |
| const executableLines = <int>{...coverageHitLines, ...coverageMissLines}; |
| |
| const profilerEntries = <int, ProfileReportEntry>{ |
| 1: ProfileReportEntry(sampleCount: 5, line: 1, inclusive: 2, exclusive: 2), |
| 3: ProfileReportEntry(sampleCount: 5, line: 3, inclusive: 1, exclusive: 1), |
| 4: ProfileReportEntry(sampleCount: 5, line: 4, inclusive: 1, exclusive: 1), |
| 7: ProfileReportEntry(sampleCount: 5, line: 7, inclusive: 1, exclusive: 1), |
| }; |
| |
| final mockParsedScript = ParsedScript( |
| script: mockScript!, |
| highlighter: mockSyntaxHighlighter, |
| executableLines: executableLines, |
| sourceReport: ProcessedSourceReport( |
| coverageHitLines: coverageHitLines, |
| coverageMissedLines: coverageMissLines, |
| profilerEntries: profilerEntries, |
| ), |
| ); |
| |
| final mockScriptRefs = [ |
| ScriptRef(uri: 'zoo:animals/cats/meow.dart', id: 'fake/id/1'), |
| ScriptRef(uri: 'zoo:animals/cats/purr.dart', id: 'fake/id/2'), |
| ScriptRef(uri: 'zoo:animals/dogs/bark.dart', id: 'fake/id/3'), |
| ScriptRef(uri: 'zoo:animals/dogs/growl.dart', id: 'fake/id/4'), |
| ScriptRef(uri: 'zoo:animals/insects/caterpillar.dart', id: 'fake/id/5'), |
| ScriptRef(uri: 'zoo:animals/insects/cicada.dart', id: 'fake/id/6'), |
| ScriptRef(uri: 'kitchen:food/catering/party.dart', id: 'fake/id/7'), |
| ScriptRef(uri: 'kitchen:food/carton/milk.dart', id: 'fake/id/8'), |
| ScriptRef(uri: 'kitchen:food/milk/carton.dart', id: 'fake/id/9'), |
| ScriptRef(uri: 'travel:adventure/cave_tours_europe.dart', id: 'fake/id/10'), |
| ScriptRef(uri: 'travel:canada/banff.dart', id: 'fake/id/11'), |
| ]; |
| |
| class MockOfflineDataController extends Mock implements OfflineDataController { |
| @override |
| ValueNotifier<bool> showingOfflineData = ValueNotifier<bool>(false); |
| } |