flutter_tester_device_test.dart 8.88 KB
Newer Older
1 2 3 4 5 6 7
// 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:dds/dds.dart';
8 9
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
10 11
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
12
import 'package:flutter_tools/src/base/platform.dart';
13 14 15
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/test/flutter_tester_device.dart';
16
import 'package:flutter_tools/src/test/font_config_manager.dart';
17
import 'package:flutter_tools/src/vmservice.dart';
18
import 'package:stream_channel/stream_channel.dart';
19
import 'package:test/fake.dart';
20 21

import '../src/context.dart';
22
import '../src/fake_process_manager.dart';
23
import '../src/fake_vm_services.dart';
24 25

void main() {
26 27 28 29
  late FakePlatform platform;
  late FileSystem fileSystem;
  late FakeProcessManager processManager;
  late FlutterTesterTestDevice device;
30 31 32 33 34 35 36 37 38 39 40 41

  setUp(() {
    fileSystem = MemoryFileSystem.test();
    // Not Windows.
    platform = FakePlatform(
      environment: <String, String>{},
    );
    processManager = FakeProcessManager.any();
  });

  FlutterTesterTestDevice createDevice({
    List<String> dartEntrypointArgs = const <String>[],
42
    bool enableVmService = false,
43 44 45 46 47
  }) =>
    TestFlutterTesterDevice(
      platform: platform,
      fileSystem: fileSystem,
      processManager: processManager,
48
      enableVmService: enableVmService,
49
      dartEntrypointArgs: dartEntrypointArgs,
50
      uriConverter: (String input) => '$input/converted',
51 52 53 54
    );

  group('The FLUTTER_TEST environment variable is passed to the test process', () {
    setUp(() {
55
      processManager = FakeProcessManager.list(<FakeCommand>[]);
56 57 58 59 60 61 62 63
      device = createDevice();

      fileSystem
          .file('.dart_tool/package_config.json')
        ..createSync(recursive: true)
        ..writeAsStringSync('{"configVersion":2,"packages":[]}');
    });

64 65 66
    FakeCommand flutterTestCommand(String expectedFlutterTestValue) {
      return FakeCommand(command: const <String>[
        '/',
67
        '--disable-vm-service',
68 69 70 71 72 73 74 75
        '--ipv6',
        '--enable-checked-mode',
        '--verify-entry-points',
        '--enable-software-rendering',
        '--skia-deterministic-rendering',
        '--enable-dart-profiling',
        '--non-interactive',
        '--use-test-fonts',
76
        '--disable-asset-fonts',
77
        '--packages=.dart_tool/package_config.json',
78
        'example.dill',
79 80 81
      ], environment: <String, String>{
        'FLUTTER_TEST': expectedFlutterTestValue,
        'FONTCONFIG_FILE': device.fontConfigManager.fontConfigFile.path,
82
        'SERVER_PORT': '0',
83
        'APP_NAME': '',
84 85 86 87
      });
    }

    testUsingContext('as true when not originally set', () async {
88 89
      processManager.addCommand(flutterTestCommand('true'));

90
      await device.start('example.dill');
91
      expect(processManager, hasNoRemainingExpectations);
92 93 94 95
    });

    testUsingContext('as true when set to true', () async {
      platform.environment = <String, String>{'FLUTTER_TEST': 'true'};
96 97
      processManager.addCommand(flutterTestCommand('true'));

98
      await device.start('example.dill');
99
      expect(processManager, hasNoRemainingExpectations);
100 101 102 103
    });

    testUsingContext('as false when set to false', () async {
      platform.environment = <String, String>{'FLUTTER_TEST': 'false'};
104 105
      processManager.addCommand(flutterTestCommand('false'));

106
      await device.start('example.dill');
107
      expect(processManager, hasNoRemainingExpectations);
108 109 110 111
    });

    testUsingContext('unchanged when set', () async {
      platform.environment = <String, String>{'FLUTTER_TEST': 'neither true nor false'};
112 113
      processManager.addCommand(flutterTestCommand('neither true nor false'));

114
      await device.start('example.dill');
115
      expect(processManager, hasNoRemainingExpectations);
116 117 118 119 120 121 122 123 124
    });
  });

  group('Dart Entrypoint Args', () {
    setUp(() {
      processManager = FakeProcessManager.list(<FakeCommand>[
        const FakeCommand(
          command: <String>[
            '/',
125
            '--disable-vm-service',
126 127 128 129 130 131 132 133
            '--ipv6',
            '--enable-checked-mode',
            '--verify-entry-points',
            '--enable-software-rendering',
            '--skia-deterministic-rendering',
            '--enable-dart-profiling',
            '--non-interactive',
            '--use-test-fonts',
134
            '--disable-asset-fonts',
135 136 137
            '--packages=.dart_tool/package_config.json',
            '--foo',
            '--bar',
138
            'example.dill',
139 140 141
          ],
          stdout: 'success',
          stderr: 'failure',
142
        ),
143 144 145 146 147
      ]);
      device = createDevice(dartEntrypointArgs: <String>['--foo', '--bar']);
    });

    testUsingContext('Can pass additional arguments to tester binary', () async {
148
      await device.start('example.dill');
149

150
      expect(processManager, hasNoRemainingExpectations);
151 152 153 154 155 156 157 158 159
    });
  });

  group('DDS', () {
    setUp(() {
      processManager = FakeProcessManager.list(<FakeCommand>[
        const FakeCommand(
          command: <String>[
            '/',
160
            '--vm-service-port=0',
161 162 163 164 165 166 167 168
            '--ipv6',
            '--enable-checked-mode',
            '--verify-entry-points',
            '--enable-software-rendering',
            '--skia-deterministic-rendering',
            '--enable-dart-profiling',
            '--non-interactive',
            '--use-test-fonts',
169
            '--disable-asset-fonts',
170
            '--packages=.dart_tool/package_config.json',
171
            'example.dill',
172
          ],
173
          stdout: 'The Dart VM service is listening on http://localhost:1234',
174
          stderr: 'failure',
175
        ),
176
      ]);
177
      device = createDevice(enableVmService: true);
178 179
    });

180
    testUsingContext('skips setting VM Service port and uses the input port for DDS instead', () async {
181
      await device.start('example.dill');
182
      await device.vmServiceUri;
183 184 185 186

      final Uri uri = await (device as TestFlutterTesterDevice).ddsServiceUriFuture();
      expect(uri.port, 1234);
    });
187 188 189

    testUsingContext('sets up UriConverter from context', () async {
      await device.start('example.dill');
190
      await device.vmServiceUri;
191 192 193 194 195 196 197 198

      final FakeDartDevelopmentService dds = (device as TestFlutterTesterDevice).dds
      as FakeDartDevelopmentService;
      final String? result = dds
          .uriConverter
          ?.call('test');
      expect(result, 'test/converted');
    });
199 200 201 202 203 204 205 206
  });
}

/// A Flutter Tester device.
///
/// Uses a mock HttpServer. We don't want to bind random ports in our CI hosts.
class TestFlutterTesterDevice extends FlutterTesterTestDevice {
  TestFlutterTesterDevice({
207 208 209
    required super.platform,
    required super.fileSystem,
    required super.processManager,
210
    required super.enableVmService,
211
    required List<String> dartEntrypointArgs,
212
    required UriConverter uriConverter,
213 214 215
  }) : super(
    id: 999,
    shellPath: '/',
216
    logger: BufferLogger.test(),
217 218 219 220 221 222 223 224 225 226 227
    debuggingOptions: DebuggingOptions.enabled(
      const BuildInfo(
        BuildMode.debug,
        '',
        treeShakeIcons: false,
      ),
      hostVmServicePort: 1234,
      dartEntrypointArgs: dartEntrypointArgs,
    ),
    machine: false,
    host: InternetAddress.loopbackIPv6,
228
    testAssetDirectory: null,
229 230 231 232
    flutterProject: null,
    icudtlPath: null,
    compileExpression: null,
    fontConfigManager: FontConfigManager(),
233
    uriConverter: uriConverter,
234
  );
235
  late DartDevelopmentService dds;
236 237 238 239 240 241

  final Completer<Uri> _ddsServiceUriCompleter = Completer<Uri>();

  Future<Uri> ddsServiceUriFuture() => _ddsServiceUriCompleter.future;

  @override
242 243 244 245
  Future<DartDevelopmentService> startDds(
    Uri uri, {
    UriConverter? uriConverter,
  }) async {
246
    _ddsServiceUriCompleter.complete(uri);
247 248 249 250 251 252
    dds = FakeDartDevelopmentService(
      Uri.parse('http://localhost:${debuggingOptions.hostVmServicePort}'),
      Uri.parse('http://localhost:8080'),
      uriConverter: uriConverter,
    );
    return dds;
253 254
  }

255 256 257 258 259 260 261 262 263 264 265
  @override
  Future<FlutterVmService> connectToVmServiceImpl(
    Uri httpUri, {
    CompileExpression? compileExpression,
    required Logger logger,
  }) async {
    return FakeVmServiceHost(requests: <VmServiceExpectation>[
      const FakeVmServiceRequest(method: '_serveObservatory'),
    ]).vmService;
  }

266
  @override
267
  Future<HttpServer> bind(InternetAddress? host, int port) async => FakeHttpServer();
268 269 270 271 272

  @override
  Future<StreamChannel<String>> get remoteChannel async => StreamChannelController<String>().foreign;
}

273
class FakeDartDevelopmentService extends Fake implements DartDevelopmentService {
274
  FakeDartDevelopmentService(this.uri, this.original, {this.uriConverter});
275 276

  final Uri original;
277
  final UriConverter? uriConverter;
278 279 280 281 282 283 284 285 286 287 288

  @override
  final Uri uri;

  @override
  Uri get remoteVmServiceUri => original;
}
class FakeHttpServer extends Fake implements HttpServer {
  @override
  int get port => 0;
}