// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // @dart = 2.8 import 'dart:async'; import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/convert.dart'; import 'package:flutter_tools/src/vmservice.dart'; import 'package:meta/meta.dart'; import 'package:test_api/test_api.dart' hide test; // ignore: deprecated_member_use import 'package:vm_service/vm_service.dart' as vm_service; export 'package:test_api/test_api.dart' hide test, isInstanceOf; // ignore: deprecated_member_use /// A fake implementation of a vm_service that mocks the JSON-RPC request /// and response structure. class FakeVmServiceHost { FakeVmServiceHost({ @required List<VmServiceExpectation> requests, Uri httpAddress, Uri wsAddress, }) : _requests = requests { _vmService = FlutterVmService(vm_service.VmService( _input.stream, _output.add, ), httpAddress: httpAddress, wsAddress: wsAddress); _applyStreamListen(); _output.stream.listen((String data) { final Map<String, Object> request = json.decode(data) as Map<String, Object>; if (_requests.isEmpty) { throw Exception('Unexpected request: $request'); } final FakeVmServiceRequest fakeRequest = _requests.removeAt(0) as FakeVmServiceRequest; expect(request, isA<Map<String, Object>>() .having((Map<String, Object> request) => request['method'], 'method', fakeRequest.method) .having((Map<String, Object> request) => request['params'], 'args', fakeRequest.args) ); if (fakeRequest.close) { unawaited(_vmService.dispose()); expect(_requests, isEmpty); return; } if (fakeRequest.errorCode == null) { _input.add(json.encode(<String, Object>{ 'jsonrpc': '2.0', 'id': request['id'], 'result': fakeRequest.jsonResponse ?? <String, Object>{'type': 'Success'}, })); } else { _input.add(json.encode(<String, Object>{ 'jsonrpc': '2.0', 'id': request['id'], 'error': <String, Object>{ 'code': fakeRequest.errorCode, } })); } _applyStreamListen(); }); } final List<VmServiceExpectation> _requests; final StreamController<String> _input = StreamController<String>(); final StreamController<String> _output = StreamController<String>(); FlutterVmService get vmService => _vmService; FlutterVmService _vmService; bool get hasRemainingExpectations => _requests.isNotEmpty; // remove FakeStreamResponse objects from _requests until it is empty // or until we hit a FakeRequest void _applyStreamListen() { while (_requests.isNotEmpty && !_requests.first.isRequest) { final FakeVmServiceStreamResponse response = _requests.removeAt(0) as FakeVmServiceStreamResponse; _input.add(json.encode(<String, Object>{ 'jsonrpc': '2.0', 'method': 'streamNotify', 'params': <String, Object>{ 'streamId': response.streamId, 'event': response.event.toJson(), }, })); } } } abstract class VmServiceExpectation { bool get isRequest; } class FakeVmServiceRequest implements VmServiceExpectation { const FakeVmServiceRequest({ @required this.method, this.args = const <String, Object>{}, this.jsonResponse, this.errorCode, this.close = false, }); final String method; /// When true, the vm service is automatically closed. final bool close; /// If non-null, the error code for a [vm_service.RPCError] in place of a /// standard response. final int errorCode; final Map<String, Object> args; final Map<String, Object> jsonResponse; @override bool get isRequest => true; } class FakeVmServiceStreamResponse implements VmServiceExpectation { const FakeVmServiceStreamResponse({ @required this.event, @required this.streamId, }); final vm_service.Event event; final String streamId; @override bool get isRequest => false; }