// 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:dds/dds.dart' as dds; import 'package:flutter_tools/src/application_package.dart'; import 'package:flutter_tools/src/asset.dart'; import 'package:flutter_tools/src/base/dds.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_system/tools/scene_importer.dart'; import 'package:flutter_tools/src/build_system/tools/shader_compiler.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/device_port_forwarder.dart'; import 'package:flutter_tools/src/project.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:package_config/package_config.dart'; import 'package:test/fake.dart'; import 'package:vm_service/vm_service.dart' as vm_service; import '../src/fake_vm_services.dart'; final vm_service.Event fakeUnpausedEvent = vm_service.Event( kind: vm_service.EventKind.kResume, timestamp: 0 ); final vm_service.Event fakePausedEvent = vm_service.Event( kind: vm_service.EventKind.kPauseException, timestamp: 0 ); final vm_service.Isolate fakeUnpausedIsolate = vm_service.Isolate( id: '1', pauseEvent: fakeUnpausedEvent, breakpoints: [], extensionRPCs: [], libraries: [ vm_service.LibraryRef( id: '1', uri: 'file:///hello_world/main.dart', name: '', ), ], livePorts: 0, name: 'test', number: '1', pauseOnExit: false, runnable: true, startTime: 0, isSystemIsolate: false, isolateFlags: [], ); final vm_service.Isolate fakePausedIsolate = vm_service.Isolate( id: '1', pauseEvent: fakePausedEvent, breakpoints: [ vm_service.Breakpoint( breakpointNumber: 123, id: 'test-breakpoint', location: vm_service.SourceLocation( tokenPos: 0, script: vm_service.ScriptRef(id: 'test-script', uri: 'foo.dart'), ), enabled: true, resolved: true, ), ], libraries: [], livePorts: 0, name: 'test', number: '1', pauseOnExit: false, runnable: true, startTime: 0, isSystemIsolate: false, isolateFlags: [], ); final vm_service.VM fakeVM = vm_service.VM( isolates: [fakeUnpausedIsolate], pid: 1, hostCPU: '', isolateGroups: [], targetCPU: '', startTime: 0, name: 'dart', architectureBits: 64, operatingSystem: '', version: '', systemIsolateGroups: [], systemIsolates: [], ); final FlutterView fakeFlutterView = FlutterView( id: 'a', uiIsolate: fakeUnpausedIsolate, ); final FakeVmServiceRequest listViews = FakeVmServiceRequest( method: kListViewsMethod, jsonResponse: { 'views': [ fakeFlutterView.toJson(), ], }, ); const FakeVmServiceRequest renderFrameRasterStats = FakeVmServiceRequest( method: kRenderFrameWithRasterStatsMethod, args: { 'viewId': 'a', 'isolateId': '1', }, error: FakeRPCError( code: RPCErrorCodes.kServerError, error: 'Raster status not supported on Impeller backend', ), ); const FakeVmServiceRequest setAssetBundlePath = FakeVmServiceRequest( method: '_flutter.setAssetBundlePath', args: { 'viewId': 'a', 'assetDirectory': 'build/flutter_assets', 'isolateId': '1', } ); const FakeVmServiceRequest evict = FakeVmServiceRequest( method: 'ext.flutter.evict', args: { 'value': 'asset', 'isolateId': '1', } ); const FakeVmServiceRequest evictShader = FakeVmServiceRequest( method: 'ext.ui.window.reinitializeShader', args: { 'assetKey': 'foo.frag', 'isolateId': '1', } ); final Uri testUri = Uri.parse('foo://bar'); // This implements [dds.DartDevelopmentService], not the [DartDevelopmentService] // interface from package:flutter_tools. class FakeDartDevelopmentService extends Fake implements dds.DartDevelopmentService { @override Future get done => Future.value(); @override Uri? get uri => null; } class FakeDartDevelopmentServiceException implements dds.DartDevelopmentServiceException { FakeDartDevelopmentServiceException({this.message = defaultMessage}); @override final int errorCode = dds.DartDevelopmentServiceException.existingDdsInstanceError; @override final String message; static const String defaultMessage = 'A DDS instance is already connected at http://localhost:8181'; @override Map toJson() => { 'error_code': errorCode, 'message': message, }; } class TestFlutterDevice extends FlutterDevice { TestFlutterDevice(super.device, { Stream? vmServiceUris }) : _vmServiceUris = vmServiceUris, super(buildInfo: BuildInfo.debug, developmentShaderCompiler: const FakeShaderCompiler()); final Stream? _vmServiceUris; @override Stream get vmServiceUris => _vmServiceUris!; } class ThrowingForwardingFileSystem extends ForwardingFileSystem { ThrowingForwardingFileSystem(super.delegate); @override File file(dynamic path) { if (path == 'foo') { throw const FileSystemException(); } return delegate.file(path); } } class FakeFlutterDevice extends Fake implements FlutterDevice { FakeVmServiceHost? Function()? vmServiceHost; Uri? testUri; UpdateFSReport report = UpdateFSReport( success: true, invalidatedSourcesCount: 1, ); Exception? reportError; Exception? runColdError; int runHotCode = 0; int runColdCode = 0; @override ResidentCompiler? generator; @override DevelopmentShaderCompiler get developmentShaderCompiler => const FakeShaderCompiler(); @override TargetPlatform targetPlatform = TargetPlatform.android; @override Stream get vmServiceUris => Stream.value(testUri); @override FlutterVmService? get vmService => vmServiceHost?.call()?.vmService; DevFS? fakeDevFS; @override DevFS? get devFS => fakeDevFS; @override set devFS(DevFS? value) { } @override Device? device; @override Future stopEchoingDeviceLog() async { } @override Future initLogReader() async { } @override Future setupDevFS(String fsName, Directory rootDirectory) async { return testUri!; } @override Future runHot({required HotRunner hotRunner, String? route}) async { return runHotCode; } @override Future runCold({required ColdRunner coldRunner, String? route}) async { if (runColdError != null) { throw runColdError!; } return runColdCode; } @override Future connect({ ReloadSources? reloadSources, Restart? restart, CompileExpression? compileExpression, GetSkSLMethod? getSkSLMethod, FlutterProject? flutterProject, PrintStructuredErrorLogMethod? printStructuredErrorLogMethod, int? hostVmServicePort, int? ddsPort, bool disableServiceAuthCodes = false, bool enableDds = true, bool cacheStartupProfile = false, required bool allowExistingDdsInstance, bool ipv6 = false, }) async { } @override Future updateDevFS({ required Uri mainUri, String? target, AssetBundle? bundle, bool bundleFirstUpload = false, bool bundleDirty = false, bool fullRestart = false, String? projectRootPath, required String pathToReload, required String dillOutputPath, required List invalidatedFiles, required PackageConfig packageConfig, }) async { if (reportError != null) { throw reportError!; } return report; } @override Future updateReloadStatus(bool wasReloadSuccessful) async { } } class FakeDelegateFlutterDevice extends FlutterDevice { FakeDelegateFlutterDevice( super.device, BuildInfo buildInfo, ResidentCompiler residentCompiler, this.fakeDevFS, ) : super(buildInfo: buildInfo, generator: residentCompiler, developmentShaderCompiler: const FakeShaderCompiler()); @override Future connect({ ReloadSources? reloadSources, Restart? restart, bool enableDds = true, bool cacheStartupProfile = false, bool disableServiceAuthCodes = false, bool ipv6 = false, CompileExpression? compileExpression, GetSkSLMethod? getSkSLMethod, FlutterProject? flutterProject, int? hostVmServicePort, int? ddsPort, PrintStructuredErrorLogMethod? printStructuredErrorLogMethod, bool allowExistingDdsInstance = false, }) async { } final DevFS fakeDevFS; @override DevFS? get devFS => fakeDevFS; @override set devFS(DevFS? value) {} } class FakeResidentCompiler extends Fake implements ResidentCompiler { CompilerOutput? nextOutput; bool didSuppressErrors = false; Uri? receivedNativeAssetsYaml; bool recompileCalled = false; @override Future recompile( Uri mainUri, List? invalidatedFiles, { required String outputPath, required PackageConfig packageConfig, String? projectRootPath, required FileSystem fs, bool suppressErrors = false, bool checkDartPluginRegistry = false, File? dartPluginRegistrant, Uri? nativeAssetsYaml, }) async { recompileCalled = true; receivedNativeAssetsYaml = nativeAssetsYaml; didSuppressErrors = suppressErrors; return nextOutput ?? const CompilerOutput('foo.dill', 0, []); } @override void accept() { } @override void reset() { } } class FakeProjectFileInvalidator extends Fake implements ProjectFileInvalidator { @override Future findInvalidated({ required DateTime? lastCompiled, required List urisToMonitor, required String packagesPath, required PackageConfig packageConfig, bool asyncScanning = false, }) async { return InvalidationResult( packageConfig: packageConfig, uris: [Uri.parse('file:///hello_world/main.dart'), ]); } } class FakeDevice extends Fake implements Device { FakeDevice({ String sdkNameAndVersion = 'Android', TargetPlatform targetPlatform = TargetPlatform.android_arm, bool isLocalEmulator = false, this.supportsHotRestart = true, this.supportsScreenshot = true, this.supportsFlutterExit = true, }) : _isLocalEmulator = isLocalEmulator, _targetPlatform = targetPlatform, _sdkNameAndVersion = sdkNameAndVersion; final bool _isLocalEmulator; final TargetPlatform _targetPlatform; final String _sdkNameAndVersion; bool disposed = false; bool appStopped = false; bool failScreenshot = false; @override bool supportsHotRestart; @override bool supportsScreenshot; @override bool supportsFlutterExit; @override PlatformType get platformType => _targetPlatform == TargetPlatform.web_javascript ? PlatformType.web : PlatformType.android; @override Future get sdkNameAndVersion async => _sdkNameAndVersion; @override Future get targetPlatform async => _targetPlatform; @override Future get isLocalEmulator async => _isLocalEmulator; @override String get name => 'FakeDevice'; @override late DartDevelopmentService dds; @override Future dispose() async { disposed = true; } @override Future stopApp(ApplicationPackage? app, {String? userIdentifier}) async { appStopped = true; return true; } @override Future takeScreenshot(File outputFile) async { if (failScreenshot) { throw Exception(); } outputFile.writeAsBytesSync(List.generate(1024, (int i) => i)); } @override FutureOr getLogReader({ ApplicationPackage? app, bool includePastLogs = false, }) => NoOpDeviceLogReader(name); @override DevicePortForwarder portForwarder = const NoOpDevicePortForwarder(); } class FakeDevFS extends Fake implements DevFS { @override DateTime? lastCompiled = DateTime(2000); @override PackageConfig? lastPackageConfig = PackageConfig.empty; @override List sources = []; @override Uri baseUri = Uri(); @override Future destroy() async { } @override Set assetPathsToEvict = {}; @override Set shaderPathsToEvict = {}; @override Set scenePathsToEvict = {}; @override bool didUpdateFontManifest = false; UpdateFSReport nextUpdateReport = UpdateFSReport(success: true); @override bool hasSetAssetDirectory = false; @override Future create() async { return Uri(); } @override void resetLastCompiled() { lastCompiled = null; } @override Future update({ required Uri mainUri, required ResidentCompiler generator, required bool trackWidgetCreation, required String pathToReload, required List invalidatedFiles, required PackageConfig packageConfig, required String dillOutputPath, required DevelopmentShaderCompiler shaderCompiler, DevelopmentSceneImporter? sceneImporter, DevFSWriter? devFSWriter, String? target, AssetBundle? bundle, bool bundleFirstUpload = false, bool fullRestart = false, String? projectRootPath, File? dartPluginRegistrant, }) async { return nextUpdateReport; } } class FakeShaderCompiler implements DevelopmentShaderCompiler { const FakeShaderCompiler(); @override void configureCompiler(TargetPlatform? platform) { } @override Future recompileShader(DevFSContent inputShader) { throw UnimplementedError(); } }