// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file // for details. 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:dwds/asset_reader.dart'; import 'package:dwds/src/debugging/chrome_inspector.dart'; import 'package:dwds/src/debugging/dart_runtime_debugger.dart'; import 'package:dwds/src/debugging/execution_context.dart'; import 'package:dwds/src/debugging/inspector.dart'; import 'package:dwds/src/debugging/instance.dart'; import 'package:dwds/src/debugging/metadata/provider.dart'; import 'package:dwds/src/debugging/modules.dart'; import 'package:dwds/src/debugging/remote_debugger.dart'; import 'package:dwds/src/debugging/webkit_debugger.dart'; import 'package:dwds/src/handlers/socket_connections.dart'; import 'package:dwds/src/loaders/require.dart'; import 'package:dwds/src/loaders/strategy.dart'; import 'package:dwds/src/services/expression_compiler.dart'; import 'package:dwds/src/utilities/objects.dart'; import 'package:shelf/shelf.dart' as shelf; import 'package:vm_service/vm_service.dart'; /// A library of fake/stub implementations of our classes and their supporting /// classes (e.g. WipConnection) for unit testing. import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'; import 'debugger_data.dart'; import 'utilities.dart'; /// Constructs a trivial Isolate we can use when we need to provide one but /// don't want go through initialization. Isolate get simpleIsolate => Isolate( id: '1', number: '1', name: 'fake', libraries: [], exceptionPauseMode: 'abc', breakpoints: [], pauseOnExit: false, pauseEvent: null, startTime: 0, livePorts: 0, runnable: false, isSystemIsolate: false, isolateFlags: [], ); class FakeChromeAppInspector extends FakeInspector implements ChromeAppInspector { FakeChromeAppInspector(this._remoteDebugger, {required super.fakeIsolate}); final WebkitDebugger _remoteDebugger; @override Future callFunction( String function, Iterable argumentIds, ) async { functionsCalled.add(function); return RemoteObject({'type': 'string', 'value': 'true'}); } @override Future instanceRefFor(Object value) async => ChromeAppInstanceHelper.kNullInstanceRef; @override Future getObject(String objectId, {int? offset, int? count}) async => Obj.parse({})!; @override Future> getProperties( String objectId, { int? offset, int? count, int? length, }) async { final response = await _remoteDebugger.sendCommand( 'Runtime.getProperties', params: {'objectId': objectId, 'ownProperties': true}, ); final result = response.result?['result']; return result .map((each) => Property(each as Map)) .toList(); } @override bool isDisplayableObject(Object? object) => true; @override bool isNativeJsError(InstanceRef instanceRef) => false; } class FakeInspector implements AppInspector { final List functionsCalled = []; FakeInspector({required this.fakeIsolate}); Isolate fakeIsolate; @override Object noSuchMethod(Invocation invocation) { throw UnsupportedError('This is a fake'); } @override Future initialize({ModifiedModuleReport? modifiedModuleReport}) async {} @override Future getScripts() async => ScriptList(scripts: []); @override Future scriptRefFor(String uri) async => ScriptRef(id: 'fake', uri: 'fake://uri'); @override ScriptRef? scriptWithId(String? scriptId) => null; @override Isolate get isolate => fakeIsolate; @override IsolateRef get isolateRef => IsolateRef( id: fakeIsolate.id, number: fakeIsolate.number, name: fakeIsolate.name, isSystemIsolate: fakeIsolate.isSystemIsolate, ); } class FakeSseConnection implements SseSocketConnection { /// A [StreamController] for incoming messages on SSE connection. final controllerIncoming = StreamController(); /// A [StreamController] for outgoing messages on SSE connection. final controllerOutgoing = StreamController(); @override bool get isInKeepAlivePeriod => false; @override StreamSink get sink => controllerOutgoing.sink; @override Stream get stream => controllerIncoming.stream; @override void shutdown() {} } class FakeModules implements Modules { final String _library; final String _module; final String _path; FakeModules({ String library = 'main.dart', String module = 'main', String path = 'web/main.dart', }) : _library = library, _module = module, _path = path; @override Future initialize( String entrypoint, { ModifiedModuleReport? modifiedModuleReport, }) async {} @override Future libraryForSource(String serverPath) async => Uri(path: _library); @override Future libraryOrPartForSource(String serverPath) async => Uri(path: _library); @override Future moduleForSource(String serverPath) async => _module; @override Future> sourcesForModule(String module) async => {_path}; @override Future> modules() async => {_path: _module}; @override Future moduleForLibrary(String libraryUri) async => _module; @override Future getRuntimeScriptIdForModule( String entrypoint, String module, ) async => null; } class FakeWebkitDebugger implements WebkitDebugger { final Map? _scripts; @override Future disable() async => null; @override Future enable() async => null; FakeWebkitDebugger({Map? scripts}) : _scripts = scripts { final buildSettings = TestBuildSettings.dart( appEntrypoint: Uri.parse('package:fakeapp/main.dart'), ); setGlobalsForTesting( toolConfiguration: TestToolConfiguration.withLoadStrategy( loadStrategy: RequireStrategy( ReloadConfiguration.none, (_) async => {}, (_) async => {}, (_, __) async => null, (MetadataProvider _, String __) async => '', (MetadataProvider _, String __) async => '', (String _) => '', (MetadataProvider _) async => {}, FakeAssetReader(), buildSettings, ), ), ); } @override Stream eventStream(String method, WipEventTransformer transformer) => Stream.empty(); @override Future getScriptSource(String scriptId) async => ''; Stream? get onClosed => null; @override Stream get onGlobalObjectCleared => Stream.empty(); @override late Stream onPaused; @override Stream get onResumed => Stream.empty(); @override Stream get onScriptParsed => Stream.empty(); @override Stream get onTargetCrashed => Stream.empty(); @override Future pause() async => fakeWipResponse; @override Future resume() async => fakeWipResponse; @override Map get scripts => _scripts!; List results = variables1; int resultsReturned = 0; @override Future sendCommand( String command, { Map? params, }) async { // Force the results that we expect for looking up the variables. if (command == 'Runtime.getProperties') { return results[resultsReturned++]; } return fakeWipResponse; } @override Future setPauseOnExceptions(PauseState state) async => fakeWipResponse; @override Future removeBreakpoint(String breakpointId) async => fakeWipResponse; @override Future stepInto({Map? params}) async => fakeWipResponse; @override Future stepOut() async => fakeWipResponse; @override Future stepOver({Map? params}) async => fakeWipResponse; @override Stream get onConsoleAPICalled => Stream.empty(); @override Stream get onExceptionThrown => Stream.empty(); @override Future close() async {} @override Stream get onClose => Stream.empty(); @override Future evaluate( String expression, { bool? returnByValue, int? contextId, }) async => RemoteObject({}); @override Future evaluateOnCallFrame( String callFrameId, String expression, ) async { return RemoteObject({}); } @override Future> getPossibleBreakpoints( WipLocation start, ) async => []; @override Future enablePage() async => fakeWipResponse; @override Future pageReload() async => fakeWipResponse; @override Future Function()? onReconnect; } /// Fake execution context that is needed for id only class FakeExecutionContext extends ExecutionContext { @override Future get id async { return 0; } FakeExecutionContext(); } class FakeStrategy extends LoadStrategy { final BuildSettings _buildSettings; FakeStrategy( super.assetReader, { super.packageConfigPath, BuildSettings? buildSettings, }) : _buildSettings = buildSettings ?? TestBuildSettings.dart( appEntrypoint: Uri.parse('package:myapp/main.dart'), ); @override Future bootstrapFor(String entrypoint) async => 'dummy_bootstrap'; @override shelf.Handler get handler => (request) => (request.url.path == 'someDummyPath') ? shelf.Response.ok('some dummy response') : shelf.Response.notFound('someDummyPath'); @override BuildSettings get buildSettings => _buildSettings; @override String get id => 'dummy-id'; @override String get moduleFormat => 'dummy-format'; @override String get loadLibrariesModule => ''; @override String get loadModuleSnippet => ''; @override late final DartRuntimeDebugger dartRuntimeDebugger = DartRuntimeDebugger( loadStrategy: this, useLibraryBundleExpression: false, ); @override ReloadConfiguration get reloadConfiguration => ReloadConfiguration.none; @override String? g3RelativePath(String absolutePath) => null; @override String loadClientSnippet(String clientScript) => 'dummy-load-client-snippet'; @override Future moduleForServerPath( String entrypoint, String serverPath, ) async => ''; @override Future serverPathForModule(String entrypoint, String module) async => ''; @override Future sourceMapPathForModule( String entrypoint, String module, ) async => ''; @override String? serverPathForAppUri(String appUri) => ''; @override MetadataProvider metadataProviderFor(String entrypoint) => createProvider(entrypoint, FakeAssetReader()); @override Future> moduleInfoForEntrypoint(String entrypoint) => throw UnimplementedError(); } class FakeAssetReader implements AssetReader { String? metadata; final String? _dartSource; final String? _sourceMap; FakeAssetReader({this.metadata, dartSource, sourceMap}) : _dartSource = dartSource, _sourceMap = sourceMap; @override String get basePath => ''; @override Future dartSourceContents(String serverPath) { return _throwUnimplementedOrReturnContents(_dartSource); } @override Future metadataContents(String serverPath) { return _throwUnimplementedOrReturnContents(metadata); } @override Future sourceMapContents(String serverPath) { return _throwUnimplementedOrReturnContents(_sourceMap); } @override Future close() async {} Future _throwUnimplementedOrReturnContents(String? contents) async { if (contents == null) throw UnimplementedError(); return contents; } } class FakeExpressionCompiler implements ExpressionCompiler { @override Future compileExpressionToJs( String isolateId, String libraryUri, String scriptUri, int line, int column, Map jsModules, Map jsFrameValues, String moduleName, String expression, ) async => ExpressionCompilationResult(expression, false); @override Future updateDependencies(Map modules) async => true; @override Future initialize(CompilerOptions options) async {} } final fakeWipResponse = WipResponse({ 'id': 1, 'result': {'fake': ''}, }); final fakeFailingWipResponse = WipResponse({'result': 'Error: Bad request'});