service_extensions_test.dart 33.5 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 9 10 11 12 13 14
// 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:ui' as ui;

import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
15
import 'package:flutter_test/flutter_test.dart';
16 17

class TestServiceExtensionsBinding extends BindingBase
18 19
  with SchedulerBinding,
       ServicesBinding,
20
       GestureBinding,
21
       PaintingBinding,
22
       SemanticsBinding,
23
       RendererBinding,
24
       WidgetsBinding {
25 26 27

  final Map<String, ServiceExtensionCallback> extensions = <String, ServiceExtensionCallback>{};

28 29
  final Map<String, List<Map<String, dynamic>>> eventsDispatched = <String, List<Map<String, dynamic>>>{};

30
  @override
31
  @protected
32
  void registerServiceExtension({
33 34
    required String name,
    required ServiceExtensionCallback callback,
35 36 37 38 39
  }) {
    expect(extensions.containsKey(name), isFalse);
    extensions[name] = callback;
  }

40
  @override
41
  void postEvent(String eventKind, Map<String, dynamic> eventData) {
42 43 44 45 46 47 48 49 50 51 52 53
    getEventsDispatched(eventKind).add(eventData);
  }

  List<Map<String, dynamic>> getEventsDispatched(String eventKind) {
    return eventsDispatched.putIfAbsent(eventKind, () => <Map<String, dynamic>>[]);
  }

  Iterable<Map<String, dynamic>> getServiceExtensionStateChangedEvents(String extensionName) {
    return getEventsDispatched('Flutter.ServiceExtensionStateChanged')
      .where((Map<String, dynamic> event) => event['extension'] == extensionName);
  }

54
  Future<Map<String, dynamic>> testExtension(String name, Map<String, String> arguments) {
55
    expect(extensions.containsKey(name), isTrue);
56
    return extensions[name]!(arguments);
57 58 59
  }

  int reassembled = 0;
60
  bool pendingReassemble = false;
61
  @override
62
  Future<void> performReassemble() {
63
    reassembled += 1;
64
    pendingReassemble = true;
65
    return super.performReassemble();
66 67 68 69 70
  }

  bool frameScheduled = false;
  @override
  void scheduleFrame() {
71
    ensureFrameCallbacksRegistered();
72 73
    frameScheduled = true;
  }
74
  Future<void> doFrame() async {
75
    frameScheduled = false;
76
    ui.window.onBeginFrame?.call(Duration.zero);
77
    await flushMicrotasks();
78 79
    ui.window.onDrawFrame?.call();
    ui.window.onReportTimings?.call(<ui.FrameTiming>[]);
80 81
  }

82 83 84 85 86 87 88 89 90 91 92
  @override
  void scheduleForcedFrame() {
    expect(true, isFalse);
  }

  @override
  void scheduleWarmUpFrame() {
    expect(pendingReassemble, isTrue);
    pendingReassemble = false;
  }

93 94
  Future<void> flushMicrotasks() {
    final Completer<void> completer = Completer<void>();
95
    Timer.run(completer.complete);
96 97 98 99
    return completer.future;
  }
}

100
late TestServiceExtensionsBinding binding;
101

102
Future<Map<String, dynamic>> hasReassemble(Future<Map<String, dynamic>> pendingResult) async {
103 104 105 106 107 108
  bool completed = false;
  pendingResult.whenComplete(() { completed = true; });
  expect(binding.frameScheduled, isFalse);
  await binding.flushMicrotasks();
  expect(binding.frameScheduled, isTrue);
  expect(completed, isFalse);
109
  await binding.flushMicrotasks();
110
  await binding.doFrame();
111 112 113 114 115 116
  await binding.flushMicrotasks();
  expect(completed, isTrue);
  expect(binding.frameScheduled, isFalse);
  return pendingResult;
}

117
void main() {
118
  final List<String?> console = <String?>[];
119

120
  setUpAll(() async {
121
    binding = TestServiceExtensionsBinding()..scheduleFrame();
122
    expect(binding.frameScheduled, isTrue);
123 124 125 126 127 128

    // We need to test this service extension here because the result is true
    // after the first binding.doFrame() call.
    Map<String, dynamic> firstFrameResult;
    expect(binding.debugDidSendFirstFrameEvent, isFalse);
    firstFrameResult = await binding.testExtension('didSendFirstFrameEvent', <String, String>{});
129
    expect(firstFrameResult, <String, String>{'enabled': 'false'});
130

131 132 133 134
    expect(binding.firstFrameRasterized, isFalse);
    firstFrameResult = await binding.testExtension('didSendFirstFrameRasterizedEvent', <String, String>{});
    expect(firstFrameResult, <String, String>{'enabled': 'false'});

135
    await binding.doFrame();
136 137 138

    expect(binding.debugDidSendFirstFrameEvent, isTrue);
    firstFrameResult = await binding.testExtension('didSendFirstFrameEvent', <String, String>{});
139
    expect(firstFrameResult, <String, String>{'enabled': 'true'});
140

141 142 143 144
    expect(binding.firstFrameRasterized, isTrue);
    firstFrameResult = await binding.testExtension('didSendFirstFrameRasterizedEvent', <String, String>{});
    expect(firstFrameResult, <String, String>{'enabled': 'true'});

145 146 147
    expect(binding.frameScheduled, isFalse);

    expect(debugPrint, equals(debugPrintThrottled));
148
    debugPrint = (String? message, { int? wrapWidth }) {
149 150 151 152
      console.add(message);
    };
  });

153 154 155
  tearDownAll(() async {
    // See widget_inspector_test.dart for tests of the ext.flutter.inspector
    // service extensions included in this count.
156
    int widgetInspectorExtensionCount = 16;
157 158 159 160 161 162
    if (WidgetInspectorService.instance.isWidgetCreationTracked()) {
      // Some inspector extensions are only exposed if widget creation locations
      // are tracked.
      widgetInspectorExtensionCount += 2;
    }

163 164
    // The following service extensions are disabled in web:
    // 1. exit
165 166
    // 2. showPerformanceOverlay
    const int disabledExtensions = kIsWeb ? 2 : 0;
167 168
    // If you add a service extension... TEST IT! :-)
    // ...then increment this number.
169
    expect(binding.extensions.length, 31 + widgetInspectorExtensionCount - disabledExtensions);
170 171 172 173 174

    expect(console, isEmpty);
    debugPrint = debugPrintThrottled;
  });

175 176 177
  // The following list is alphabetical, one test per extension.

  test('Service extensions - debugAllowBanner', () async {
178
    Map<String, dynamic> result;
179 180 181 182

    expect(binding.frameScheduled, isFalse);
    expect(WidgetsApp.debugAllowBannerOverride, true);
    result = await binding.testExtension('debugAllowBanner', <String, String>{});
183
    expect(result, <String, String>{'enabled': 'true'});
184
    expect(WidgetsApp.debugAllowBannerOverride, true);
185 186
    result = await binding.testExtension('debugAllowBanner', <String, String>{'enabled': 'false'});
    expect(result, <String, String>{'enabled': 'false'});
187 188
    expect(WidgetsApp.debugAllowBannerOverride, false);
    result = await binding.testExtension('debugAllowBanner', <String, String>{});
189
    expect(result, <String, String>{'enabled': 'false'});
190
    expect(WidgetsApp.debugAllowBannerOverride, false);
191 192
    result = await binding.testExtension('debugAllowBanner', <String, String>{'enabled': 'true'});
    expect(result, <String, String>{'enabled': 'true'});
193 194
    expect(WidgetsApp.debugAllowBannerOverride, true);
    result = await binding.testExtension('debugAllowBanner', <String, String>{});
195
    expect(result, <String, String>{'enabled': 'true'});
196 197 198 199
    expect(WidgetsApp.debugAllowBannerOverride, true);
    expect(binding.frameScheduled, isFalse);
  });

200 201 202 203 204 205
  test('Service extensions - debugCheckElevationsEnabled', () async {
    expect(binding.frameScheduled, isFalse);
    expect(debugCheckElevationsEnabled, false);

    bool lastValue = false;
    Future<void> _updateAndCheck(bool newValue) async {
206
      Map<String, dynamic>? result;
207 208
      binding.testExtension(
        'debugCheckElevationsEnabled',
209
        <String, String>{'enabled': '$newValue'},
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
      ).then((Map<String, dynamic> answer) => result = answer);
      await binding.flushMicrotasks();
      expect(binding.frameScheduled, lastValue != newValue);
      await binding.doFrame();
      await binding.flushMicrotasks();
      expect(result, <String, String>{'enabled': '$newValue'});
      expect(debugCheckElevationsEnabled, newValue);
      lastValue = newValue;
    }

    await _updateAndCheck(false);
    await _updateAndCheck(true);
    await _updateAndCheck(true);
    await _updateAndCheck(false);
    await _updateAndCheck(false);
    expect(binding.frameScheduled, isFalse);
  });

228
  test('Service extensions - debugDumpApp', () async {
229
    final Map<String, dynamic> result = await binding.testExtension('debugDumpApp', <String, String>{});
230

231 232 233
    expect(result, <String, dynamic>{
      'data': matches('TestServiceExtensionsBinding - DEBUG MODE\n<no tree currently mounted>'),
    });
234 235 236
  });

  test('Service extensions - debugDumpRenderTree', () async {
237
    await binding.doFrame();
238 239 240 241
    final Map<String, dynamic> result = await binding.testExtension('debugDumpRenderTree', <String, String>{});

    expect(result, <String, dynamic>{
      'data': matches(
242
        r'^'
243
        r'RenderView#[0-9a-f]{5}\n'
244
        r'   debug mode enabled - [a-zA-Z]+\n'
245 246 247
        r'   window size: Size\(2400\.0, 1800\.0\) \(in physical pixels\)\n'
        r'   device pixel ratio: 3\.0 \(physical pixels per logical pixel\)\n'
        r'   configuration: Size\(800\.0, 600\.0\) at 3\.0x \(in logical pixels\)\n'
248
        r'$',
249
      ),
250
    });
251 252
  });

253 254
  test('Service extensions - debugDumpLayerTree', () async {
    await binding.doFrame();
255 256 257 258
    final Map<String, dynamic> result = await binding.testExtension('debugDumpLayerTree', <String, String>{});

    expect(result, <String, dynamic>{
      'data': matches(
259
        r'^'
260 261
        r'TransformLayer#[0-9a-f]{5}\n'
        r'   owner: RenderView#[0-9a-f]{5}\n'
262
        r'   creator: RenderView\n'
263
        r'   engine layer: (TransformEngineLayer|PersistedTransform)#[0-9a-f]{5}\n'
264 265
        r'   offset: Offset\(0\.0, 0\.0\)\n'
        r'   transform:\n'
266 267
        r'     \[0] 3\.0,0\.0,0\.0,0\.0\n'
        r'     \[1] 0\.0,3\.0,0\.0,0\.0\n'
268 269
        r'     \[2] 0\.0,0\.0,1\.0,0\.0\n'
        r'     \[3] 0\.0,0\.0,0\.0,1\.0\n'
270
        r'$',
271
      ),
272
    });
273 274
  });

275
  test('Service extensions - debugDumpSemanticsTreeInTraversalOrder', () async {
276
    await binding.doFrame();
277 278 279
    final Map<String, dynamic> result = await binding.testExtension('debugDumpSemanticsTreeInTraversalOrder', <String, String>{});

    expect(result, <String, String>{
280
      'data': 'Semantics not collected.',
281
    });
282 283 284 285
  });

  test('Service extensions - debugDumpSemanticsTreeInInverseHitTestOrder', () async {
    await binding.doFrame();
286 287 288
    final Map<String, dynamic> result = await binding.testExtension('debugDumpSemanticsTreeInInverseHitTestOrder', <String, String>{});

    expect(result, <String, String>{
289
      'data': 'Semantics not collected.',
290
    });
291 292
  });

293
  test('Service extensions - debugPaint', () async {
294 295
    final Iterable<Map<String, dynamic>> extensionChangedEvents = binding.getServiceExtensionStateChangedEvents('ext.flutter.debugPaint');
    Map<String, dynamic> extensionChangedEvent;
296 297
    Map<String, dynamic> result;
    Future<Map<String, dynamic>> pendingResult;
298 299 300 301 302
    bool completed;

    expect(binding.frameScheduled, isFalse);
    expect(debugPaintSizeEnabled, false);
    result = await binding.testExtension('debugPaint', <String, String>{});
303
    expect(result, <String, String>{'enabled': 'false'});
304
    expect(debugPaintSizeEnabled, false);
305
    expect(extensionChangedEvents, isEmpty);
306
    expect(binding.frameScheduled, isFalse);
307
    pendingResult = binding.testExtension('debugPaint', <String, String>{'enabled': 'true'});
308 309 310 311 312
    completed = false;
    pendingResult.whenComplete(() { completed = true; });
    await binding.flushMicrotasks();
    expect(binding.frameScheduled, isTrue);
    expect(completed, isFalse);
313
    await binding.doFrame();
314 315 316 317
    await binding.flushMicrotasks();
    expect(completed, isTrue);
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
318
    expect(result, <String, String>{'enabled': 'true'});
319
    expect(debugPaintSizeEnabled, true);
320 321 322 323
    expect(extensionChangedEvents.length, 1);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.debugPaint');
    expect(extensionChangedEvent['value'], 'true');
324
    result = await binding.testExtension('debugPaint', <String, String>{});
325
    expect(result, <String, String>{'enabled': 'true'});
326
    expect(debugPaintSizeEnabled, true);
327
    expect(extensionChangedEvents.length, 1);
328
    expect(binding.frameScheduled, isFalse);
329
    pendingResult = binding.testExtension('debugPaint', <String, String>{'enabled': 'false'});
330 331
    await binding.flushMicrotasks();
    expect(binding.frameScheduled, isTrue);
332
    await binding.doFrame();
333 334
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
335
    expect(result, <String, String>{'enabled': 'false'});
336
    expect(debugPaintSizeEnabled, false);
337 338 339 340
    expect(extensionChangedEvents.length, 2);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.debugPaint');
    expect(extensionChangedEvent['value'], 'false');
341
    result = await binding.testExtension('debugPaint', <String, String>{});
342
    expect(result, <String, String>{'enabled': 'false'});
343
    expect(debugPaintSizeEnabled, false);
344
    expect(extensionChangedEvents.length, 2);
345 346 347
    expect(binding.frameScheduled, isFalse);
  });

348
  test('Service extensions - debugPaintBaselinesEnabled', () async {
349 350
    Map<String, dynamic> result;
    Future<Map<String, dynamic>> pendingResult;
351 352 353 354 355
    bool completed;

    expect(binding.frameScheduled, isFalse);
    expect(debugPaintBaselinesEnabled, false);
    result = await binding.testExtension('debugPaintBaselinesEnabled', <String, String>{});
356
    expect(result, <String, String>{'enabled': 'false'});
357 358
    expect(debugPaintBaselinesEnabled, false);
    expect(binding.frameScheduled, isFalse);
359
    pendingResult = binding.testExtension('debugPaintBaselinesEnabled', <String, String>{'enabled': 'true'});
360 361 362 363 364 365 366 367 368 369
    completed = false;
    pendingResult.whenComplete(() { completed = true; });
    await binding.flushMicrotasks();
    expect(binding.frameScheduled, isTrue);
    expect(completed, isFalse);
    await binding.doFrame();
    await binding.flushMicrotasks();
    expect(completed, isTrue);
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
370
    expect(result, <String, String>{'enabled': 'true'});
371 372
    expect(debugPaintBaselinesEnabled, true);
    result = await binding.testExtension('debugPaintBaselinesEnabled', <String, String>{});
373
    expect(result, <String, String>{'enabled': 'true'});
374 375
    expect(debugPaintBaselinesEnabled, true);
    expect(binding.frameScheduled, isFalse);
376
    pendingResult = binding.testExtension('debugPaintBaselinesEnabled', <String, String>{'enabled': 'false'});
377 378 379 380 381
    await binding.flushMicrotasks();
    expect(binding.frameScheduled, isTrue);
    await binding.doFrame();
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
382
    expect(result, <String, String>{'enabled': 'false'});
383 384
    expect(debugPaintBaselinesEnabled, false);
    result = await binding.testExtension('debugPaintBaselinesEnabled', <String, String>{});
385
    expect(result, <String, String>{'enabled': 'false'});
386 387 388 389
    expect(debugPaintBaselinesEnabled, false);
    expect(binding.frameScheduled, isFalse);
  });

390 391
  test('Service extensions - invertOversizedImages', () async {
    Map<String, dynamic> result;
392 393
    Future<Map<String, dynamic>> pendingResult;
    bool completed;
394 395 396 397 398 399

    expect(binding.frameScheduled, isFalse);
    expect(debugInvertOversizedImages, false);
    result = await binding.testExtension('invertOversizedImages', <String, String>{});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugInvertOversizedImages, false);
400 401 402 403 404 405 406 407 408 409 410 411 412
    expect(binding.frameScheduled, isFalse);

    pendingResult = binding.testExtension('invertOversizedImages', <String, String>{'enabled': 'true'});
    completed = false;
    pendingResult.whenComplete(() { completed = true; });
    await binding.flushMicrotasks();
    expect(binding.frameScheduled, isTrue);
    expect(completed, isFalse);
    await binding.doFrame();
    await binding.flushMicrotasks();
    expect(completed, isTrue);
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
413 414
    expect(result, <String, String>{'enabled': 'true'});
    expect(debugInvertOversizedImages, true);
415

416 417 418
    result = await binding.testExtension('invertOversizedImages', <String, String>{});
    expect(result, <String, String>{'enabled': 'true'});
    expect(debugInvertOversizedImages, true);
419 420 421 422 423 424 425 426
    expect(binding.frameScheduled, isFalse);

    pendingResult = binding.testExtension('invertOversizedImages', <String, String>{'enabled': 'false'});
    await binding.flushMicrotasks();
    expect(binding.frameScheduled, isTrue);
    await binding.doFrame();
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
427 428
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugInvertOversizedImages, false);
429

430 431 432 433 434 435
    result = await binding.testExtension('invertOversizedImages', <String, String>{});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugInvertOversizedImages, false);
    expect(binding.frameScheduled, isFalse);
  });

436 437 438 439 440 441 442
  test('Service extensions - profileWidgetBuilds', () async {
    Map<String, dynamic> result;

    expect(binding.frameScheduled, isFalse);
    expect(debugProfileBuildsEnabled, false);

    result = await binding.testExtension('profileWidgetBuilds', <String, String>{});
443
    expect(result, <String, String>{'enabled': 'false'});
444 445
    expect(debugProfileBuildsEnabled, false);

446 447
    result = await binding.testExtension('profileWidgetBuilds', <String, String>{'enabled': 'true'});
    expect(result, <String, String>{'enabled': 'true'});
448 449 450
    expect(debugProfileBuildsEnabled, true);

    result = await binding.testExtension('profileWidgetBuilds', <String, String>{});
451
    expect(result, <String, String>{'enabled': 'true'});
452 453
    expect(debugProfileBuildsEnabled, true);

454 455
    result = await binding.testExtension('profileWidgetBuilds', <String, String>{'enabled': 'false'});
    expect(result, <String, String>{'enabled': 'false'});
456 457 458
    expect(debugProfileBuildsEnabled, false);

    result = await binding.testExtension('profileWidgetBuilds', <String, String>{});
459
    expect(result, <String, String>{'enabled': 'false'});
460 461 462 463 464
    expect(debugProfileBuildsEnabled, false);

    expect(binding.frameScheduled, isFalse);
  });

465
  test('Service extensions - evict', () async {
466
    Map<String, dynamic> result;
467 468 469
    bool completed;

    completed = false;
470
    ServicesBinding.instance!.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', (ByteData? message) async {
471
      expect(utf8.decode(message!.buffer.asUint8List()), 'test');
472
      completed = true;
473
      return ByteData(5); // 0x0000000000
474 475
    });
    bool data;
476 477 478 479
    data = await rootBundle.loadStructuredData<bool>('test', (String value) async {
      expect(value, '\x00\x00\x00\x00\x00');
      return true;
    });
480 481 482
    expect(data, isTrue);
    expect(completed, isTrue);
    completed = false;
483
    data = await rootBundle.loadStructuredData('test', (String value) async {
484
      throw Error();
485
    });
486 487
    expect(data, isTrue);
    expect(completed, isFalse);
488 489
    result = await binding.testExtension('evict', <String, String>{'value': 'test'});
    expect(result, <String, String>{'value': ''});
490
    expect(completed, isFalse);
491 492 493 494
    data = await rootBundle.loadStructuredData<bool>('test', (String value) async {
      expect(value, '\x00\x00\x00\x00\x00');
      return false;
    });
495 496
    expect(data, isFalse);
    expect(completed, isTrue);
497
    ServicesBinding.instance!.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', null);
498 499 500 501
  });

  test('Service extensions - exit', () async {
    // no test for _calling_ 'exit', because that should terminate the process!
502 503
    // Not expecting extension to be available for web platform.
    expect(binding.extensions.containsKey('exit'), !isBrowser);
504 505
  });

506
  test('Service extensions - platformOverride', () async {
507 508
    final Iterable<Map<String, dynamic>> extensionChangedEvents = binding.getServiceExtensionStateChangedEvents('ext.flutter.platformOverride');
    Map<String, dynamic> extensionChangedEvent;
509
    Map<String, dynamic> result;
510 511 512 513 514 515

    expect(binding.reassembled, 0);
    expect(defaultTargetPlatform, TargetPlatform.android);
    result = await binding.testExtension('platformOverride', <String, String>{});
    expect(result, <String, String>{'value': 'android'});
    expect(defaultTargetPlatform, TargetPlatform.android);
516
    expect(extensionChangedEvents, isEmpty);
517 518 519 520
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'iOS'}));
    expect(result, <String, String>{'value': 'iOS'});
    expect(binding.reassembled, 1);
    expect(defaultTargetPlatform, TargetPlatform.iOS);
521 522 523 524
    expect(extensionChangedEvents.length, 1);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'iOS');
Dan Field's avatar
Dan Field committed
525 526 527 528 529 530 531 532
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'macOS'}));
    expect(result, <String, String>{'value': 'macOS'});
    expect(binding.reassembled, 2);
    expect(defaultTargetPlatform, TargetPlatform.macOS);
    expect(extensionChangedEvents.length, 2);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'macOS');
533 534
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'android'}));
    expect(result, <String, String>{'value': 'android'});
Dan Field's avatar
Dan Field committed
535
    expect(binding.reassembled, 3);
536
    expect(defaultTargetPlatform, TargetPlatform.android);
Dan Field's avatar
Dan Field committed
537
    expect(extensionChangedEvents.length, 3);
538 539 540
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'android');
541 542
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'fuchsia'}));
    expect(result, <String, String>{'value': 'fuchsia'});
Dan Field's avatar
Dan Field committed
543
    expect(binding.reassembled, 4);
544
    expect(defaultTargetPlatform, TargetPlatform.fuchsia);
Dan Field's avatar
Dan Field committed
545
    expect(extensionChangedEvents.length, 4);
546 547 548
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'fuchsia');
549 550
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'default'}));
    expect(result, <String, String>{'value': 'android'});
Dan Field's avatar
Dan Field committed
551
    expect(binding.reassembled, 5);
552
    expect(defaultTargetPlatform, TargetPlatform.android);
Dan Field's avatar
Dan Field committed
553
    expect(extensionChangedEvents.length, 5);
554 555 556
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'android');
557 558
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'iOS'}));
    expect(result, <String, String>{'value': 'iOS'});
Dan Field's avatar
Dan Field committed
559
    expect(binding.reassembled, 6);
560
    expect(defaultTargetPlatform, TargetPlatform.iOS);
Dan Field's avatar
Dan Field committed
561
    expect(extensionChangedEvents.length, 6);
562 563 564
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'iOS');
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'linux'}));
    expect(result, <String, String>{'value': 'linux'});
    expect(binding.reassembled, 7);
    expect(defaultTargetPlatform, TargetPlatform.linux);
    expect(extensionChangedEvents.length, 7);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'linux');
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'windows'}));
    expect(result, <String, String>{'value': 'windows'});
    expect(binding.reassembled, 8);
    expect(defaultTargetPlatform, TargetPlatform.windows);
    expect(extensionChangedEvents.length, 8);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'windows');
581 582
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'bogus'}));
    expect(result, <String, String>{'value': 'android'});
583
    expect(binding.reassembled, 9);
584
    expect(defaultTargetPlatform, TargetPlatform.android);
585
    expect(extensionChangedEvents.length, 9);
586 587 588
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'android');
589 590 591
    binding.reassembled = 0;
  });

592
  test('Service extensions - repaintRainbow', () async {
593 594
    Map<String, dynamic> result;
    Future<Map<String, dynamic>> pendingResult;
595 596 597 598 599
    bool completed;

    expect(binding.frameScheduled, isFalse);
    expect(debugRepaintRainbowEnabled, false);
    result = await binding.testExtension('repaintRainbow', <String, String>{});
600
    expect(result, <String, String>{'enabled': 'false'});
601 602
    expect(debugRepaintRainbowEnabled, false);
    expect(binding.frameScheduled, isFalse);
603
    pendingResult = binding.testExtension('repaintRainbow', <String, String>{'enabled': 'true'});
604 605 606 607 608 609
    completed = false;
    pendingResult.whenComplete(() { completed = true; });
    await binding.flushMicrotasks();
    expect(completed, true);
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
610
    expect(result, <String, String>{'enabled': 'true'});
611 612
    expect(debugRepaintRainbowEnabled, true);
    result = await binding.testExtension('repaintRainbow', <String, String>{});
613
    expect(result, <String, String>{'enabled': 'true'});
614 615
    expect(debugRepaintRainbowEnabled, true);
    expect(binding.frameScheduled, isFalse);
616
    pendingResult = binding.testExtension('repaintRainbow', <String, String>{'enabled': 'false'});
617 618 619 620 621
    completed = false;
    pendingResult.whenComplete(() { completed = true; });
    await binding.flushMicrotasks();
    expect(completed, false);
    expect(binding.frameScheduled, isTrue);
622
    await binding.doFrame();
623 624 625 626
    await binding.flushMicrotasks();
    expect(completed, true);
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
627
    expect(result, <String, String>{'enabled': 'false'});
628 629
    expect(debugRepaintRainbowEnabled, false);
    result = await binding.testExtension('repaintRainbow', <String, String>{});
630
    expect(result, <String, String>{'enabled': 'false'});
631 632 633 634 635
    expect(debugRepaintRainbowEnabled, false);
    expect(binding.frameScheduled, isFalse);
  });

  test('Service extensions - reassemble', () async {
636 637
    Map<String, dynamic> result;
    Future<Map<String, dynamic>> pendingResult;
638 639 640 641 642 643 644 645 646
    bool completed;

    completed = false;
    expect(binding.reassembled, 0);
    pendingResult = binding.testExtension('reassemble', <String, String>{});
    pendingResult.whenComplete(() { completed = true; });
    await binding.flushMicrotasks();
    expect(binding.frameScheduled, isTrue);
    expect(completed, false);
647
    await binding.flushMicrotasks();
648
    await binding.doFrame();
649 650 651 652 653 654 655 656 657
    await binding.flushMicrotasks();
    expect(completed, true);
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
    expect(result, <String, String>{});
    expect(binding.reassembled, 1);
  });

  test('Service extensions - showPerformanceOverlay', () async {
658
    Map<String, dynamic> result;
659

660 661 662 663 664 665
    // The performance overlay service extension is disabled on the web.
    if (kIsWeb) {
      expect(binding.extensions.containsKey('showPerformanceOverlay'), isFalse);
      return;
    }

666 667 668
    expect(binding.frameScheduled, isFalse);
    expect(WidgetsApp.showPerformanceOverlayOverride, false);
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{});
669
    expect(result, <String, String>{'enabled': 'false'});
670
    expect(WidgetsApp.showPerformanceOverlayOverride, false);
671 672
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{'enabled': 'true'});
    expect(result, <String, String>{'enabled': 'true'});
673 674
    expect(WidgetsApp.showPerformanceOverlayOverride, true);
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{});
675
    expect(result, <String, String>{'enabled': 'true'});
676
    expect(WidgetsApp.showPerformanceOverlayOverride, true);
677 678
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{'enabled': 'false'});
    expect(result, <String, String>{'enabled': 'false'});
679 680
    expect(WidgetsApp.showPerformanceOverlayOverride, false);
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{});
681
    expect(result, <String, String>{'enabled': 'false'});
682 683 684 685
    expect(WidgetsApp.showPerformanceOverlayOverride, false);
    expect(binding.frameScheduled, isFalse);
  });

686 687 688 689 690
  test('Service extensions - debugWidgetInspector', () async {
    Map<String, dynamic> result;
    expect(binding.frameScheduled, isFalse);
    expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
    result = await binding.testExtension('debugWidgetInspector', <String, String>{});
691
    expect(result, <String, String>{'enabled': 'false'});
692
    expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
693 694
    result = await binding.testExtension('debugWidgetInspector', <String, String>{'enabled': 'true'});
    expect(result, <String, String>{'enabled': 'true'});
695 696
    expect(WidgetsApp.debugShowWidgetInspectorOverride, true);
    result = await binding.testExtension('debugWidgetInspector', <String, String>{});
697
    expect(result, <String, String>{'enabled': 'true'});
698
    expect(WidgetsApp.debugShowWidgetInspectorOverride, true);
699 700
    result = await binding.testExtension('debugWidgetInspector', <String, String>{'enabled': 'false'});
    expect(result, <String, String>{'enabled': 'false'});
701 702
    expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
    result = await binding.testExtension('debugWidgetInspector', <String, String>{});
703
    expect(result, <String, String>{'enabled': 'false'});
704 705 706 707
    expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
    expect(binding.frameScheduled, isFalse);
  });

708
  test('Service extensions - timeDilation', () async {
709 710
    final Iterable<Map<String, dynamic>> extensionChangedEvents = binding.getServiceExtensionStateChangedEvents('ext.flutter.timeDilation');
    Map<String, dynamic> extensionChangedEvent;
711
    Map<String, dynamic> result;
712 713 714 715

    expect(binding.frameScheduled, isFalse);
    expect(timeDilation, 1.0);
    result = await binding.testExtension('timeDilation', <String, String>{});
716
    expect(result, <String, String>{'timeDilation': 1.0.toString()});
717
    expect(timeDilation, 1.0);
718
    expect(extensionChangedEvents, isEmpty);
719
    result = await binding.testExtension('timeDilation', <String, String>{'timeDilation': '100.0'});
720
    expect(result, <String, String>{'timeDilation': 100.0.toString()});
721
    expect(timeDilation, 100.0);
722 723 724
    expect(extensionChangedEvents.length, 1);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.timeDilation');
725
    expect(extensionChangedEvent['value'], 100.0.toString());
726
    result = await binding.testExtension('timeDilation', <String, String>{});
727
    expect(result, <String, String>{'timeDilation': 100.0.toString()});
728
    expect(timeDilation, 100.0);
729
    expect(extensionChangedEvents.length, 1);
730
    result = await binding.testExtension('timeDilation', <String, String>{'timeDilation': '1.0'});
731
    expect(result, <String, String>{'timeDilation': 1.0.toString()});
732
    expect(timeDilation, 1.0);
733 734 735
    expect(extensionChangedEvents.length, 2);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.timeDilation');
736
    expect(extensionChangedEvent['value'], 1.0.toString());
737
    result = await binding.testExtension('timeDilation', <String, String>{});
738
    expect(result, <String, String>{'timeDilation': 1.0.toString()});
739
    expect(timeDilation, 1.0);
740
    expect(extensionChangedEvents.length, 2);
741 742 743
    expect(binding.frameScheduled, isFalse);
  });

744
  test('Service extensions - brightnessOverride', () async {
745
    Map<String, dynamic> result;
746 747 748 749 750
    result = await binding.testExtension('brightnessOverride', <String, String>{});
    final String brightnessValue = result['value'] as String;

    expect(brightnessValue, 'Brightness.light');
  });
751 752 753 754 755 756 757 758 759 760 761 762 763

  test('Service extensions - activeDevToolsServerAddress', () async {
    Map<String, dynamic> result;
    result = await binding.testExtension('activeDevToolsServerAddress', <String, String>{});
    String serverAddress = result['value'] as String;
    expect(serverAddress, '');
    result = await binding.testExtension('activeDevToolsServerAddress', <String, String>{'value': 'http://127.0.0.1:9101'});
    serverAddress = result['value'] as String;
    expect(serverAddress, 'http://127.0.0.1:9101');
    result = await binding.testExtension('activeDevToolsServerAddress', <String, String>{'value': 'http://127.0.0.1:9102'});
    serverAddress = result['value'] as String;
    expect(serverAddress, 'http://127.0.0.1:9102');
  });
764 765 766 767 768 769 770 771 772 773 774 775 776

  test('Service extensions - connectedVmServiceUri', () async {
    Map<String, dynamic> result;
    result = await binding.testExtension('connectedVmServiceUri', <String, String>{});
    String serverAddress = result['value'] as String;
    expect(serverAddress, '');
    result = await binding.testExtension('connectedVmServiceUri', <String, String>{'value': 'http://127.0.0.1:54669/kMUMseKAnog=/'});
    serverAddress = result['value'] as String;
    expect(serverAddress, 'http://127.0.0.1:54669/kMUMseKAnog=/');
    result = await binding.testExtension('connectedVmServiceUri', <String, String>{'value': 'http://127.0.0.1:54000/kMUMseKAnog=/'});
    serverAddress = result['value'] as String;
    expect(serverAddress, 'http://127.0.0.1:54000/kMUMseKAnog=/');
  });
777
}