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

import 'dart:async';
import 'dart:convert';
import 'dart:io';

9
import 'package:flutter_devicelab/framework/devices.dart';
10
import 'package:flutter_devicelab/framework/framework.dart';
11
import 'package:flutter_devicelab/framework/task_result.dart';
12
import 'package:flutter_devicelab/framework/utils.dart';
13
import 'package:path/path.dart' as path;
14 15
import 'package:vm_service/vm_service.dart';
import 'package:vm_service/vm_service_io.dart';
16 17 18

void main() {
  task(() async {
19
    int? vmServicePort;
20 21 22 23 24

    final Device device = await devices.workingDevice;
    await device.unlock();
    final Directory appDir = dir(path.join(flutterDirectory.path, 'dev/integration_tests/ui'));
    await inDirectory(appDir, () async {
25
      final Completer<void> ready = Completer<void>();
26
      late bool ok;
27 28 29
      print('run: starting...');
      final Process run = await startProcess(
        path.join(flutterDirectory.path, 'bin', 'flutter'),
30
        <String>['run', '--verbose', '--no-fast-start', '--no-publish-port', '--disable-service-auth-codes', '-d', device.deviceId, 'lib/main.dart'],
31 32
      );
      run.stdout
33 34
          .transform<String>(utf8.decoder)
          .transform<String>(const LineSplitter())
35 36
          .listen((String line) {
        print('run:stdout: $line');
37
        if (vmServicePort == null) {
38
          vmServicePort = parseServicePort(line);
39 40 41 42
          if (vmServicePort != null) {
            print('service protocol connection available at port $vmServicePort');
            print('run: ready!');
            ready.complete();
43
            ok = true;
44
          }
45 46 47
        }
      });
      run.stderr
48 49
          .transform<String>(utf8.decoder)
          .transform<String>(const LineSplitter())
50 51 52
          .listen((String line) {
        stderr.writeln('run:stderr: $line');
      });
53
      unawaited(run.exitCode.then<void>((int exitCode) { ok = false; }));
54
      await Future.any<dynamic>(<Future<dynamic>>[ ready.future, run.exitCode ]);
55
      if (!ok) {
56
        throw 'Failed to run test app.';
57
      }
58

59
      final VmService client = await vmServiceConnectUri('ws://localhost:$vmServicePort/ws');
60
      final VM vm = await client.getVM();
61
      final IsolateRef isolate = vm.isolates!.first;
62

63 64 65 66 67 68 69 70 71
      final StreamController<Event> frameEventsController = StreamController<Event>();
      final StreamController<Event> navigationEventsController = StreamController<Event>();
      try {
        await client.streamListen(EventKind.kExtension);
      } catch (err) {
        // Do nothing on errors.
      }
      client.onExtensionEvent.listen((Event event) {
        if (event.extensionKind == 'Flutter.Frame') {
72
          frameEventsController.add(event);
73
        } else if (event.extensionKind == 'Flutter.Navigation') {
74
          navigationEventsController.add(event);
75 76 77
        }
      });

78 79
      final Stream<Event> frameEvents = frameEventsController.stream;
      final Stream<Event> navigationEvents = navigationEventsController.stream;
80

81
      print('reassembling app...');
82 83
      final Future<Event> frameFuture = frameEvents.first;
      await client.callServiceExtension('ext.flutter.reassemble', isolateId: isolate.id);
84 85

      // ensure we get an event
86
      final Event event = await frameFuture;
87
      print('${event.kind}: ${event.data}');
88 89

      // validate the fields
90
      // {number: 8, startTime: 0, elapsed: 1437, build: 600, raster: 800}
91 92 93 94 95 96 97 98 99 100 101
      print(event.extensionData!.data);
      expect(event.extensionData!.data['number'] is int);
      expect((event.extensionData!.data['number'] as int) >= 0);
      expect(event.extensionData!.data['startTime'] is int);
      expect((event.extensionData!.data['startTime'] as int) >= 0);
      expect(event.extensionData!.data['elapsed'] is int);
      expect((event.extensionData!.data['elapsed'] as int) >= 0);
      expect(event.extensionData!.data['build'] is int);
      expect((event.extensionData!.data['build'] as int) >= 0);
      expect(event.extensionData!.data['raster'] is int);
      expect((event.extensionData!.data['raster'] as int) >= 0);
102

103
      final Future<Event> navigationFuture = navigationEvents.first;
104
      // This tap triggers a navigation event.
105
      unawaited(device.tap(100, 200));
106

107
      final Event navigationEvent = await navigationFuture;
108
      // validate the fields
109 110
      expect(navigationEvent.extensionData!.data['route'] is Map<dynamic, dynamic>);
      final Map<dynamic, dynamic> route = navigationEvent.extensionData!.data['route'] as Map<dynamic, dynamic>;
111 112
      expect(route['description'] is String);
      expect(route['settings'] is Map<dynamic, dynamic>);
113
      final Map<dynamic, dynamic> settings = route['settings'] as Map<dynamic, dynamic>;
114
      expect(settings.containsKey('name'));
115

116 117
      run.stdin.write('q');
      final int result = await run.exitCode;
118
      if (result != 0) {
119
        throw 'Received unexpected exit code $result from run process.';
120
      }
121
    });
122
    return TaskResult.success(null);
123 124 125 126
  });
}

void expect(bool value) {
127
  if (!value) {
128
    throw 'failed assertion in service extensions test';
129
  }
130
}