fuchsia_remote_connection_test.dart 12.2 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:fuchsia_remote_debug_protocol/fuchsia_remote_debug_protocol.dart';
6 7
import 'package:test/fake.dart';
import 'package:vm_service/vm_service.dart' as vms;
8

9 10
import 'common.dart';

11 12
void main() {
  group('FuchsiaRemoteConnection.connect', () {
13
    late List<FakePortForwarder> forwardedPorts;
14
    List<FakeVmService> fakeVmServices;
15
    late List<Uri> uriConnections;
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

    setUp(() {
      final List<Map<String, dynamic>> flutterViewCannedResponses =
          <Map<String, dynamic>>[
        <String, dynamic>{
          'views': <Map<String, dynamic>>[
            <String, dynamic>{
              'type': 'FlutterView',
              'id': 'flutterView0',
            },
          ],
        },
        <String, dynamic>{
          'views': <Map<String, dynamic>>[
            <String, dynamic>{
              'type': 'FlutterView',
              'id': 'flutterView1',
              'isolate': <String, dynamic>{
                'type': '@Isolate',
                'fixedId': 'true',
                'id': 'isolates/1',
                'name': 'file://flutterBinary1',
                'number': '1',
              },
40
            },
41 42 43 44 45 46 47 48 49 50 51 52 53 54
          ],
        },
        <String, dynamic>{
          'views': <Map<String, dynamic>>[
            <String, dynamic>{
              'type': 'FlutterView',
              'id': 'flutterView2',
              'isolate': <String, dynamic>{
                'type': '@Isolate',
                'fixedId': 'true',
                'id': 'isolates/2',
                'name': 'file://flutterBinary2',
                'number': '2',
              },
55
            },
56 57 58 59
          ],
        },
      ];

60 61
      forwardedPorts = <FakePortForwarder>[];
      fakeVmServices = <FakeVmService>[];
62
      uriConnections = <Uri>[];
63
      Future<vms.VmService> fakeVmConnectionFunction(
64
        Uri uri, {
65
        Duration? timeout,
66
      }) {
67
        return Future<vms.VmService>(() async {
68 69
          final FakeVmService service = FakeVmService();
          fakeVmServices.add(service);
70
          uriConnections.add(uri);
71
          service.flutterListViews = vms.Response.parse(flutterViewCannedResponses[uri.port]);
72
          return service;
73 74 75
        });
      }

76
      fuchsiaVmServiceConnectionFunction = fakeVmConnectionFunction;
77 78 79
    });

    tearDown(() {
80
      /// Most tests will fake out the port forwarding and connection
81 82 83 84
      /// functions.
      restoreFuchsiaPortForwardingFunction();
      restoreVmServiceConnectionFunction();
    });
85

86
    test('end-to-end with three vm connections and flutter view query', () async {
87
      int port = 0;
88
      Future<PortForwarder> fakePortForwardingFunction(
89 90
        String address,
        int remotePort, [
91 92
        String? interface = '',
        String? configFile,
93 94
      ]) {
        return Future<PortForwarder>(() {
95
          final FakePortForwarder pf = FakePortForwarder();
96
          forwardedPorts.add(pf);
97 98
          pf.port = port++;
          pf.remotePort = remotePort;
99 100 101 102
          return pf;
        });
      }

103 104
      fuchsiaPortForwardingFunction = fakePortForwardingFunction;
      final FakeSshCommandRunner fakeRunner = FakeSshCommandRunner();
105
      // Adds some extra junk to make sure the strings will be cleaned up.
106 107 108 109
      fakeRunner.findResponse = <String>['/hub/blah/blah/blah/vmservice-port\n'];
      fakeRunner.lsResponse = <String>['123\n\n\n', '456  ', '789'];
      fakeRunner.address = 'fe80::8eae:4cff:fef4:9247';
      fakeRunner.interface = 'eno1';
110

111
      final FuchsiaRemoteConnection connection =
112
          await FuchsiaRemoteConnection.connectWithSshCommandRunner(fakeRunner);
113

114
      // [fakePortForwardingFunction] will have returned three different
115 116 117 118 119 120 121 122 123 124
      // forwarded ports, incrementing the port each time by one. (Just a sanity
      // check that the forwarding port was called).
      expect(forwardedPorts.length, 3);
      expect(forwardedPorts[0].remotePort, 123);
      expect(forwardedPorts[1].remotePort, 456);
      expect(forwardedPorts[2].remotePort, 789);
      expect(forwardedPorts[0].port, 0);
      expect(forwardedPorts[1].port, 1);
      expect(forwardedPorts[2].port, 2);

125
      // VMs should be accessed via localhost ports given by
126
      // [fakePortForwardingFunction].
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
      expect(uriConnections[0],
        Uri(scheme:'ws', host:'[::1]', port:0, path:'/ws'));
      expect(uriConnections[1],
        Uri(scheme:'ws', host:'[::1]', port:1, path:'/ws'));
      expect(uriConnections[2],
        Uri(scheme:'ws', host:'[::1]', port:2, path:'/ws'));

      final List<FlutterView> views = await connection.getFlutterViews();
      expect(views, isNot(null));
      expect(views.length, 3);
      // Since name can be null, check for the ID on all of them.
      expect(views[0].id, 'flutterView0');
      expect(views[1].id, 'flutterView1');
      expect(views[2].id, 'flutterView2');

      expect(views[0].name, equals(null));
      expect(views[1].name, 'file://flutterBinary1');
      expect(views[2].name, 'file://flutterBinary2');

      // Ensure the ports are all closed after stop was called.
      await connection.stop();
148 149 150
      expect(forwardedPorts[0].stopped, true);
      expect(forwardedPorts[1].stopped, true);
      expect(forwardedPorts[2].stopped, true);
151 152 153 154
    });

    test('end-to-end with three vms and remote open port', () async {
      int port = 0;
155
      Future<PortForwarder> fakePortForwardingFunction(
156 157
        String address,
        int remotePort, [
158 159
        String? interface = '',
        String? configFile,
160 161
      ]) {
        return Future<PortForwarder>(() {
162
          final FakePortForwarder pf = FakePortForwarder();
163
          forwardedPorts.add(pf);
164 165 166
          pf.port = port++;
          pf.remotePort = remotePort;
          pf.openPortAddress = 'fe80::1:2%eno2';
167 168 169 170
          return pf;
        });
      }

171 172
      fuchsiaPortForwardingFunction = fakePortForwardingFunction;
      final FakeSshCommandRunner fakeRunner = FakeSshCommandRunner();
173
      // Adds some extra junk to make sure the strings will be cleaned up.
174 175 176 177
      fakeRunner.findResponse = <String>['/hub/blah/blah/blah/vmservice-port\n'];
      fakeRunner.lsResponse = <String>['123\n\n\n', '456  ', '789'];
      fakeRunner.address = 'fe80::8eae:4cff:fef4:9247';
      fakeRunner.interface = 'eno1';
178
      final FuchsiaRemoteConnection connection =
179
          await FuchsiaRemoteConnection.connectWithSshCommandRunner(fakeRunner);
180

181
      // [fakePortForwardingFunction] will have returned three different
182 183 184 185 186 187 188 189 190 191
      // forwarded ports, incrementing the port each time by one. (Just a sanity
      // check that the forwarding port was called).
      expect(forwardedPorts.length, 3);
      expect(forwardedPorts[0].remotePort, 123);
      expect(forwardedPorts[1].remotePort, 456);
      expect(forwardedPorts[2].remotePort, 789);
      expect(forwardedPorts[0].port, 0);
      expect(forwardedPorts[1].port, 1);
      expect(forwardedPorts[2].port, 2);

192
      // VMs should be accessed via the alternate address given by
193
      // [fakePortForwardingFunction].
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
      expect(uriConnections[0],
        Uri(scheme:'ws', host:'[fe80::1:2%25eno2]', port:0, path:'/ws'));
      expect(uriConnections[1],
        Uri(scheme:'ws', host:'[fe80::1:2%25eno2]', port:1, path:'/ws'));
      expect(uriConnections[2],
        Uri(scheme:'ws', host:'[fe80::1:2%25eno2]', port:2, path:'/ws'));

      final List<FlutterView> views = await connection.getFlutterViews();
      expect(views, isNot(null));
      expect(views.length, 3);
      // Since name can be null, check for the ID on all of them.
      expect(views[0].id, 'flutterView0');
      expect(views[1].id, 'flutterView1');
      expect(views[2].id, 'flutterView2');

      expect(views[0].name, equals(null));
      expect(views[1].name, 'file://flutterBinary1');
      expect(views[2].name, 'file://flutterBinary2');

      // Ensure the ports are all closed after stop was called.
      await connection.stop();
215 216 217
      expect(forwardedPorts[0].stopped, true);
      expect(forwardedPorts[1].stopped, true);
      expect(forwardedPorts[2].stopped, true);
218 219 220 221
    });

    test('end-to-end with three vms and ipv4', () async {
      int port = 0;
222
      Future<PortForwarder> fakePortForwardingFunction(
223 224
        String address,
        int remotePort, [
225 226
        String? interface = '',
        String? configFile,
227 228
      ]) {
        return Future<PortForwarder>(() {
229
          final FakePortForwarder pf = FakePortForwarder();
230
          forwardedPorts.add(pf);
231 232
          pf.port = port++;
          pf.remotePort = remotePort;
233 234 235 236
          return pf;
        });
      }

237 238
      fuchsiaPortForwardingFunction = fakePortForwardingFunction;
      final FakeSshCommandRunner fakeRunner = FakeSshCommandRunner();
239
      // Adds some extra junk to make sure the strings will be cleaned up.
240 241 242
      fakeRunner.findResponse = <String>['/hub/blah/blah/blah/vmservice-port\n'];
      fakeRunner.lsResponse = <String>['123\n\n\n', '456  ', '789'];
      fakeRunner.address = '196.168.1.4';
243 244

      final FuchsiaRemoteConnection connection =
245
          await FuchsiaRemoteConnection.connectWithSshCommandRunner(fakeRunner);
246

247
      // [fakePortForwardingFunction] will have returned three different
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
      // forwarded ports, incrementing the port each time by one. (Just a sanity
      // check that the forwarding port was called).
      expect(forwardedPorts.length, 3);
      expect(forwardedPorts[0].remotePort, 123);
      expect(forwardedPorts[1].remotePort, 456);
      expect(forwardedPorts[2].remotePort, 789);
      expect(forwardedPorts[0].port, 0);
      expect(forwardedPorts[1].port, 1);
      expect(forwardedPorts[2].port, 2);

      // VMs should be accessed via the ipv4 loopback.
      expect(uriConnections[0],
        Uri(scheme:'ws', host:'127.0.0.1', port:0, path:'/ws'));
      expect(uriConnections[1],
        Uri(scheme:'ws', host:'127.0.0.1', port:1, path:'/ws'));
      expect(uriConnections[2],
        Uri(scheme:'ws', host:'127.0.0.1', port:2, path:'/ws'));

266 267 268 269 270 271 272 273 274 275 276 277 278 279
      final List<FlutterView> views = await connection.getFlutterViews();
      expect(views, isNot(null));
      expect(views.length, 3);
      // Since name can be null, check for the ID on all of them.
      expect(views[0].id, 'flutterView0');
      expect(views[1].id, 'flutterView1');
      expect(views[2].id, 'flutterView2');

      expect(views[0].name, equals(null));
      expect(views[1].name, 'file://flutterBinary1');
      expect(views[2].name, 'file://flutterBinary2');

      // Ensure the ports are all closed after stop was called.
      await connection.stop();
280 281 282
      expect(forwardedPorts[0].stopped, true);
      expect(forwardedPorts[1].stopped, true);
      expect(forwardedPorts[2].stopped, true);
283
    });
284 285

    test('env variable test without remote addr', () async {
286
      Future<void> failingFunction() async {
287 288 289 290 291
        await FuchsiaRemoteConnection.connect();
      }

      // Should fail as no env variable has been passed.
      expect(failingFunction,
Dan Field's avatar
Dan Field committed
292
          throwsA(isA<FuchsiaRemoteConnectionError>()));
293
    });
294 295 296
  });
}

297
class FakeSshCommandRunner extends Fake implements SshCommandRunner {
298 299
  List<String>? findResponse;
  List<String>? lsResponse;
300 301 302
  @override
  Future<List<String>> run(String command) async {
    if (command.startsWith('/bin/find')) {
303
      return findResponse!;
304 305
    }
    if (command.startsWith('/bin/ls')) {
306
      return lsResponse!;
307 308 309 310 311
    }
    throw UnimplementedError(command);
  }

  @override
312
  String interface = '';
313 314

  @override
315
  String address = '';
316 317 318 319 320 321 322

  @override
  String get sshConfigPath => '~/.ssh';
}

class FakePortForwarder extends Fake implements PortForwarder {
  @override
323
  int port = 0;
324 325

  @override
326
  int remotePort = 0;
327

328
  @override
329
  String? openPortAddress;
330

331 332 333 334 335 336 337 338 339
  bool stopped = false;
  @override
  Future<void> stop() async {
    stopped = true;
  }
}

class FakeVmService extends Fake implements vms.VmService {
  bool disposed = false;
340
  vms.Response? flutterListViews;
341 342 343 344 345 346 347

  @override
  Future<void> dispose() async {
    disposed = true;
  }

  @override
348
  Future<vms.Response> callMethod(String method, {String? isolateId, Map<String, dynamic>? args}) async {
349
    if (method == '_flutter.listViews') {
350
      return flutterListViews!;
351 352 353 354 355
    }
    throw UnimplementedError(method);
  }

  @override
356
  Future<void> onDone = Future<void>.value();
357 358 359 360 361 362

  @override
  Future<vms.Version> getVersion() async {
    return vms.Version(major: -1, minor: -1);
  }
}