service_extensions_test.dart 28.6 KB
Newer Older
1 2 3 4
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
@TestOn('!chrome')

7 8 9 10 11 12 13 14 15 16 17
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
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';
18
import '../flutter_test_alternative.dart';
19 20

class TestServiceExtensionsBinding extends BindingBase
21
  with ServicesBinding,
22
       GestureBinding,
23 24
       SchedulerBinding,
       PaintingBinding,
25
       SemanticsBinding,
26 27 28 29 30
       RendererBinding,
       WidgetsBinding {

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

31 32
  final Map<String, List<Map<String, dynamic>>> eventsDispatched = <String, List<Map<String, dynamic>>>{};

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

42 43 44 45 46 47 48 49 50 51 52 53 54 55
  @override
  void postEvent(String eventKind, Map<dynamic, dynamic> eventData) {
    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);
  }

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

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

  bool frameScheduled = false;
  @override
  void scheduleFrame() {
    frameScheduled = true;
  }
75
  Future<void> doFrame() async {
76 77
    frameScheduled = false;
    if (ui.window.onBeginFrame != null)
78
      ui.window.onBeginFrame(Duration.zero);
79 80 81
    await flushMicrotasks();
    if (ui.window.onDrawFrame != null)
      ui.window.onDrawFrame();
82 83
    if (ui.window.onReportTimings != null)
      ui.window.onReportTimings(<ui.FrameTiming>[]);
84 85
  }

86 87 88 89 90 91 92 93 94 95 96
  @override
  void scheduleForcedFrame() {
    expect(true, isFalse);
  }

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

97 98
  Future<void> flushMicrotasks() {
    final Completer<void> completer = Completer<void>();
99
    Timer.run(completer.complete);
100 101 102 103
    return completer.future;
  }
}

104 105
TestServiceExtensionsBinding binding;

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

121
void main() {
122
  final List<String> console = <String>[];
123

124
  setUpAll(() async {
125
    binding = TestServiceExtensionsBinding();
126
    expect(binding.frameScheduled, isTrue);
127 128 129 130 131 132

    // 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>{});
133
    expect(firstFrameResult, <String, String>{'enabled': 'false'});
134

135 136 137 138
    expect(binding.firstFrameRasterized, isFalse);
    firstFrameResult = await binding.testExtension('didSendFirstFrameRasterizedEvent', <String, String>{});
    expect(firstFrameResult, <String, String>{'enabled': 'false'});

139
    await binding.doFrame(); // initial frame scheduled by creating the binding
140 141 142

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

145 146 147 148
    expect(binding.firstFrameRasterized, isTrue);
    firstFrameResult = await binding.testExtension('didSendFirstFrameRasterizedEvent', <String, String>{});
    expect(firstFrameResult, <String, String>{'enabled': 'true'});

149 150 151 152 153 154 155 156
    expect(binding.frameScheduled, isFalse);

    expect(debugPrint, equals(debugPrintThrottled));
    debugPrint = (String message, { int wrapWidth }) {
      console.add(message);
    };
  });

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

    // If you add a service extension... TEST IT! :-)
    // ...then increment this number.
169
    expect(binding.extensions.length, 27 + widgetInspectorExtensionCount);
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 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
  test('Service extensions - debugCheckElevationsEnabled', () async {
    expect(binding.frameScheduled, isFalse);
    expect(debugCheckElevationsEnabled, false);

    bool lastValue = false;
    Future<void> _updateAndCheck(bool newValue) async {
      Map<String, dynamic> result;
      binding.testExtension(
        'debugCheckElevationsEnabled',
        <String, String>{'enabled': '$newValue'}
      ).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
    Map<String, dynamic> result;
230 231 232 233 234 235 236 237

    result = await binding.testExtension('debugDumpApp', <String, String>{});
    expect(result, <String, String>{});
    expect(console, <String>['TestServiceExtensionsBinding - CHECKED MODE', '<no tree currently mounted>']);
    console.clear();
  });

  test('Service extensions - debugDumpRenderTree', () async {
238
    Map<String, dynamic> result;
239

240
    await binding.doFrame();
241 242
    result = await binding.testExtension('debugDumpRenderTree', <String, String>{});
    expect(result, <String, String>{});
243 244
    expect(console, <Matcher>[
      matches(
245
        r'^'
246
        r'RenderView#[0-9a-f]{5}\n'
247
        r'   debug mode enabled - [a-zA-Z]+\n'
248 249 250
        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'
251
        r'$'
252
      ),
253 254 255 256
    ]);
    console.clear();
  });

257
  test('Service extensions - debugDumpLayerTree', () async {
258
    Map<String, dynamic> result;
259 260 261 262 263 264 265

    await binding.doFrame();
    result = await binding.testExtension('debugDumpLayerTree', <String, String>{});
    expect(result, <String, String>{});
    expect(console, <Matcher>[
      matches(
        r'^'
266 267
        r'TransformLayer#[0-9a-f]{5}\n'
        r'   owner: RenderView#[0-9a-f]{5}\n'
268 269 270
        r'   creator: RenderView\n'
        r'   offset: Offset\(0\.0, 0\.0\)\n'
        r'   transform:\n'
271 272
        r'     \[0] 3\.0,0\.0,0\.0,0\.0\n'
        r'     \[1] 0\.0,3\.0,0\.0,0\.0\n'
273 274 275 276 277 278 279 280
        r'     \[2] 0\.0,0\.0,1\.0,0\.0\n'
        r'     \[3] 0\.0,0\.0,0\.0,1\.0\n'
        r'$'
      ),
    ]);
    console.clear();
  });

281
  test('Service extensions - debugDumpSemanticsTreeInTraversalOrder', () async {
282
    Map<String, dynamic> result;
283 284

    await binding.doFrame();
285
    result = await binding.testExtension('debugDumpSemanticsTreeInTraversalOrder', <String, String>{});
286 287 288 289 290 291
    expect(result, <String, String>{});
    expect(console, <String>['Semantics not collected.']);
    console.clear();
  });

  test('Service extensions - debugDumpSemanticsTreeInInverseHitTestOrder', () async {
292
    Map<String, dynamic> result;
293 294 295

    await binding.doFrame();
    result = await binding.testExtension('debugDumpSemanticsTreeInInverseHitTestOrder', <String, String>{});
296 297 298 299 300
    expect(result, <String, String>{});
    expect(console, <String>['Semantics not collected.']);
    console.clear();
  });

301
  test('Service extensions - debugPaint', () async {
302 303
    final Iterable<Map<String, dynamic>> extensionChangedEvents = binding.getServiceExtensionStateChangedEvents('ext.flutter.debugPaint');
    Map<String, dynamic> extensionChangedEvent;
304 305
    Map<String, dynamic> result;
    Future<Map<String, dynamic>> pendingResult;
306 307 308 309 310
    bool completed;

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

356
  test('Service extensions - debugPaintBaselinesEnabled', () async {
357 358
    Map<String, dynamic> result;
    Future<Map<String, dynamic>> pendingResult;
359 360 361 362 363
    bool completed;

    expect(binding.frameScheduled, isFalse);
    expect(debugPaintBaselinesEnabled, false);
    result = await binding.testExtension('debugPaintBaselinesEnabled', <String, String>{});
364
    expect(result, <String, String>{'enabled': 'false'});
365 366
    expect(debugPaintBaselinesEnabled, false);
    expect(binding.frameScheduled, isFalse);
367
    pendingResult = binding.testExtension('debugPaintBaselinesEnabled', <String, String>{'enabled': 'true'});
368 369 370 371 372 373 374 375 376 377
    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;
378
    expect(result, <String, String>{'enabled': 'true'});
379 380
    expect(debugPaintBaselinesEnabled, true);
    result = await binding.testExtension('debugPaintBaselinesEnabled', <String, String>{});
381
    expect(result, <String, String>{'enabled': 'true'});
382 383
    expect(debugPaintBaselinesEnabled, true);
    expect(binding.frameScheduled, isFalse);
384
    pendingResult = binding.testExtension('debugPaintBaselinesEnabled', <String, String>{'enabled': 'false'});
385 386 387 388 389
    await binding.flushMicrotasks();
    expect(binding.frameScheduled, isTrue);
    await binding.doFrame();
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
390
    expect(result, <String, String>{'enabled': 'false'});
391 392
    expect(debugPaintBaselinesEnabled, false);
    result = await binding.testExtension('debugPaintBaselinesEnabled', <String, String>{});
393
    expect(result, <String, String>{'enabled': 'false'});
394 395 396 397
    expect(debugPaintBaselinesEnabled, false);
    expect(binding.frameScheduled, isFalse);
  });

398 399 400 401 402 403 404
  test('Service extensions - profileWidgetBuilds', () async {
    Map<String, dynamic> result;

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

    result = await binding.testExtension('profileWidgetBuilds', <String, String>{});
405
    expect(result, <String, String>{'enabled': 'false'});
406 407
    expect(debugProfileBuildsEnabled, false);

408 409
    result = await binding.testExtension('profileWidgetBuilds', <String, String>{'enabled': 'true'});
    expect(result, <String, String>{'enabled': 'true'});
410 411 412
    expect(debugProfileBuildsEnabled, true);

    result = await binding.testExtension('profileWidgetBuilds', <String, String>{});
413
    expect(result, <String, String>{'enabled': 'true'});
414 415
    expect(debugProfileBuildsEnabled, true);

416 417
    result = await binding.testExtension('profileWidgetBuilds', <String, String>{'enabled': 'false'});
    expect(result, <String, String>{'enabled': 'false'});
418 419 420
    expect(debugProfileBuildsEnabled, false);

    result = await binding.testExtension('profileWidgetBuilds', <String, String>{});
421
    expect(result, <String, String>{'enabled': 'false'});
422 423 424 425 426
    expect(debugProfileBuildsEnabled, false);

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

427
  test('Service extensions - evict', () async {
428
    Map<String, dynamic> result;
429 430 431
    bool completed;

    completed = false;
432
    defaultBinaryMessenger.setMockMessageHandler('flutter/assets', (ByteData message) async {
433
      expect(utf8.decode(message.buffer.asUint8List()), 'test');
434
      completed = true;
435
      return ByteData(5); // 0x0000000000
436 437 438 439 440 441 442 443 444
    });
    bool data;
    data = await rootBundle.loadStructuredData<bool>('test', (String value) async { expect(value, '\x00\x00\x00\x00\x00'); return true; });
    expect(data, isTrue);
    expect(completed, isTrue);
    completed = false;
    data = await rootBundle.loadStructuredData('test', (String value) async { expect(true, isFalse); return null; });
    expect(data, isTrue);
    expect(completed, isFalse);
445 446
    result = await binding.testExtension('evict', <String, String>{'value': 'test'});
    expect(result, <String, String>{'value': ''});
447 448 449 450
    expect(completed, isFalse);
    data = await rootBundle.loadStructuredData<bool>('test', (String value) async { expect(value, '\x00\x00\x00\x00\x00'); return false; });
    expect(data, isFalse);
    expect(completed, isTrue);
451
    defaultBinaryMessenger.setMockMessageHandler('flutter/assets', null);
452 453 454 455
  });

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

460
  test('Service extensions - platformOverride', () async {
461 462
    final Iterable<Map<String, dynamic>> extensionChangedEvents = binding.getServiceExtensionStateChangedEvents('ext.flutter.platformOverride');
    Map<String, dynamic> extensionChangedEvent;
463
    Map<String, dynamic> result;
464 465 466 467 468 469

    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);
470
    expect(extensionChangedEvents, isEmpty);
471 472 473 474
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'iOS'}));
    expect(result, <String, String>{'value': 'iOS'});
    expect(binding.reassembled, 1);
    expect(defaultTargetPlatform, TargetPlatform.iOS);
475 476 477 478
    expect(extensionChangedEvents.length, 1);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'iOS');
479 480 481 482
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'android'}));
    expect(result, <String, String>{'value': 'android'});
    expect(binding.reassembled, 2);
    expect(defaultTargetPlatform, TargetPlatform.android);
483 484 485 486
    expect(extensionChangedEvents.length, 2);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'android');
487 488 489 490
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'fuchsia'}));
    expect(result, <String, String>{'value': 'fuchsia'});
    expect(binding.reassembled, 3);
    expect(defaultTargetPlatform, TargetPlatform.fuchsia);
491 492 493 494
    expect(extensionChangedEvents.length, 3);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'fuchsia');
495 496 497 498
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'default'}));
    expect(result, <String, String>{'value': 'android'});
    expect(binding.reassembled, 4);
    expect(defaultTargetPlatform, TargetPlatform.android);
499 500 501 502
    expect(extensionChangedEvents.length, 4);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'android');
503 504 505 506
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'iOS'}));
    expect(result, <String, String>{'value': 'iOS'});
    expect(binding.reassembled, 5);
    expect(defaultTargetPlatform, TargetPlatform.iOS);
507 508 509 510
    expect(extensionChangedEvents.length, 5);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'iOS');
511 512 513 514
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'bogus'}));
    expect(result, <String, String>{'value': 'android'});
    expect(binding.reassembled, 6);
    expect(defaultTargetPlatform, TargetPlatform.android);
515 516 517 518
    expect(extensionChangedEvents.length, 6);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'android');
519 520 521
    binding.reassembled = 0;
  });

522
  test('Service extensions - repaintRainbow', () async {
523 524
    Map<String, dynamic> result;
    Future<Map<String, dynamic>> pendingResult;
525 526 527 528 529
    bool completed;

    expect(binding.frameScheduled, isFalse);
    expect(debugRepaintRainbowEnabled, false);
    result = await binding.testExtension('repaintRainbow', <String, String>{});
530
    expect(result, <String, String>{'enabled': 'false'});
531 532
    expect(debugRepaintRainbowEnabled, false);
    expect(binding.frameScheduled, isFalse);
533
    pendingResult = binding.testExtension('repaintRainbow', <String, String>{'enabled': 'true'});
534 535 536 537 538 539
    completed = false;
    pendingResult.whenComplete(() { completed = true; });
    await binding.flushMicrotasks();
    expect(completed, true);
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
540
    expect(result, <String, String>{'enabled': 'true'});
541 542
    expect(debugRepaintRainbowEnabled, true);
    result = await binding.testExtension('repaintRainbow', <String, String>{});
543
    expect(result, <String, String>{'enabled': 'true'});
544 545
    expect(debugRepaintRainbowEnabled, true);
    expect(binding.frameScheduled, isFalse);
546
    pendingResult = binding.testExtension('repaintRainbow', <String, String>{'enabled': 'false'});
547 548 549 550 551
    completed = false;
    pendingResult.whenComplete(() { completed = true; });
    await binding.flushMicrotasks();
    expect(completed, false);
    expect(binding.frameScheduled, isTrue);
552
    await binding.doFrame();
553 554 555 556
    await binding.flushMicrotasks();
    expect(completed, true);
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
557
    expect(result, <String, String>{'enabled': 'false'});
558 559
    expect(debugRepaintRainbowEnabled, false);
    result = await binding.testExtension('repaintRainbow', <String, String>{});
560
    expect(result, <String, String>{'enabled': 'false'});
561 562 563 564 565
    expect(debugRepaintRainbowEnabled, false);
    expect(binding.frameScheduled, isFalse);
  });

  test('Service extensions - reassemble', () async {
566 567
    Map<String, dynamic> result;
    Future<Map<String, dynamic>> pendingResult;
568 569 570 571 572 573 574 575 576
    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);
577
    await binding.flushMicrotasks();
578
    await binding.doFrame();
579 580 581 582 583 584 585 586 587
    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 {
588
    Map<String, dynamic> result;
589 590 591 592

    expect(binding.frameScheduled, isFalse);
    expect(WidgetsApp.showPerformanceOverlayOverride, false);
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{});
593
    expect(result, <String, String>{'enabled': 'false'});
594
    expect(WidgetsApp.showPerformanceOverlayOverride, false);
595 596
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{'enabled': 'true'});
    expect(result, <String, String>{'enabled': 'true'});
597 598
    expect(WidgetsApp.showPerformanceOverlayOverride, true);
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{});
599
    expect(result, <String, String>{'enabled': 'true'});
600
    expect(WidgetsApp.showPerformanceOverlayOverride, true);
601 602
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{'enabled': 'false'});
    expect(result, <String, String>{'enabled': 'false'});
603 604
    expect(WidgetsApp.showPerformanceOverlayOverride, false);
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{});
605
    expect(result, <String, String>{'enabled': 'false'});
606 607 608 609
    expect(WidgetsApp.showPerformanceOverlayOverride, false);
    expect(binding.frameScheduled, isFalse);
  });

610 611 612 613 614
  test('Service extensions - debugWidgetInspector', () async {
    Map<String, dynamic> result;
    expect(binding.frameScheduled, isFalse);
    expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
    result = await binding.testExtension('debugWidgetInspector', <String, String>{});
615
    expect(result, <String, String>{'enabled': 'false'});
616
    expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
617 618
    result = await binding.testExtension('debugWidgetInspector', <String, String>{'enabled': 'true'});
    expect(result, <String, String>{'enabled': 'true'});
619 620
    expect(WidgetsApp.debugShowWidgetInspectorOverride, true);
    result = await binding.testExtension('debugWidgetInspector', <String, String>{});
621
    expect(result, <String, String>{'enabled': 'true'});
622
    expect(WidgetsApp.debugShowWidgetInspectorOverride, true);
623 624
    result = await binding.testExtension('debugWidgetInspector', <String, String>{'enabled': 'false'});
    expect(result, <String, String>{'enabled': 'false'});
625 626
    expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
    result = await binding.testExtension('debugWidgetInspector', <String, String>{});
627
    expect(result, <String, String>{'enabled': 'false'});
628 629 630 631
    expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
    expect(binding.frameScheduled, isFalse);
  });

632
  test('Service extensions - timeDilation', () async {
633 634
    final Iterable<Map<String, dynamic>> extensionChangedEvents = binding.getServiceExtensionStateChangedEvents('ext.flutter.timeDilation');
    Map<String, dynamic> extensionChangedEvent;
635
    Map<String, dynamic> result;
636 637 638 639

    expect(binding.frameScheduled, isFalse);
    expect(timeDilation, 1.0);
    result = await binding.testExtension('timeDilation', <String, String>{});
640
    expect(result, <String, String>{'timeDilation': '1.0'});
641
    expect(timeDilation, 1.0);
642
    expect(extensionChangedEvents, isEmpty);
643 644
    result = await binding.testExtension('timeDilation', <String, String>{'timeDilation': '100.0'});
    expect(result, <String, String>{'timeDilation': '100.0'});
645
    expect(timeDilation, 100.0);
646 647 648 649
    expect(extensionChangedEvents.length, 1);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.timeDilation');
    expect(extensionChangedEvent['value'], '100.0');
650
    result = await binding.testExtension('timeDilation', <String, String>{});
651
    expect(result, <String, String>{'timeDilation': '100.0'});
652
    expect(timeDilation, 100.0);
653
    expect(extensionChangedEvents.length, 1);
654 655
    result = await binding.testExtension('timeDilation', <String, String>{'timeDilation': '1.0'});
    expect(result, <String, String>{'timeDilation': '1.0'});
656
    expect(timeDilation, 1.0);
657 658 659 660
    expect(extensionChangedEvents.length, 2);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.timeDilation');
    expect(extensionChangedEvent['value'], '1.0');
661
    result = await binding.testExtension('timeDilation', <String, String>{});
662
    expect(result, <String, String>{'timeDilation': '1.0'});
663
    expect(timeDilation, 1.0);
664
    expect(extensionChangedEvents.length, 2);
665 666 667
    expect(binding.frameScheduled, isFalse);
  });

668
  test('Service extensions - saveCompilationTrace', () async {
669
    Map<String, dynamic> result;
670
    result = await binding.testExtension('saveCompilationTrace', <String, String>{});
671 672 673 674
    final String trace = String.fromCharCodes(result['value']);
    expect(trace, contains('dart:core,Object,Object.\n'));
    expect(trace, contains('package:test_api/test_api.dart,::,test\n'));
    expect(trace, contains('service_extensions_test.dart,::,main\n'));
675
  }, skip: isBrowser);
676
}