mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 03:07:49 +00:00
[ package:vm_service ] Migrate service extension tests
Change-Id: If589d96d7d9549456f43fc9ed96a692737a711fe Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/332501 Commit-Queue: Ben Konyi <bkonyi@google.com> Reviewed-by: Derek Xu <derekx@google.com>
This commit is contained in:
parent
f54ecbdf31
commit
290a675add
|
@ -448,6 +448,44 @@ List<String> removeAdjacentDuplicates(List<String> fromList) {
|
|||
return result;
|
||||
}
|
||||
|
||||
typedef ServiceExtensionHandler = Future<Map<String, dynamic>> Function(
|
||||
Map<String, dynamic> cb,
|
||||
);
|
||||
|
||||
/// Registers a service extension and returns the actual service name used to
|
||||
/// invoke the service.
|
||||
Future<String> registerServiceHelper(
|
||||
VmService primaryClient,
|
||||
VmService serviceRegisterClient,
|
||||
String serviceName,
|
||||
ServiceExtensionHandler callback,
|
||||
) async {
|
||||
final serviceNameCompleter = Completer<String>();
|
||||
late final StreamSubscription sub;
|
||||
sub = primaryClient.onServiceEvent.listen((event) {
|
||||
if (event.kind == EventKind.kServiceRegistered &&
|
||||
event.method!.endsWith(serviceName)) {
|
||||
serviceNameCompleter.complete(event.method!);
|
||||
sub.cancel();
|
||||
}
|
||||
});
|
||||
// TODO(bkonyi): if we end up in a situation where this call throws due to a
|
||||
// prior subscription to the Service stream, we should do something similar
|
||||
// to _subscribeDebugStream in this method.
|
||||
await primaryClient.streamListen(EventStreams.kService);
|
||||
|
||||
// Register the service.
|
||||
serviceRegisterClient.registerServiceCallback(serviceName, callback);
|
||||
await serviceRegisterClient.registerService(serviceName, serviceName);
|
||||
|
||||
// Wait for the service registered event on the non-registering client to get
|
||||
// the actual service name.
|
||||
final actualServiceName = await serviceNameCompleter.future;
|
||||
print("Service '$serviceName' registered as '$actualServiceName'");
|
||||
await primaryClient.streamCancel(EventStreams.kService);
|
||||
return actualServiceName;
|
||||
}
|
||||
|
||||
Future<void> evaluateInFrameAndExpect(
|
||||
VmService service,
|
||||
String isolateId,
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
// 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.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:vm_service/vm_service_io.dart';
|
||||
|
||||
import 'common/service_test_common.dart';
|
||||
import 'common/test_helper.dart';
|
||||
|
||||
const successServiceName = 'successService';
|
||||
const errorServiceName = 'errorService';
|
||||
const serviceAlias = 'serviceAlias';
|
||||
const paramKey = 'pkey';
|
||||
const paramValue = 'pvalue';
|
||||
const resultKey = 'rkey';
|
||||
const resultValue = 'rvalue';
|
||||
const errorCode = 5000;
|
||||
const errorKey = 'ekey';
|
||||
const errorValue = 'evalue';
|
||||
const repetition = 5;
|
||||
|
||||
Future<void> testSuccessService(
|
||||
VmService primaryClient,
|
||||
VmService secondaryClient,
|
||||
) async {
|
||||
final successServiceRequests =
|
||||
<(Map<String, dynamic>, Completer<Map<String, dynamic>>)>[];
|
||||
final allRequestsReceivedCompleter = Completer<void>();
|
||||
|
||||
final successServiceMethod = await registerServiceHelper(
|
||||
primaryClient,
|
||||
secondaryClient,
|
||||
successServiceName,
|
||||
(params) async {
|
||||
final completer = Completer<Map<String, dynamic>>();
|
||||
successServiceRequests.add((params, completer));
|
||||
if (successServiceRequests.length == repetition) {
|
||||
allRequestsReceivedCompleter.complete();
|
||||
}
|
||||
return await completer.future;
|
||||
},
|
||||
);
|
||||
|
||||
// Testing parallel invocation of service which succeeds
|
||||
final results = <Future<Response>>[
|
||||
for (int i = 0; i < repetition; ++i)
|
||||
primaryClient.callServiceExtension(
|
||||
successServiceMethod,
|
||||
args: {
|
||||
paramKey + i.toString(): paramValue + i.toString(),
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
// Wait for all of the requests to be received before processing.
|
||||
await allRequestsReceivedCompleter.future;
|
||||
|
||||
final completions = <Function>[];
|
||||
for (final request in successServiceRequests) {
|
||||
final (params, responseCompleter) = request;
|
||||
final iteration = successServiceRequests.indexOf(request);
|
||||
final end = iteration.toString();
|
||||
|
||||
// check requests while they arrive
|
||||
expect(params[paramKey + end], paramValue + end);
|
||||
// answer later
|
||||
completions.add(() => responseCompleter.complete({
|
||||
'result': {
|
||||
resultKey + end: resultValue + end,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
// Shuffle and respond out of order.
|
||||
completions.shuffle();
|
||||
for (final c in completions) {
|
||||
c();
|
||||
}
|
||||
|
||||
final responses = await Future.wait(results);
|
||||
for (int i = 0; i < responses.length; ++i) {
|
||||
final response = responses[i];
|
||||
expect(response, isNotNull);
|
||||
expect(
|
||||
response.json![resultKey + i.toString()],
|
||||
resultValue + i.toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testErrorService(
|
||||
VmService primaryClient,
|
||||
VmService secondaryClient,
|
||||
) async {
|
||||
final serviceRequests =
|
||||
<(Map<String, dynamic>, Completer<Map<String, dynamic>>)>[];
|
||||
final allRequestsReceivedCompleter = Completer<void>();
|
||||
|
||||
final errorServiceMethod = await registerServiceHelper(
|
||||
primaryClient,
|
||||
secondaryClient,
|
||||
errorServiceName,
|
||||
(params) async {
|
||||
final completer = Completer<Map<String, dynamic>>();
|
||||
serviceRequests.add((params, completer));
|
||||
if (serviceRequests.length == repetition) {
|
||||
allRequestsReceivedCompleter.complete();
|
||||
}
|
||||
return await completer.future;
|
||||
},
|
||||
);
|
||||
|
||||
// Testing parallel invocation of service which returns an error
|
||||
final results = <Future<Response>>[
|
||||
for (int i = 0; i < repetition; ++i)
|
||||
primaryClient.callServiceExtension(
|
||||
errorServiceMethod,
|
||||
args: {
|
||||
paramKey + i.toString(): paramValue + i.toString(),
|
||||
},
|
||||
)
|
||||
// We ignore these futures so that when they complete with an error
|
||||
// without being awaited or do not have an error handler registered
|
||||
// they won't cause an unhandled exception.
|
||||
..ignore(),
|
||||
];
|
||||
|
||||
// Wait for all of the requests to be received before processing.
|
||||
await allRequestsReceivedCompleter.future;
|
||||
|
||||
final completions = <Function>[];
|
||||
for (final request in serviceRequests) {
|
||||
final (params, responseCompleter) = request;
|
||||
final iteration = serviceRequests.indexOf(request);
|
||||
final end = iteration.toString();
|
||||
|
||||
// check requests while they arrive
|
||||
expect(params[paramKey + end], paramValue + end);
|
||||
// answer later
|
||||
completions.add(
|
||||
() => responseCompleter.complete(
|
||||
{
|
||||
'error': {
|
||||
'code': errorCode + iteration,
|
||||
'data': {errorKey + end: errorValue + end},
|
||||
'message': 'error message',
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Shuffle and respond out of order.
|
||||
completions.shuffle();
|
||||
for (final c in completions) {
|
||||
c();
|
||||
}
|
||||
|
||||
for (int i = 0; i < results.length; ++i) {
|
||||
final response = results[i];
|
||||
try {
|
||||
await response;
|
||||
fail('Response should be an error');
|
||||
} on RPCError catch (e) {
|
||||
expect(
|
||||
e.data![errorKey + i.toString()],
|
||||
errorValue + i.toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final tests = <IsolateTest>[
|
||||
(VmService primaryClient, IsolateRef isolateRef) async {
|
||||
final secondaryClient = await vmServiceConnectUri(primaryClient.wsUri!);
|
||||
await testSuccessService(primaryClient, secondaryClient);
|
||||
await testErrorService(primaryClient, secondaryClient);
|
||||
},
|
||||
];
|
||||
|
||||
void main([args = const <String>[]]) => runIsolateTests(
|
||||
args,
|
||||
tests,
|
||||
'external_service_asynchronous_invocation_test.dart',
|
||||
);
|
90
pkg/vm_service/test/external_service_disappear_test.dart
Normal file
90
pkg/vm_service/test/external_service_disappear_test.dart
Normal file
|
@ -0,0 +1,90 @@
|
|||
// 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.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:vm_service/vm_service_io.dart';
|
||||
|
||||
import 'common/service_test_common.dart';
|
||||
import 'common/test_helper.dart';
|
||||
|
||||
const serviceName = 'disappearService';
|
||||
const serviceAlias = 'serviceAlias';
|
||||
const paramKey = 'pkey';
|
||||
const paramValue = 'pvalue';
|
||||
const repetition = 5;
|
||||
|
||||
final tests = <IsolateTest>[
|
||||
(VmService primaryClient, IsolateRef isolateRef) async {
|
||||
final secondaryClient = await vmServiceConnectUri(primaryClient.wsUri!);
|
||||
|
||||
final allRequestsReceivedCompleter = Completer<void>();
|
||||
final requests = <Map<String, dynamic>>[];
|
||||
|
||||
// Register the service.
|
||||
final serviceMethodName = await registerServiceHelper(
|
||||
primaryClient,
|
||||
secondaryClient,
|
||||
serviceName,
|
||||
(params) async {
|
||||
final completer = Completer<Map<String, dynamic>>();
|
||||
requests.add(params);
|
||||
if (requests.length == repetition) {
|
||||
allRequestsReceivedCompleter.complete();
|
||||
}
|
||||
// We never complete this future as we want to see how the client
|
||||
// handles the service disappearing while there are outstanding
|
||||
// requests.
|
||||
return await completer.future;
|
||||
},
|
||||
);
|
||||
|
||||
// Invoke the service multiple times.
|
||||
{
|
||||
final results = <Future<Response>>[
|
||||
for (int i = 0; i < repetition; ++i)
|
||||
primaryClient.callServiceExtension(
|
||||
serviceMethodName,
|
||||
args: {
|
||||
paramKey + i.toString(): paramValue + i.toString(),
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
// Wait for all of the requests to be received before processing.
|
||||
await allRequestsReceivedCompleter.future;
|
||||
|
||||
// Verify the request parameters as a sanity check.
|
||||
for (final params in requests) {
|
||||
final iteration = requests.indexOf(params);
|
||||
final end = iteration.toString();
|
||||
|
||||
// check requests while they arrive
|
||||
expect(params[paramKey + end], paramValue + end);
|
||||
}
|
||||
|
||||
// Disconnect the service client that registered the service extension.
|
||||
await secondaryClient.dispose();
|
||||
|
||||
// Check that all of the outstanding requests complete with an RPC error.
|
||||
for (final future in results) {
|
||||
try {
|
||||
await future;
|
||||
fail('Service should have disappeared');
|
||||
} on RPCError catch (e) {
|
||||
expect(e.code, RPCErrorKind.kServiceDisappeared.code);
|
||||
expect(e.message, 'Service has disappeared');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
void main([args = const <String>[]]) => runIsolateTests(
|
||||
args,
|
||||
tests,
|
||||
'external_service_disappear_test.dart',
|
||||
);
|
34
pkg/vm_service/test/external_service_registration_test.dart
Normal file
34
pkg/vm_service/test/external_service_registration_test.dart
Normal file
|
@ -0,0 +1,34 @@
|
|||
// 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.
|
||||
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'common/test_helper.dart';
|
||||
|
||||
const serviceName = 'serviceName';
|
||||
const serviceAlias = 'serviceAlias';
|
||||
|
||||
final tests = <IsolateTest>[
|
||||
(VmService primaryClient, IsolateRef isolateRef) async {
|
||||
// Register two unique services.
|
||||
await primaryClient.registerService(serviceName, serviceAlias);
|
||||
await primaryClient.registerService(serviceName + '2', serviceAlias + '2');
|
||||
|
||||
try {
|
||||
// Try to register with an existing service name.
|
||||
await primaryClient.registerService(serviceName, serviceAlias);
|
||||
fail('Successfully registered service with duplicate name');
|
||||
} on RPCError catch (e) {
|
||||
expect(e.code, RPCErrorKind.kServiceAlreadyRegistered.code);
|
||||
expect(e.message, 'Service already registered');
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
void main([args = const <String>[]]) => runIsolateTests(
|
||||
args,
|
||||
tests,
|
||||
'external_service_registration_test.dart',
|
||||
);
|
|
@ -0,0 +1,117 @@
|
|||
// 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.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:vm_service/vm_service_io.dart';
|
||||
|
||||
import 'common/service_test_common.dart';
|
||||
import 'common/test_helper.dart';
|
||||
|
||||
const successServiceName = 'successService';
|
||||
const errorServiceName = 'errorService';
|
||||
const serviceAlias = 'serviceAlias';
|
||||
const paramKey = 'pkey';
|
||||
const paramValue = 'pvalue';
|
||||
const resultKey = 'rkey';
|
||||
const resultValue = 'rvalue';
|
||||
const errorCode = 5000;
|
||||
const errorKey = 'ekey';
|
||||
const errorValue = 'evalue';
|
||||
const repetition = 5;
|
||||
const errorMessage = 'Error message';
|
||||
|
||||
Future<void> testSuccessService(
|
||||
VmService primaryClient,
|
||||
VmService secondaryClient,
|
||||
) async {
|
||||
int requestCount = 0;
|
||||
final successServiceMethod = await registerServiceHelper(
|
||||
primaryClient,
|
||||
secondaryClient,
|
||||
successServiceName,
|
||||
(params) async {
|
||||
final i = requestCount.toString();
|
||||
expect(params[paramKey + i], paramValue + i);
|
||||
++requestCount;
|
||||
return {
|
||||
'result': {
|
||||
resultKey + i: resultValue + i,
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
// Testing serial invocation of service which succeeds
|
||||
{
|
||||
for (int i = 0; i < repetition; ++i) {
|
||||
final response = await primaryClient.callServiceExtension(
|
||||
successServiceMethod,
|
||||
args: {
|
||||
paramKey + i.toString(): paramValue + i.toString(),
|
||||
},
|
||||
);
|
||||
expect(
|
||||
response.json![resultKey + i.toString()],
|
||||
resultValue + i.toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testErrorService(
|
||||
VmService primaryClient,
|
||||
VmService secondaryClient,
|
||||
) async {
|
||||
int requestCount = 0;
|
||||
final errorServiceMethod = await registerServiceHelper(
|
||||
primaryClient,
|
||||
secondaryClient,
|
||||
errorServiceName,
|
||||
(params) async {
|
||||
final i = requestCount++;
|
||||
final iStr = i.toString();
|
||||
return {
|
||||
'error': {
|
||||
'code': errorCode + i,
|
||||
'data': {errorKey + iStr: errorValue + iStr},
|
||||
'message': errorMessage,
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
// Testing serial invocation of service which returns an error
|
||||
for (int i = 0; i < repetition; ++i) {
|
||||
try {
|
||||
await primaryClient.callServiceExtension(
|
||||
errorServiceMethod,
|
||||
args: {
|
||||
paramKey + i.toString(): paramValue + i.toString(),
|
||||
},
|
||||
);
|
||||
fail('Response should be an error');
|
||||
} on RPCError catch (e) {
|
||||
expect(e.code, errorCode + i);
|
||||
expect(e.data![errorKey + i.toString()], errorValue + i.toString());
|
||||
expect(e.message, errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final tests = <IsolateTest>[
|
||||
(VmService primaryClient, IsolateRef isolateRef) async {
|
||||
final secondaryClient = await vmServiceConnectUri(primaryClient.wsUri!);
|
||||
await testSuccessService(primaryClient, secondaryClient);
|
||||
await testErrorService(primaryClient, secondaryClient);
|
||||
},
|
||||
];
|
||||
|
||||
void main([args = const <String>[]]) => runIsolateTests(
|
||||
args,
|
||||
tests,
|
||||
'external_service_asynchronous_invocation_test.dart',
|
||||
);
|
Loading…
Reference in a new issue