blob: 1edee0f26850c29b595264d2a6dd274c8c2ac576 [file] [log] [blame]
// 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:test/test.dart';
import 'package:vm_service/vm_service.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',
);