From 370300ffd7d8fa14767343055ac239f5cb5e6471 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Wed, 8 Nov 2023 10:17:22 -0800 Subject: [PATCH 1/5] Make dwds tolerant to failure of detecting dart execution context id --- .../lib/src/connections/debug_connection.dart | 2 +- dwds/lib/src/debugging/execution_context.dart | 35 ++- dwds/lib/src/debugging/remote_debugger.dart | 2 +- dwds/lib/src/debugging/webkit_debugger.dart | 2 +- dwds/lib/src/dwds_vm_client.dart | 15 +- dwds/lib/src/servers/extension_debugger.dart | 2 +- dwds/test/execution_context_test.dart | 209 ++++++++++++++++++ dwds/test/fixtures/fakes.dart | 2 +- 8 files changed, 251 insertions(+), 18 deletions(-) create mode 100644 dwds/test/execution_context_test.dart diff --git a/dwds/lib/src/connections/debug_connection.dart b/dwds/lib/src/connections/debug_connection.dart index 97de4c448..8ecaf9985 100644 --- a/dwds/lib/src/connections/debug_connection.dart +++ b/dwds/lib/src/connections/debug_connection.dart @@ -37,7 +37,7 @@ class DebugConnection { VmService get vmService => _appDebugServices.dwdsVmClient.client; Future close() => _closed ??= () async { - _appDebugServices.chromeProxyService.remoteDebugger.close(); + await _appDebugServices.chromeProxyService.remoteDebugger.close(); await _appDebugServices.close(); _onDoneCompleter.complete(); }(); diff --git a/dwds/lib/src/debugging/execution_context.dart b/dwds/lib/src/debugging/execution_context.dart index a65e582a6..71b84fb8e 100644 --- a/dwds/lib/src/debugging/execution_context.dart +++ b/dwds/lib/src/debugging/execution_context.dart @@ -9,8 +9,9 @@ import 'package:dwds/src/debugging/remote_debugger.dart'; import 'package:logging/logging.dart'; abstract class ExecutionContext { - /// Returns the context ID that contains the running Dart application. - Future get id; + /// Returns the context ID that contains the running Dart application, + /// if available. + Future get id; } /// The execution context in which to do remote evaluations. @@ -23,12 +24,12 @@ class RemoteDebuggerExecutionContext extends ExecutionContext { /// Context can be null if an error has occurred and we cannot detect /// and parse the context ID. late StreamQueue _contexts; + final _contextController = StreamController(); + final _seenContexts = []; int? _id; - @override - Future get id async { - if (_id != null) return _id!; + Future _lookUpId() async { _logger.fine('Looking for Dart execution context...'); const timeoutInMs = 100; while (await _contexts.hasNext.timeout( @@ -41,6 +42,7 @@ class RemoteDebuggerExecutionContext extends ExecutionContext { }, )) { final context = await _contexts.next; + _seenContexts.add(context); _logger.fine('Checking context id: $context'); try { final result = await _remoteDebugger.sendCommand( @@ -52,8 +54,7 @@ class RemoteDebuggerExecutionContext extends ExecutionContext { ); if (result.result?['result']?['value'] != null) { _logger.fine('Found valid execution context: $context'); - _id = context; - break; + return context; } } catch (_) { // Errors may be thrown if we attempt to evaluate in a stale a context. @@ -61,14 +62,24 @@ class RemoteDebuggerExecutionContext extends ExecutionContext { _logger.fine('Invalid execution context: $context'); } } + return null; + } + + @override + Future get id async { + if (_id != null) return _id!; + _id = await _lookUpId(); + if (_id == null) { - throw StateError('No context with the running Dart application.'); + // Add seen contexts back to the queue in case the injected + // client is still loading, so the next call to `id` succeeds. + _seenContexts.forEach(_contextController.add); + _seenContexts.clear(); } - return _id!; + return _id; } RemoteDebuggerExecutionContext(this._id, this._remoteDebugger) { - final contextController = StreamController(); _remoteDebugger .eventStream('Runtime.executionContextsCleared', (e) => e) .listen((_) => _id = null); @@ -86,8 +97,8 @@ class RemoteDebuggerExecutionContext extends ExecutionContext { } return parsedId; }).listen((e) { - if (e != null) contextController.add(e); + if (e != null) _contextController.add(e); }); - _contexts = StreamQueue(contextController.stream); + _contexts = StreamQueue(_contextController.stream); } } diff --git a/dwds/lib/src/debugging/remote_debugger.dart b/dwds/lib/src/debugging/remote_debugger.dart index e181eb4fb..48657ce81 100644 --- a/dwds/lib/src/debugging/remote_debugger.dart +++ b/dwds/lib/src/debugging/remote_debugger.dart @@ -72,5 +72,5 @@ abstract class RemoteDebugger { Stream eventStream(String method, WipEventTransformer transformer); - void close(); + Future close(); } diff --git a/dwds/lib/src/debugging/webkit_debugger.dart b/dwds/lib/src/debugging/webkit_debugger.dart index 1804abadb..23bbd0c8f 100644 --- a/dwds/lib/src/debugging/webkit_debugger.dart +++ b/dwds/lib/src/debugging/webkit_debugger.dart @@ -32,7 +32,7 @@ class WebkitDebugger implements RemoteDebugger { _wipDebugger.sendCommand(command, params: params); @override - void close() => _closed ??= _wipDebugger.connection.close(); + Future close() => _closed ??= _wipDebugger.connection.close(); @override Future disable() => _wipDebugger.disable(); diff --git a/dwds/lib/src/dwds_vm_client.dart b/dwds/lib/src/dwds_vm_client.dart index c1ebeab60..145b7354a 100644 --- a/dwds/lib/src/dwds_vm_client.dart +++ b/dwds/lib/src/dwds_vm_client.dart @@ -213,6 +213,19 @@ void _recordDwdsStats(DwdsStats dwdsStats, String screen) { } } +Future tryGetContextId( + ChromeProxyService chromeProxyService, { + int retries = 3, +}) async { + const waitInMs = 50; + for (var retry = 0; retry < retries; retry++) { + final tryId = await chromeProxyService.executionContext.id; + if (tryId != null) return tryId; + await Future.delayed(const Duration(milliseconds: waitInMs)); + } + throw StateError('No context with the running Dart application.'); +} + Future> _hotRestart( ChromeProxyService chromeProxyService, VmService client, @@ -223,7 +236,7 @@ Future> _hotRestart( await _disableBreakpointsAndResume(client, chromeProxyService); try { _logger.info('Attempting to get execution context ID.'); - await chromeProxyService.executionContext.id; + await tryGetContextId(chromeProxyService); _logger.info('Got execution context ID.'); } on StateError catch (e) { // We couldn't find the execution context. `hotRestart` may have been diff --git a/dwds/lib/src/servers/extension_debugger.dart b/dwds/lib/src/servers/extension_debugger.dart index 8dda548b8..6c0736198 100644 --- a/dwds/lib/src/servers/extension_debugger.dart +++ b/dwds/lib/src/servers/extension_debugger.dart @@ -192,7 +192,7 @@ class ExtensionDebugger implements RemoteDebugger { int newId() => _completerId++; @override - void close() => _closed ??= () { + Future close() => _closed ??= () { _closeController.add({}); return Future.wait([ sseConnection.sink.close(), diff --git a/dwds/test/execution_context_test.dart b/dwds/test/execution_context_test.dart new file mode 100644 index 000000000..47f756e84 --- /dev/null +++ b/dwds/test/execution_context_test.dart @@ -0,0 +1,209 @@ +// Copyright (c) 2023, 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. + +@Timeout(Duration(minutes: 2)) +import 'dart:async'; +import 'dart:convert'; + +import 'package:dwds/data/devtools_request.dart'; +import 'package:dwds/data/extension_request.dart'; +import 'package:dwds/data/serializers.dart'; +import 'package:dwds/src/debugging/execution_context.dart'; +import 'package:dwds/src/servers/extension_debugger.dart'; +import 'package:test/test.dart'; +import 'package:test_common/logging.dart'; + +import 'fixtures/fakes.dart'; + +void main() async { + final bool debug = false; + + group('ExecutionContext', () { + setUpAll(() { + setCurrentLogWriter(debug: debug); + }); + + TestDebuggerConnection? debugger; + TestDebuggerConnection getDebugger() => debugger!; + + setUp(() async { + setCurrentLogWriter(debug: debug); + debugger = TestDebuggerConnection(); + }); + + tearDown(() async { + await debugger?.close(); + }); + + test('is created on devtools request', () async { + final debugger = getDebugger(); + await debugger.createDebuggerExecutionContext(contextId: 1); + + // Expect the context ID to be set. + expect(await debugger.getDefaultContextId(), 1); + }); + + test('clears context ID', () async { + final debugger = getDebugger(); + await debugger.createDebuggerExecutionContext(contextId: 1); + + debugger.sendContextsClearedEvent(); + + // Expect non-dart context. + expect(await debugger.getDefaultContextId(), isNull); + }); + + test('finds dart context ID', () async { + final debugger = getDebugger(); + + await debugger.createDebuggerExecutionContext(contextId: null); + debugger.sendContextCreatedEvent(2); + + // Expect dart context. + expect(await debugger.getContextId(isDart: true), 2); + }); + + test('does not find dart context ID if not available', () async { + final debugger = getDebugger(); + + await debugger.createDebuggerExecutionContext(contextId: null); + + // No context IDs received yet. + expect(await debugger.getDefaultContextId(), null); + + // Send 'context created' event + final contextId = 2; + debugger.sendContextCreatedEvent(contextId); + + // Expect non-dart context + // This mocks injected client still loading. + expect(await debugger.getContextId(isDart: false), null); + + // Expect dart context. + // This mocks injected client loading later for previously + // received context ID. + expect(await debugger.getContextId(isDart: true), contextId); + }); + }); +} + +class TestDebuggerConnection { + late final ExtensionDebugger extensionDebugger; + late final FakeSseConnection connection; + + int _evaluateRequestId = 0; + + TestDebuggerConnection() { + connection = FakeSseConnection(); + extensionDebugger = ExtensionDebugger(connection); + } + + /// Create a new execution context in the debugger. + Future createDebuggerExecutionContext({int? contextId}) { + _sendDevToolsRequest(contextId: contextId); + return _executionContext(); + } + + /// Flush the streams and close debugger connection. + Future close() async { + unawaited(connection.controllerOutgoing.stream.any((e) => false)); + unawaited(extensionDebugger.devToolsRequestStream.any((e) => false)); + + await connection.controllerIncoming.sink.close(); + await connection.controllerOutgoing.sink.close(); + + await extensionDebugger.close(); + } + + /// Return the initial context ID from the DevToolsRequest. + Future getDefaultContextId() async { + // Give the previous events time to propagate. + await Future.delayed(Duration(milliseconds: 100)); + return await extensionDebugger.executionContext!.id; + } + + /// Mock receiving context IDs in the execution context. + /// + /// [isDart] will cause a dart context to be created. + /// + /// Dart context is detected by evaluation of + /// `window.$dartAppInstanceId` in that context returning + /// a non-null value. + Future getContextId({bool isDart = false}) async { + // Try getting execution id. + final executionContextId = extensionDebugger.executionContext!.id; + + // Give it time to send the evaluate request. + await Future.delayed(Duration(milliseconds: 100)); + + // Respond to the evaluate request + final extensionResponse = ExtensionResponse( + (b) => b + ..result = jsonEncode({ + 'result': {'value': isDart ? 'dart' : null}, + }) + ..id = _evaluateRequestId++ + ..success = true, + ); + connection.controllerIncoming.sink + .add(jsonEncode(serializers.serialize(extensionResponse))); + + return await executionContextId; + } + + /// Send `Runtime.executionContextsCleared` event to the execution + /// context in the extension debugger. + void sendContextsClearedEvent() { + final extensionEvent = ExtensionEvent( + (b) => b + ..method = jsonEncode('Runtime.executionContextsCleared') + ..params = jsonEncode({}), + ); + connection.controllerIncoming.sink + .add(jsonEncode(serializers.serialize(extensionEvent))); + } + + /// Send `Runtime.executionContextCreated` event to the execution + /// context in the extension debugger. + void sendContextCreatedEvent(int contextId) { + final extensionEvent = ExtensionEvent( + (b) => b + ..method = jsonEncode('Runtime.executionContextCreated') + ..params = jsonEncode({ + "context": {"id": "$contextId"}, + }), + ); + connection.controllerIncoming.sink + .add(jsonEncode(serializers.serialize(extensionEvent))); + } + + void _sendDevToolsRequest({int? contextId}) { + final devToolsRequest = DevToolsRequest( + (b) => b + ..contextId = contextId + ..appId = 'app' + ..instanceId = '0', + ); + connection.controllerIncoming.sink + .add(jsonEncode(serializers.serialize(devToolsRequest))); + } + + Future _executionContext() async { + final executionContext = await _waitForExecutionContext().timeout( + const Duration(milliseconds: 100), + onTimeout: () { + expect(fail, 'Timeout getting execution context'); + return null; + }, + ); + expect(executionContext, isNotNull); + } + + Future _waitForExecutionContext() async { + while (extensionDebugger.executionContext == null) { + await Future.delayed(Duration(milliseconds: 20)); + } + return extensionDebugger.executionContext; + } +} diff --git a/dwds/test/fixtures/fakes.dart b/dwds/test/fixtures/fakes.dart index 5470108dc..a24381d7a 100644 --- a/dwds/test/fixtures/fakes.dart +++ b/dwds/test/fixtures/fakes.dart @@ -275,7 +275,7 @@ class FakeWebkitDebugger implements WebkitDebugger { Stream get onExceptionThrown => Stream.empty(); @override - void close() {} + Future close() async {} @override Stream get onClose => Stream.empty(); From d5a91a5805f6293469b8d0a59c359be3a708d2d0 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Wed, 8 Nov 2023 11:15:33 -0800 Subject: [PATCH 2/5] Update changelog --- dwds/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/dwds/CHANGELOG.md b/dwds/CHANGELOG.md index 51ca5c848..daf4dcab2 100644 --- a/dwds/CHANGELOG.md +++ b/dwds/CHANGELOG.md @@ -1,5 +1,6 @@ ## 23.0.0-wip - Restructure `LoadStrategy` to provide build settings. - [#2270](https://github.com/dart-lang/webdev/pull/2270) +- Tolerate failures to detect a dart execution context. - [#2286](https://github.com/dart-lang/webdev/pull/2286) ## 22.1.0 - Update `package:vm_service` constraint to `^13.0.0`. - [#2265](https://github.com/dart-lang/webdev/pull/2265) From 22ba56fb7ada26075041a96130bd81d19c4a5f55 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Wed, 8 Nov 2023 16:50:34 -0800 Subject: [PATCH 3/5] Addressed CR comments --- dwds/lib/src/debugging/execution_context.dart | 9 +- dwds/test/execution_context_test.dart | 172 +++++++++++++----- 2 files changed, 136 insertions(+), 45 deletions(-) diff --git a/dwds/lib/src/debugging/execution_context.dart b/dwds/lib/src/debugging/execution_context.dart index 71b84fb8e..978bd845e 100644 --- a/dwds/lib/src/debugging/execution_context.dart +++ b/dwds/lib/src/debugging/execution_context.dart @@ -53,13 +53,14 @@ class RemoteDebuggerExecutionContext extends ExecutionContext { }, ); if (result.result?['result']?['value'] != null) { - _logger.fine('Found valid execution context: $context'); + _logger.fine('Found dart execution context: $context'); return context; } } catch (_) { // Errors may be thrown if we attempt to evaluate in a stale a context. // Ignore and continue. - _logger.fine('Invalid execution context: $context'); + _logger.fine('Stale execution context: $context'); + _seenContexts.remove(context); } } return null; @@ -67,9 +68,9 @@ class RemoteDebuggerExecutionContext extends ExecutionContext { @override Future get id async { - if (_id != null) return _id!; - _id = await _lookUpId(); + if (_id != null) return _id; + _id = await _lookUpId(); if (_id == null) { // Add seen contexts back to the queue in case the injected // client is still loading, so the next call to `id` succeeds. diff --git a/dwds/test/execution_context_test.dart b/dwds/test/execution_context_test.dart index 47f756e84..fa8646350 100644 --- a/dwds/test/execution_context_test.dart +++ b/dwds/test/execution_context_test.dart @@ -13,6 +13,7 @@ import 'package:dwds/src/debugging/execution_context.dart'; import 'package:dwds/src/servers/extension_debugger.dart'; import 'package:test/test.dart'; import 'package:test_common/logging.dart'; +import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'; import 'fixtures/fakes.dart'; @@ -38,70 +39,136 @@ void main() async { test('is created on devtools request', () async { final debugger = getDebugger(); - await debugger.createDebuggerExecutionContext(contextId: 1); + await debugger.createDebuggerExecutionContext(TestContextId.dartDefault); // Expect the context ID to be set. - expect(await debugger.getDefaultContextId(), 1); + expect(await debugger.defaultContextId(), TestContextId.dartDefault); }); test('clears context ID', () async { final debugger = getDebugger(); - await debugger.createDebuggerExecutionContext(contextId: 1); + await debugger.createDebuggerExecutionContext(TestContextId.dartDefault); debugger.sendContextsClearedEvent(); // Expect non-dart context. - expect(await debugger.getDefaultContextId(), isNull); + expect(await debugger.defaultContextId(), TestContextId.none); }); test('finds dart context ID', () async { final debugger = getDebugger(); + await debugger.createDebuggerExecutionContext(TestContextId.none); - await debugger.createDebuggerExecutionContext(contextId: null); - debugger.sendContextCreatedEvent(2); + debugger.sendContextCreatedEvent(TestContextId.dartNormal); // Expect dart context. - expect(await debugger.getContextId(isDart: true), 2); + expect(await debugger.dartContextId(), TestContextId.dartNormal); }); test('does not find dart context ID if not available', () async { final debugger = getDebugger(); - - await debugger.createDebuggerExecutionContext(contextId: null); + await debugger.createDebuggerExecutionContext(TestContextId.none); // No context IDs received yet. - expect(await debugger.getDefaultContextId(), null); + expect(await debugger.defaultContextId(), TestContextId.none); - // Send 'context created' event - final contextId = 2; - debugger.sendContextCreatedEvent(contextId); + debugger.sendContextCreatedEvent(TestContextId.dartLate); - // Expect non-dart context + // Expect no dart context. // This mocks injected client still loading. - expect(await debugger.getContextId(isDart: false), null); + expect(await debugger.noContextId(), TestContextId.none); // Expect dart context. // This mocks injected client loading later for previously // received context ID. - expect(await debugger.getContextId(isDart: true), contextId); + expect(await debugger.dartContextId(), TestContextId.dartLate); + }); + + test('works with stale contexts', () async { + final debugger = getDebugger(); + await debugger.createDebuggerExecutionContext(TestContextId.none); + + debugger.sendContextCreatedEvent(TestContextId.stale); + debugger.sendContextsClearedEvent(); + + // Expect no dart context. + // This mocks the previous context going stale. + expect(await debugger.noContextId(), TestContextId.none); + + debugger.sendContextCreatedEvent(TestContextId.dartNormal); + + // Expect dart context. + expect(await debugger.dartContextId(), TestContextId.dartNormal); }); }); } +class TestExtensionDebugger extends ExtensionDebugger { + TestExtensionDebugger(FakeSseConnection super.sseConnection); + + @override + Future sendCommand( + String command, { + Map? params, + }) { + final id = params?['contextId']; + final response = super.sendCommand(command, params: params); + + /// Mock stale contexts that cause the evaluation to throw. + if (command == 'Runtime.evaluate' && + TestContextId.from(id) == TestContextId.stale) { + throw Exception('Stale execution context'); + } + return response; + } +} + +enum TestContextId { + none, + dartDefault, + dartNormal, + dartLate, + nonDart, + stale; + + factory TestContextId.from(int? value) { + return switch (value) { + null => none, + 0 => dartDefault, + 1 => dartNormal, + 2 => dartLate, + 3 => nonDart, + 4 => stale, + _ => throw StateError('$value is not a TestContextId'), + }; + } + + int? get id { + return switch (this) { + none => null, + dartDefault => 0, + dartNormal => 1, + dartLate => 2, + nonDart => 3, + stale => 4, + }; + } +} + class TestDebuggerConnection { - late final ExtensionDebugger extensionDebugger; + late final TestExtensionDebugger extensionDebugger; late final FakeSseConnection connection; int _evaluateRequestId = 0; TestDebuggerConnection() { connection = FakeSseConnection(); - extensionDebugger = ExtensionDebugger(connection); + extensionDebugger = TestExtensionDebugger(connection); } /// Create a new execution context in the debugger. - Future createDebuggerExecutionContext({int? contextId}) { - _sendDevToolsRequest(contextId: contextId); + Future createDebuggerExecutionContext(TestContextId contextId) { + _sendDevToolsRequest(contextId: contextId.id); return _executionContext(); } @@ -117,39 +184,50 @@ class TestDebuggerConnection { } /// Return the initial context ID from the DevToolsRequest. - Future getDefaultContextId() async { + Future defaultContextId() async { // Give the previous events time to propagate. await Future.delayed(Duration(milliseconds: 100)); - return await extensionDebugger.executionContext!.id; + return TestContextId.from(await extensionDebugger.executionContext!.id); } - /// Mock receiving context IDs in the execution context. + /// Mock receiving dart context ID in the execution context. /// - /// [isDart] will cause a dart context to be created. - /// - /// Dart context is detected by evaluation of + /// Note: dart context is detected by evaluation of /// `window.$dartAppInstanceId` in that context returning /// a non-null value. - Future getContextId({bool isDart = false}) async { + Future dartContextId() async { // Try getting execution id. final executionContextId = extensionDebugger.executionContext!.id; // Give it time to send the evaluate request. await Future.delayed(Duration(milliseconds: 100)); - // Respond to the evaluate request - final extensionResponse = ExtensionResponse( - (b) => b - ..result = jsonEncode({ - 'result': {'value': isDart ? 'dart' : null}, - }) - ..id = _evaluateRequestId++ - ..success = true, - ); - connection.controllerIncoming.sink - .add(jsonEncode(serializers.serialize(extensionResponse))); + // Respond to the evaluate request. + _sendEvaluationResponse({ + 'result': {'value': 'dart'}, + }); + + return TestContextId.from(await executionContextId); + } + + /// Mock receiving non-dart context ID in the execution context. + /// + /// Note: dart context is detected by evaluation of + /// `window.$dartAppInstanceId` in that context returning + /// a null value. + Future noContextId() async { + // Try getting execution id. + final executionContextId = extensionDebugger.executionContext!.id; + + // Give it time to send the evaluate request. + await Future.delayed(Duration(milliseconds: 100)); + + // Respond to the evaluate request. + _sendEvaluationResponse({ + 'result': {'value': null}, + }); - return await executionContextId; + return TestContextId.from(await executionContextId); } /// Send `Runtime.executionContextsCleared` event to the execution @@ -166,18 +244,30 @@ class TestDebuggerConnection { /// Send `Runtime.executionContextCreated` event to the execution /// context in the extension debugger. - void sendContextCreatedEvent(int contextId) { + void sendContextCreatedEvent(TestContextId contextId) { final extensionEvent = ExtensionEvent( (b) => b ..method = jsonEncode('Runtime.executionContextCreated') ..params = jsonEncode({ - "context": {"id": "$contextId"}, + 'context': {'id': '${contextId.id}'}, }), ); connection.controllerIncoming.sink .add(jsonEncode(serializers.serialize(extensionEvent))); } + void _sendEvaluationResponse(Map response) { + // Respond to the evaluate request. + final extensionResponse = ExtensionResponse( + (b) => b + ..result = jsonEncode(response) + ..id = _evaluateRequestId++ + ..success = true, + ); + connection.controllerIncoming.sink + .add(jsonEncode(serializers.serialize(extensionResponse))); + } + void _sendDevToolsRequest({int? contextId}) { final devToolsRequest = DevToolsRequest( (b) => b From 850a1808da767e3c0b6ae449fd83020b5f066ccf Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Wed, 8 Nov 2023 16:52:23 -0800 Subject: [PATCH 4/5] Cleanup --- dwds/test/execution_context_test.dart | 40 +++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/dwds/test/execution_context_test.dart b/dwds/test/execution_context_test.dart index fa8646350..1be5c4619 100644 --- a/dwds/test/execution_context_test.dart +++ b/dwds/test/execution_context_test.dart @@ -103,26 +103,6 @@ void main() async { }); } -class TestExtensionDebugger extends ExtensionDebugger { - TestExtensionDebugger(FakeSseConnection super.sseConnection); - - @override - Future sendCommand( - String command, { - Map? params, - }) { - final id = params?['contextId']; - final response = super.sendCommand(command, params: params); - - /// Mock stale contexts that cause the evaluation to throw. - if (command == 'Runtime.evaluate' && - TestContextId.from(id) == TestContextId.stale) { - throw Exception('Stale execution context'); - } - return response; - } -} - enum TestContextId { none, dartDefault, @@ -155,6 +135,26 @@ enum TestContextId { } } +class TestExtensionDebugger extends ExtensionDebugger { + TestExtensionDebugger(FakeSseConnection super.sseConnection); + + @override + Future sendCommand( + String command, { + Map? params, + }) { + final id = params?['contextId']; + final response = super.sendCommand(command, params: params); + + /// Mock stale contexts that cause the evaluation to throw. + if (command == 'Runtime.evaluate' && + TestContextId.from(id) == TestContextId.stale) { + throw Exception('Stale execution context'); + } + return response; + } +} + class TestDebuggerConnection { late final TestExtensionDebugger extensionDebugger; late final FakeSseConnection connection; From 0b6229db14b07ca048e6c79d7899492f23355375 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Wed, 8 Nov 2023 17:00:52 -0800 Subject: [PATCH 5/5] Cleanup --- dwds/test/execution_context_test.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dwds/test/execution_context_test.dart b/dwds/test/execution_context_test.dart index 1be5c4619..90d961d1b 100644 --- a/dwds/test/execution_context_test.dart +++ b/dwds/test/execution_context_test.dart @@ -89,12 +89,11 @@ void main() async { await debugger.createDebuggerExecutionContext(TestContextId.none); debugger.sendContextCreatedEvent(TestContextId.stale); - debugger.sendContextsClearedEvent(); // Expect no dart context. - // This mocks the previous context going stale. expect(await debugger.noContextId(), TestContextId.none); + debugger.sendContextsClearedEvent(); debugger.sendContextCreatedEvent(TestContextId.dartNormal); // Expect dart context.