fake_vm_services.dart 4.03 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// 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.

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: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({
19 20 21
    required List<VmServiceExpectation> requests,
    Uri? httpAddress,
    Uri? wsAddress,
22 23 24 25 26 27 28
  }) : _requests = requests {
    _vmService = FlutterVmService(vm_service.VmService(
      _input.stream,
      _output.add,
    ), httpAddress: httpAddress, wsAddress: wsAddress);
    _applyStreamListen();
    _output.stream.listen((String data) {
29
      final Map<String, Object?> request = json.decode(data) as Map<String, Object?>;
30 31 32 33
      if (_requests.isEmpty) {
        throw Exception('Unexpected request: $request');
      }
      final FakeVmServiceRequest fakeRequest = _requests.removeAt(0) as FakeVmServiceRequest;
34 35 36
      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)
37 38 39 40 41 42 43
      );
      if (fakeRequest.close) {
        unawaited(_vmService.dispose());
        expect(_requests, isEmpty);
        return;
      }
      if (fakeRequest.errorCode == null) {
44
        _input.add(json.encode(<String, Object?>{
45 46 47 48 49
          'jsonrpc': '2.0',
          'id': request['id'],
          'result': fakeRequest.jsonResponse ?? <String, Object>{'type': 'Success'},
        }));
      } else {
50
        _input.add(json.encode(<String, Object?>{
51 52
          'jsonrpc': '2.0',
          'id': request['id'],
53
          'error': <String, Object?>{
54
            'code': fakeRequest.errorCode,
55
            'message': 'error',
56 57 58 59 60 61 62 63 64 65 66 67
          }
        }));
      }
      _applyStreamListen();
    });
  }

  final List<VmServiceExpectation> _requests;
  final StreamController<String> _input = StreamController<String>();
  final StreamController<String> _output = StreamController<String>();

  FlutterVmService get vmService => _vmService;
68
  late final FlutterVmService _vmService;
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95


  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({
96
    required this.method,
97 98 99 100 101 102 103 104 105 106 107 108 109
    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.
110 111
  final int? errorCode;
  final Map<String, Object>? args;
112
  final Map<String, Object?>? jsonResponse;
113 114 115 116 117 118 119

  @override
  bool get isRequest => true;
}

class FakeVmServiceStreamResponse implements VmServiceExpectation {
  const FakeVmServiceStreamResponse({
120 121
    required this.event,
    required this.streamId,
122 123 124 125 126 127 128 129
  });

  final vm_service.Event event;
  final String streamId;

  @override
  bool get isRequest => false;
}