From 1771c5523881b3752faf7537aaee01785bc8ad6a Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Mon, 4 Apr 2022 22:07:21 +0000 Subject: [PATCH] [ DDS ] Split DevTools server tests into smaller pieces Should resolve timeout issues, particularly around server connection tests Change-Id: Ic06d1179b09a17497e38de9aa19549f66ff79d2c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/240180 Reviewed-by: Kenzie Davisson --- pkg/dds/test/devtools_server/bind_test.dart | 51 +++ ...ools_client_test.dart => client_test.dart} | 0 .../devtools_server_connection_test.dart | 103 ----- .../devtools_server/devtools_server_test.dart | 369 ------------------ .../test/devtools_server/embedding_test.dart | 44 +++ .../fixtures/empty_dart_app.dart | 0 .../devtools_server/instance_reuse_test.dart | 150 +++++++ .../devtools_server/remote_control_test.dart | 82 ++++ .../server_connection_api_test.dart | 9 + .../server_connection_common.dart | 101 +++++ .../server_connection_vm_service_test.dart | 9 + .../service_registration_test.dart | 36 ++ .../{ => utils}/serve_devtools.dart | 5 +- .../server_driver.dart} | 9 +- pkg/pkg.status | 2 - 15 files changed, 490 insertions(+), 480 deletions(-) create mode 100644 pkg/dds/test/devtools_server/bind_test.dart rename pkg/dds/test/devtools_server/{devtools_client_test.dart => client_test.dart} (100%) delete mode 100644 pkg/dds/test/devtools_server/devtools_server_connection_test.dart delete mode 100644 pkg/dds/test/devtools_server/devtools_server_test.dart create mode 100644 pkg/dds/test/devtools_server/embedding_test.dart rename pkg/dds/test/{ => devtools_server}/fixtures/empty_dart_app.dart (100%) create mode 100644 pkg/dds/test/devtools_server/instance_reuse_test.dart create mode 100644 pkg/dds/test/devtools_server/remote_control_test.dart create mode 100644 pkg/dds/test/devtools_server/server_connection_api_test.dart create mode 100644 pkg/dds/test/devtools_server/server_connection_common.dart create mode 100644 pkg/dds/test/devtools_server/server_connection_vm_service_test.dart create mode 100644 pkg/dds/test/devtools_server/service_registration_test.dart rename pkg/dds/test/devtools_server/{ => utils}/serve_devtools.dart (75%) rename pkg/dds/test/devtools_server/{devtools_server_driver.dart => utils/server_driver.dart} (97%) diff --git a/pkg/dds/test/devtools_server/bind_test.dart b/pkg/dds/test/devtools_server/bind_test.dart new file mode 100644 index 00000000000..efb13bb0f28 --- /dev/null +++ b/pkg/dds/test/devtools_server/bind_test.dart @@ -0,0 +1,51 @@ +// Copyright 2022 The Chromium 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 'package:test/test.dart'; + +import 'utils/server_driver.dart'; + +late final DevToolsServerTestController testController; + +void main() { + testController = DevToolsServerTestController(); + + setUp(() async { + await testController.setUp(); + }); + + tearDown(() async { + await testController.tearDown(); + }); + + test('can bind to next available port', () async { + final server1 = await DevToolsServerDriver.create(port: 8855); + try { + // Wait for the first server to start up and ensure it got the + // expected port. + final event1 = (await server1.stdout.firstWhere( + (map) => map!['event'] == 'server.started', + ))!; + expect(event1['params']['port'], 8855); + + // Now spawn another requesting the same port and ensure it got the next + // port number. + final server2 = await DevToolsServerDriver.create( + port: 8855, + tryPorts: 2, + ); + try { + final event2 = (await server2.stdout.firstWhere( + (map) => map!['event'] == 'server.started', + ))!; + + expect(event2['params']['port'], 8856); + } finally { + server2.kill(); + } + } finally { + server1.kill(); + } + }, timeout: const Timeout.factor(10)); +} diff --git a/pkg/dds/test/devtools_server/devtools_client_test.dart b/pkg/dds/test/devtools_server/client_test.dart similarity index 100% rename from pkg/dds/test/devtools_server/devtools_client_test.dart rename to pkg/dds/test/devtools_server/client_test.dart diff --git a/pkg/dds/test/devtools_server/devtools_server_connection_test.dart b/pkg/dds/test/devtools_server/devtools_server_connection_test.dart deleted file mode 100644 index 5bb42a84c7a..00000000000 --- a/pkg/dds/test/devtools_server/devtools_server_connection_test.dart +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2022 The Chromium 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 'package:devtools_shared/devtools_test_utils.dart'; -import 'package:test/test.dart'; - -import 'devtools_server_driver.dart'; - -// Note: this test is broken out from devtools_server_test.dart so that the -// tests run faster and we do not have to mark them as slow. - -late final DevToolsServerTestController testController; - -void main() { - testController = DevToolsServerTestController(); - - setUp(() async { - await testController.setUp(); - }); - - tearDown(() async { - await testController.tearDown(); - }); - - for (final bool useVmService in [true, false]) { - group('Server (${useVmService ? 'VM Service' : 'API'})', () { - test( - 'DevTools connects back to server API and registers that it is connected', - () async { - // Register the VM. - await testController.send( - 'vm.register', - {'uri': testController.appFixture.serviceUri.toString()}, - ); - - // Send a request to launch DevTools in a browser. - await testController.sendLaunchDevToolsRequest( - useVmService: useVmService, - ); - - final serverResponse = - await testController.waitForClients(requiredConnectionState: true); - expect(serverResponse, isNotNull); - expect(serverResponse['clients'], hasLength(1)); - expect(serverResponse['clients'][0]['hasConnection'], isTrue); - expect( - serverResponse['clients'][0]['vmServiceUri'], - testController.appFixture.serviceUri.toString(), - ); - }, timeout: const Timeout.factor(10)); - - test('DevTools reports disconnects from a VM', () async { - // Register the VM. - await testController.send( - 'vm.register', - {'uri': testController.appFixture.serviceUri.toString()}, - ); - - // Send a request to launch DevTools in a browser. - await testController.sendLaunchDevToolsRequest( - useVmService: useVmService, - ); - - // Wait for the DevTools to inform server that it's connected. - await testController.waitForClients(requiredConnectionState: true); - - // Terminate the VM. - await testController.appFixture.teardown(); - - // Ensure the client is marked as disconnected. - final serverResponse = await testController.waitForClients( - requiredConnectionState: false, - ); - expect(serverResponse['clients'], hasLength(1)); - expect(serverResponse['clients'][0]['hasConnection'], isFalse); - expect(serverResponse['clients'][0]['vmServiceUri'], isNull); - }, timeout: const Timeout.factor(20)); - - test('server removes clients that disconnect from the API', () async { - final event = await testController.serverStartedEvent.future; - - // Spawn our own Chrome process so we can terminate it. - final devToolsUri = - 'http://${event['params']['host']}:${event['params']['port']}'; - final chrome = await Chrome.locate()!.start(url: devToolsUri); - - // Wait for DevTools to inform server that it's connected. - await testController.waitForClients(); - - // Close the browser, which will disconnect DevTools SSE connection - // back to the server. - chrome.kill(); - - // Await a long delay to wait for the SSE client to close. - await delay(duration: const Duration(seconds: 15)); - - // Ensure the client is completely removed from the list. - await testController.waitForClients(expectNone: true); - }, timeout: const Timeout.factor(20)); - }); - } -} diff --git a/pkg/dds/test/devtools_server/devtools_server_test.dart b/pkg/dds/test/devtools_server/devtools_server_test.dart deleted file mode 100644 index 2a0ce6882a3..00000000000 --- a/pkg/dds/test/devtools_server/devtools_server_test.dart +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright 2022 The Chromium 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:convert'; -import 'dart:io'; - -import 'package:dds/devtools_server.dart'; -import 'package:dds/src/devtools/machine_mode_command_handler.dart'; -import 'package:devtools_shared/devtools_shared.dart'; -import 'package:devtools_shared/devtools_test_utils.dart'; -import 'package:test/test.dart'; - -import 'devtools_server_driver.dart'; - -late final DevToolsServerTestController testController; - -void main() { - testController = DevToolsServerTestController(); - - setUp(() async { - await testController.setUp(); - }); - - tearDown(() async { - await testController.tearDown(); - }); - - test('registers service', () async { - final serverResponse = await testController.send( - 'vm.register', - {'uri': testController.appFixture.serviceUri.toString()}, - ); - expect(serverResponse['success'], isTrue); - - // Expect the VM service to see the launchDevTools service registered. - expect( - testController.registeredServices, - contains(DevToolsServer.launchDevToolsService), - ); - }, timeout: const Timeout.factor(10)); - - test('can bind to next available port', () async { - final server1 = await DevToolsServerDriver.create(port: 8855); - try { - // Wait for the first server to start up and ensure it got the - // expected port. - final event1 = (await server1.stdout.firstWhere( - (map) => map!['event'] == 'server.started', - ))!; - expect(event1['params']['port'], 8855); - - // Now spawn another requesting the same port and ensure it got the next - // port number. - final server2 = await DevToolsServerDriver.create( - port: 8855, - tryPorts: 2, - ); - try { - final event2 = (await server2.stdout.firstWhere( - (map) => map!['event'] == 'server.started', - ))!; - - expect(event2['params']['port'], 8856); - } finally { - server2.kill(); - } - } finally { - server1.kill(); - } - }, timeout: const Timeout.factor(10)); - - test('allows embedding without flag', () async { - final server = await DevToolsServerDriver.create(); - final httpClient = HttpClient(); - late HttpClientResponse resp; - try { - final startedEvent = (await server.stdout.firstWhere( - (map) => map!['event'] == 'server.started', - ))!; - final host = startedEvent['params']['host']; - final port = startedEvent['params']['port']; - - final req = await httpClient.get(host, port, '/'); - resp = await req.close(); - expect(resp.headers.value('x-frame-options'), isNull); - } finally { - httpClient.close(); - await resp.drain(); - server.kill(); - } - }, timeout: const Timeout.factor(10)); - - test('does not allow embedding with flag', () async { - final server = await DevToolsServerDriver.create( - additionalArgs: ['--no-allow-embedding'], - ); - final httpClient = HttpClient(); - late HttpClientResponse resp; - try { - final startedEvent = (await server.stdout.firstWhere( - (map) => map!['event'] == 'server.started', - ))!; - final host = startedEvent['params']['host']; - final port = startedEvent['params']['port']; - - final req = await httpClient.get(host, port, '/'); - resp = await req.close(); - expect(resp.headers.value('x-frame-options'), 'SAMEORIGIN'); - } finally { - httpClient.close(); - await resp.drain(); - server.kill(); - } - }, timeout: const Timeout.factor(10)); - - test('Analytics Survey', () async { - var serverResponse = await testController.send('devTools.survey', { - 'surveyRequest': 'copyAndCreateDevToolsFile', - }); - expect(serverResponse, isNotNull); - expect(serverResponse['success'], isTrue); - - serverResponse = await testController.send('devTools.survey', { - 'surveyRequest': apiSetActiveSurvey, - 'value': 'Q3-2019', - }); - expect(serverResponse, isNotNull); - expect(serverResponse['success'], isTrue); - expect(serverResponse['activeSurvey'], 'Q3-2019'); - - serverResponse = await testController.send('devTools.survey', { - 'surveyRequest': apiIncrementSurveyShownCount, - }); - expect(serverResponse, isNotNull); - expect(serverResponse['activeSurvey'], 'Q3-2019'); - expect(serverResponse['surveyShownCount'], 1); - - serverResponse = await testController.send('devTools.survey', { - 'surveyRequest': apiIncrementSurveyShownCount, - }); - expect(serverResponse, isNotNull); - expect(serverResponse['activeSurvey'], 'Q3-2019'); - expect(serverResponse['surveyShownCount'], 2); - - serverResponse = await testController.send('devTools.survey', { - 'surveyRequest': apiGetSurveyShownCount, - }); - expect(serverResponse, isNotNull); - expect(serverResponse['activeSurvey'], 'Q3-2019'); - expect(serverResponse['surveyShownCount'], 2); - - serverResponse = await testController.send('devTools.survey', { - 'surveyRequest': apiGetSurveyActionTaken, - }); - expect(serverResponse, isNotNull); - expect(serverResponse['activeSurvey'], 'Q3-2019'); - expect(serverResponse['surveyActionTaken'], isFalse); - - serverResponse = await testController.send('devTools.survey', { - 'surveyRequest': apiSetSurveyActionTaken, - 'value': json.encode(true), - }); - expect(serverResponse, isNotNull); - expect(serverResponse['activeSurvey'], 'Q3-2019'); - expect(serverResponse['surveyActionTaken'], isTrue); - - serverResponse = await testController.send('devTools.survey', { - 'surveyRequest': MachineModeCommandHandler.restoreDevToolsFile, - }); - expect(serverResponse, isNotNull); - expect(serverResponse['success'], isTrue); - expect( - serverResponse['content'], - '{\n' - ' \"Q3-2019\": {\n' - ' \"surveyActionTaken\": true,\n' - ' \"surveyShownCount\": 2\n' - ' }\n' - '}\n', - ); - }, timeout: const Timeout.factor(10)); - - for (final bool useVmService in [true, false]) { - group('Server (${useVmService ? 'VM Service' : 'API'})', () { - test('can launch on a specific page', () async { - // Register the VM. - await testController.send( - 'vm.register', - {'uri': testController.appFixture.serviceUri.toString()}, - ); - - // Send a request to launch at a certain page. - await testController.sendLaunchDevToolsRequest( - useVmService: useVmService, - page: 'memory', - ); - - final serverResponse = - await testController.waitForClients(requiredPage: 'memory'); - expect(serverResponse, isNotNull); - expect(serverResponse['clients'], hasLength(1)); - expect(serverResponse['clients'][0]['hasConnection'], isTrue); - expect( - serverResponse['clients'][0]['vmServiceUri'], - testController.appFixture.serviceUri.toString(), - ); - expect(serverResponse['clients'][0]['currentPage'], 'memory'); - }, timeout: const Timeout.factor(10)); - - test('can switch page', () async { - await testController.send( - 'vm.register', - {'uri': testController.appFixture.serviceUri.toString()}, - ); - - // Launch on the memory page and wait for the connection. - await testController.sendLaunchDevToolsRequest( - useVmService: useVmService, - page: 'memory', - ); - await testController.waitForClients(requiredPage: 'memory'); - - // Re-launch, allowing reuse and with a different page. - await testController.sendLaunchDevToolsRequest( - useVmService: useVmService, - reuseWindows: true, - page: 'logging', - ); - - final serverResponse = - await testController.waitForClients(requiredPage: 'logging'); - expect(serverResponse, isNotNull); - expect(serverResponse['clients'], hasLength(1)); - expect(serverResponse['clients'][0]['hasConnection'], isTrue); - expect( - serverResponse['clients'][0]['vmServiceUri'], - testController.appFixture.serviceUri.toString(), - ); - expect(serverResponse['clients'][0]['currentPage'], 'logging'); - }, timeout: const Timeout.factor(20)); - - test('Server reuses DevTools instance if already connected to same VM', - () async { - // Register the VM. - await testController.send( - 'vm.register', - {'uri': testController.appFixture.serviceUri.toString()}, - ); - - // Send a request to launch DevTools in a browser. - await testController.sendLaunchDevToolsRequest( - useVmService: useVmService, - ); - - { - final serverResponse = await testController.waitForClients( - requiredConnectionState: true, - ); - expect(serverResponse['clients'], hasLength(1)); - } - - // Request again, allowing reuse, and server emits an event saying the - // window was reused. - final launchResponse = await testController.sendLaunchDevToolsRequest( - useVmService: useVmService, - reuseWindows: true, - ); - expect(launchResponse['reused'], isTrue); - - // Ensure there's still only one connection (eg. we didn't spawn a new one - // we reused the existing one). - final serverResponse = - await testController.waitForClients(requiredConnectionState: true); - expect(serverResponse['clients'], hasLength(1)); - }, timeout: const Timeout.factor(20)); - - test('Server does not reuse DevTools instance if embedded', () async { - // Register the VM. - await testController.send( - 'vm.register', - {'uri': testController.appFixture.serviceUri.toString()}, - ); - - // Spawn an embedded version of DevTools in a browser. - final event = await testController.serverStartedEvent.future; - final devToolsUri = - 'http://${event['params']['host']}:${event['params']['port']}'; - final launchUrl = '$devToolsUri/?embed=true&page=logging' - '&uri=${Uri.encodeQueryComponent(testController.appFixture.serviceUri.toString())}'; - final chrome = await Chrome.locate()!.start(url: launchUrl); - try { - { - final serverResponse = await testController.waitForClients( - requiredConnectionState: true, - ); - expect(serverResponse['clients'], hasLength(1)); - } - - // Send a request to the server to launch and ensure it did - // not reuse the existing connection. Launch it on a different page - // so we can easily tell once this one has connected. - final launchResponse = await testController.sendLaunchDevToolsRequest( - useVmService: useVmService, - reuseWindows: true, - page: 'memory', - ); - expect(launchResponse['reused'], isFalse); - - // Ensure there's now two connections. - final serverResponse = await testController.waitForClients( - requiredConnectionState: true, - requiredPage: 'memory', - ); - expect(serverResponse['clients'], hasLength(2)); - } finally { - chrome.kill(); - } - }, timeout: const Timeout.factor(20)); - - test('Server reuses DevTools instance if not connected to a VM', - () async { - // Register the VM. - await testController.send( - 'vm.register', - {'uri': testController.appFixture.serviceUri.toString()}, - ); - - // Send a request to launch DevTools in a browser. - await testController.sendLaunchDevToolsRequest( - useVmService: useVmService, - ); - - // Wait for the DevTools to inform server that it's connected. - await testController.waitForClients(requiredConnectionState: true); - - // Terminate the VM. - await testController.appFixture.teardown(); - - // Ensure the client is marked as disconnected. - await testController.waitForClients(requiredConnectionState: false); - - // Start up a new app. - await testController.startApp(); - await testController.send( - 'vm.register', - {'uri': testController.appFixture.serviceUri.toString()}, - ); - - // Send a new request to launch. - await testController.sendLaunchDevToolsRequest( - useVmService: useVmService, - reuseWindows: true, - notify: true, - ); - - // Ensure we now have a single connected client. - final serverResponse = - await testController.waitForClients(requiredConnectionState: true); - expect(serverResponse['clients'], hasLength(1)); - expect(serverResponse['clients'][0]['hasConnection'], isTrue); - expect( - serverResponse['clients'][0]['vmServiceUri'], - testController.appFixture.serviceUri.toString(), - ); - }, timeout: const Timeout.factor(20)); - }); - } -} diff --git a/pkg/dds/test/devtools_server/embedding_test.dart b/pkg/dds/test/devtools_server/embedding_test.dart new file mode 100644 index 00000000000..99adce4b78f --- /dev/null +++ b/pkg/dds/test/devtools_server/embedding_test.dart @@ -0,0 +1,44 @@ +// Copyright 2022 The Chromium 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:io'; + +import 'package:test/test.dart'; + +import 'utils/server_driver.dart'; + +late final DevToolsServerTestController testController; + +void main() { + testController = DevToolsServerTestController(); + + setUp(() async { + await testController.setUp(); + }); + + tearDown(() async { + await testController.tearDown(); + }); + + test('allows embedding without flag', () async { + final server = await DevToolsServerDriver.create(); + final httpClient = HttpClient(); + late HttpClientResponse resp; + try { + final startedEvent = (await server.stdout.firstWhere( + (map) => map!['event'] == 'server.started', + ))!; + final host = startedEvent['params']['host']; + final port = startedEvent['params']['port']; + + final req = await httpClient.get(host, port, '/'); + resp = await req.close(); + expect(resp.headers.value('x-frame-options'), isNull); + } finally { + httpClient.close(); + await resp.drain(); + server.kill(); + } + }, timeout: const Timeout.factor(10)); +} diff --git a/pkg/dds/test/fixtures/empty_dart_app.dart b/pkg/dds/test/devtools_server/fixtures/empty_dart_app.dart similarity index 100% rename from pkg/dds/test/fixtures/empty_dart_app.dart rename to pkg/dds/test/devtools_server/fixtures/empty_dart_app.dart diff --git a/pkg/dds/test/devtools_server/instance_reuse_test.dart b/pkg/dds/test/devtools_server/instance_reuse_test.dart new file mode 100644 index 00000000000..71d74285cec --- /dev/null +++ b/pkg/dds/test/devtools_server/instance_reuse_test.dart @@ -0,0 +1,150 @@ +// Copyright 2022 The Chromium 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 'package:devtools_shared/devtools_test_utils.dart'; +import 'package:test/test.dart'; + +import 'utils/server_driver.dart'; + +late final DevToolsServerTestController testController; + +void main() { + testController = DevToolsServerTestController(); + + setUp(() async { + await testController.setUp(); + }); + + tearDown(() async { + await testController.tearDown(); + }); + + for (final bool useVmService in [true, false]) { + group('Server (${useVmService ? 'VM Service' : 'API'})', () { + test('reuses DevTools instance if already connected to same VM', + () async { + // Register the VM. + await testController.send( + 'vm.register', + {'uri': testController.appFixture.serviceUri.toString()}, + ); + + // Send a request to launch DevTools in a browser. + await testController.sendLaunchDevToolsRequest( + useVmService: useVmService, + ); + + { + final serverResponse = await testController.waitForClients( + requiredConnectionState: true, + ); + expect(serverResponse['clients'], hasLength(1)); + } + + // Request again, allowing reuse, and server emits an event saying the + // window was reused. + final launchResponse = await testController.sendLaunchDevToolsRequest( + useVmService: useVmService, + reuseWindows: true, + ); + expect(launchResponse['reused'], isTrue); + + // Ensure there's still only one connection (eg. we didn't spawn a new one + // we reused the existing one). + final serverResponse = + await testController.waitForClients(requiredConnectionState: true); + expect(serverResponse['clients'], hasLength(1)); + }, timeout: const Timeout.factor(20)); + + test('Server does not reuse DevTools instance if embedded', () async { + // Register the VM. + await testController.send( + 'vm.register', + {'uri': testController.appFixture.serviceUri.toString()}, + ); + + // Spawn an embedded version of DevTools in a browser. + final event = await testController.serverStartedEvent.future; + final devToolsUri = + 'http://${event['params']['host']}:${event['params']['port']}'; + final launchUrl = '$devToolsUri/?embed=true&page=logging' + '&uri=${Uri.encodeQueryComponent(testController.appFixture.serviceUri.toString())}'; + final chrome = await Chrome.locate()!.start(url: launchUrl); + try { + { + final serverResponse = await testController.waitForClients( + requiredConnectionState: true, + ); + expect(serverResponse['clients'], hasLength(1)); + } + + // Send a request to the server to launch and ensure it did + // not reuse the existing connection. Launch it on a different page + // so we can easily tell once this one has connected. + final launchResponse = await testController.sendLaunchDevToolsRequest( + useVmService: useVmService, + reuseWindows: true, + page: 'memory', + ); + expect(launchResponse['reused'], isFalse); + + // Ensure there's now two connections. + final serverResponse = await testController.waitForClients( + requiredConnectionState: true, + requiredPage: 'memory', + ); + expect(serverResponse['clients'], hasLength(2)); + } finally { + chrome.kill(); + } + }, timeout: const Timeout.factor(20)); + + test('reuses DevTools instance if not connected to a VM', () async { + // Register the VM. + await testController.send( + 'vm.register', + {'uri': testController.appFixture.serviceUri.toString()}, + ); + + // Send a request to launch DevTools in a browser. + await testController.sendLaunchDevToolsRequest( + useVmService: useVmService, + ); + + // Wait for the DevTools to inform server that it's connected. + await testController.waitForClients(requiredConnectionState: true); + + // Terminate the VM. + await testController.appFixture.teardown(); + + // Ensure the client is marked as disconnected. + await testController.waitForClients(requiredConnectionState: false); + + // Start up a new app. + await testController.startApp(); + await testController.send( + 'vm.register', + {'uri': testController.appFixture.serviceUri.toString()}, + ); + + // Send a new request to launch. + await testController.sendLaunchDevToolsRequest( + useVmService: useVmService, + reuseWindows: true, + notify: true, + ); + + // Ensure we now have a single connected client. + final serverResponse = + await testController.waitForClients(requiredConnectionState: true); + expect(serverResponse['clients'], hasLength(1)); + expect(serverResponse['clients'][0]['hasConnection'], isTrue); + expect( + serverResponse['clients'][0]['vmServiceUri'], + testController.appFixture.serviceUri.toString(), + ); + }, timeout: const Timeout.factor(20)); + }); + } +} diff --git a/pkg/dds/test/devtools_server/remote_control_test.dart b/pkg/dds/test/devtools_server/remote_control_test.dart new file mode 100644 index 00000000000..c93d2a0530f --- /dev/null +++ b/pkg/dds/test/devtools_server/remote_control_test.dart @@ -0,0 +1,82 @@ +// Copyright 2022 The Chromium 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 'package:test/test.dart'; + +import 'utils/server_driver.dart'; + +late final DevToolsServerTestController testController; + +void main() { + testController = DevToolsServerTestController(); + + setUp(() async { + await testController.setUp(); + }); + + tearDown(() async { + await testController.tearDown(); + }); + + for (final bool useVmService in [true, false]) { + group('Server (${useVmService ? 'VM Service' : 'API'})', () { + test('can launch on a specific page', () async { + // Register the VM. + await testController.send( + 'vm.register', + {'uri': testController.appFixture.serviceUri.toString()}, + ); + + // Send a request to launch at a certain page. + await testController.sendLaunchDevToolsRequest( + useVmService: useVmService, + page: 'memory', + ); + + final serverResponse = + await testController.waitForClients(requiredPage: 'memory'); + expect(serverResponse, isNotNull); + expect(serverResponse['clients'], hasLength(1)); + expect(serverResponse['clients'][0]['hasConnection'], isTrue); + expect( + serverResponse['clients'][0]['vmServiceUri'], + testController.appFixture.serviceUri.toString(), + ); + expect(serverResponse['clients'][0]['currentPage'], 'memory'); + }, timeout: const Timeout.factor(10)); + + test('can switch page', () async { + await testController.send( + 'vm.register', + {'uri': testController.appFixture.serviceUri.toString()}, + ); + + // Launch on the memory page and wait for the connection. + await testController.sendLaunchDevToolsRequest( + useVmService: useVmService, + page: 'memory', + ); + await testController.waitForClients(requiredPage: 'memory'); + + // Re-launch, allowing reuse and with a different page. + await testController.sendLaunchDevToolsRequest( + useVmService: useVmService, + reuseWindows: true, + page: 'logging', + ); + + final serverResponse = + await testController.waitForClients(requiredPage: 'logging'); + expect(serverResponse, isNotNull); + expect(serverResponse['clients'], hasLength(1)); + expect(serverResponse['clients'][0]['hasConnection'], isTrue); + expect( + serverResponse['clients'][0]['vmServiceUri'], + testController.appFixture.serviceUri.toString(), + ); + expect(serverResponse['clients'][0]['currentPage'], 'logging'); + }, timeout: const Timeout.factor(20)); + }); + } +} diff --git a/pkg/dds/test/devtools_server/server_connection_api_test.dart b/pkg/dds/test/devtools_server/server_connection_api_test.dart new file mode 100644 index 00000000000..e4d5ff84bfe --- /dev/null +++ b/pkg/dds/test/devtools_server/server_connection_api_test.dart @@ -0,0 +1,9 @@ +// Copyright 2022 The Chromium 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 'server_connection_common.dart'; + +// This test reuses much of the same logic as server_connection_vm_service_test +// but running both in the same process will result in a timeout. +void main() => runTest(useVmService: false); diff --git a/pkg/dds/test/devtools_server/server_connection_common.dart b/pkg/dds/test/devtools_server/server_connection_common.dart new file mode 100644 index 00000000000..f812e9a5b48 --- /dev/null +++ b/pkg/dds/test/devtools_server/server_connection_common.dart @@ -0,0 +1,101 @@ +// Copyright 2022 The Chromium 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 'package:devtools_shared/devtools_test_utils.dart'; +import 'package:test/test.dart'; + +import 'utils/server_driver.dart'; + +// Note: this test is broken out from devtools_server_test.dart so that the +// tests run faster and we do not have to mark them as slow. + +late final DevToolsServerTestController testController; + +void runTest({required bool useVmService}) { + testController = DevToolsServerTestController(); + + setUp(() async { + await testController.setUp(); + }); + + tearDown(() async { + await testController.tearDown(); + }); + + group('Server (${useVmService ? 'VM Service' : 'API'})', () { + test( + 'DevTools connects back to server API and registers that it is connected', + () async { + // Register the VM. + await testController.send( + 'vm.register', + {'uri': testController.appFixture.serviceUri.toString()}, + ); + + // Send a request to launch DevTools in a browser. + await testController.sendLaunchDevToolsRequest( + useVmService: useVmService, + ); + + final serverResponse = + await testController.waitForClients(requiredConnectionState: true); + expect(serverResponse, isNotNull); + expect(serverResponse['clients'], hasLength(1)); + expect(serverResponse['clients'][0]['hasConnection'], isTrue); + expect( + serverResponse['clients'][0]['vmServiceUri'], + testController.appFixture.serviceUri.toString(), + ); + }, timeout: const Timeout.factor(10)); + + test('DevTools reports disconnects from a VM', () async { + // Register the VM. + await testController.send( + 'vm.register', + {'uri': testController.appFixture.serviceUri.toString()}, + ); + + // Send a request to launch DevTools in a browser. + await testController.sendLaunchDevToolsRequest( + useVmService: useVmService, + ); + + // Wait for the DevTools to inform server that it's connected. + await testController.waitForClients(requiredConnectionState: true); + + // Terminate the VM. + await testController.appFixture.teardown(); + + // Ensure the client is marked as disconnected. + final serverResponse = await testController.waitForClients( + requiredConnectionState: false, + ); + expect(serverResponse['clients'], hasLength(1)); + expect(serverResponse['clients'][0]['hasConnection'], isFalse); + expect(serverResponse['clients'][0]['vmServiceUri'], isNull); + }, timeout: const Timeout.factor(20)); + + test('server removes clients that disconnect from the API', () async { + final event = await testController.serverStartedEvent.future; + + // Spawn our own Chrome process so we can terminate it. + final devToolsUri = + 'http://${event['params']['host']}:${event['params']['port']}'; + final chrome = await Chrome.locate()!.start(url: devToolsUri); + + // Wait for DevTools to inform server that it's connected. + await testController.waitForClients(); + + // Close the browser, which will disconnect DevTools SSE connection + // back to the server. + chrome.kill(); + + // Await a long delay to wait for the SSE client to close. + await delay(duration: const Duration(seconds: 15)); + + // Ensure the client is completely removed from the list. + await testController.waitForClients(expectNone: true); + }, timeout: const Timeout.factor(20)); + }); +} diff --git a/pkg/dds/test/devtools_server/server_connection_vm_service_test.dart b/pkg/dds/test/devtools_server/server_connection_vm_service_test.dart new file mode 100644 index 00000000000..ef4dfd3c4c3 --- /dev/null +++ b/pkg/dds/test/devtools_server/server_connection_vm_service_test.dart @@ -0,0 +1,9 @@ +// Copyright 2022 The Chromium 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 'server_connection_common.dart'; + +// This test reuses much of the same logic as server_connection_api_test but +// running both in the same process will result in a timeout. +void main() => runTest(useVmService: true); diff --git a/pkg/dds/test/devtools_server/service_registration_test.dart b/pkg/dds/test/devtools_server/service_registration_test.dart new file mode 100644 index 00000000000..fa85a4f6c5a --- /dev/null +++ b/pkg/dds/test/devtools_server/service_registration_test.dart @@ -0,0 +1,36 @@ +// Copyright 2022 The Chromium 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 'package:dds/devtools_server.dart'; +import 'package:test/test.dart'; + +import 'utils/server_driver.dart'; + +late final DevToolsServerTestController testController; + +void main() { + testController = DevToolsServerTestController(); + + setUp(() async { + await testController.setUp(); + }); + + tearDown(() async { + await testController.tearDown(); + }); + + test('registers service', () async { + final serverResponse = await testController.send( + 'vm.register', + {'uri': testController.appFixture.serviceUri.toString()}, + ); + expect(serverResponse['success'], isTrue); + + // Expect the VM service to see the launchDevTools service registered. + expect( + testController.registeredServices, + contains(DevToolsServer.launchDevToolsService), + ); + }, timeout: const Timeout.factor(10)); +} diff --git a/pkg/dds/test/devtools_server/serve_devtools.dart b/pkg/dds/test/devtools_server/utils/serve_devtools.dart similarity index 75% rename from pkg/dds/test/devtools_server/serve_devtools.dart rename to pkg/dds/test/devtools_server/utils/serve_devtools.dart index 7fbd12c0adc..ad0d7649b2e 100644 --- a/pkg/dds/test/devtools_server/serve_devtools.dart +++ b/pkg/dds/test/devtools_server/utils/serve_devtools.dart @@ -6,13 +6,14 @@ import 'dart:async'; import 'package:dds/devtools_server.dart'; -import '../common/test_helper.dart'; +import '../../common/test_helper.dart'; void main(List args) async { unawaited( DevToolsServer().serveDevToolsWithArgs( args, - customDevToolsPath: devtoolsAppUri(prefix: '../../../../').toFilePath(), + customDevToolsPath: + devtoolsAppUri(prefix: '../../../../../').toFilePath(), ), ); } diff --git a/pkg/dds/test/devtools_server/devtools_server_driver.dart b/pkg/dds/test/devtools_server/utils/server_driver.dart similarity index 97% rename from pkg/dds/test/devtools_server/devtools_server_driver.dart rename to pkg/dds/test/devtools_server/utils/server_driver.dart index 9f56b816f3c..7a2d8c7fd9a 100644 --- a/pkg/dds/test/devtools_server/devtools_server_driver.dart +++ b/pkg/dds/test/devtools_server/utils/server_driver.dart @@ -63,8 +63,9 @@ class DevToolsServerDriver { int? tryPorts, List additionalArgs = const [], }) async { - final script = - Platform.script.resolveUri(Uri.parse('./serve_devtools.dart')); + final script = Platform.script.resolveUri( + Uri.parse('utils/serve_devtools.dart'), + ); final args = [ script.toFilePath(), '--machine', @@ -200,8 +201,8 @@ class DevToolsServerTestController { } Future startApp() async { - final appUri = Platform.script - .resolveUri(Uri.parse('../fixtures/empty_dart_app.dart')); + final appUri = + Platform.script.resolveUri(Uri.parse('fixtures/empty_dart_app.dart')); appFixture = await CliAppFixture.create(appUri.toFilePath()); // Track services method names as they're registered. diff --git a/pkg/pkg.status b/pkg/pkg.status index 38658de4fcc..2aef55f42ce 100644 --- a/pkg/pkg.status +++ b/pkg/pkg.status @@ -145,8 +145,6 @@ vm_service/test/*: SkipByDesign # Uses dart:io vm_snapshot_analysis/test/*: SkipByDesign # Only meant to run on vm [ $system == windows ] -dds/test/devtools_server/devtools_server_connection_test: Pass, Slow -dds/test/devtools_server/devtools_server_test: Pass, Slow front_end/test/fasta/bootstrap_test: Skip # Issue 31902 front_end/test/fasta/strong_test: Pass, Slow, Timeout front_end/test/fasta/text_serialization_test: Pass, Slow, Timeout