service_extensions_test.dart 32.3 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 15
// 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: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';
16
import '../flutter_test_alternative.dart';
17 18

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

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

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

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

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

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

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

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

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

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

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

101
late TestServiceExtensionsBinding binding;
102

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

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

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

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

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

136
    await binding.doFrame();
137 138 139

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

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

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

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

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

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

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

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

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

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

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

    bool lastValue = false;
    Future<void> _updateAndCheck(bool newValue) async {
207
      Map<String, dynamic>? result;
208 209
      binding.testExtension(
        'debugCheckElevationsEnabled',
210
        <String, String>{'enabled': '$newValue'},
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
      ).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);
  });

229
  test('Service extensions - debugDumpApp', () async {
230
    Map<String, dynamic> result;
231 232 233 234 235 236 237 238

    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 {
239
    Map<String, dynamic> result;
240

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

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

    await binding.doFrame();
    result = await binding.testExtension('debugDumpLayerTree', <String, String>{});
    expect(result, <String, String>{});
    expect(console, <Matcher>[
      matches(
        r'^'
267 268
        r'TransformLayer#[0-9a-f]{5}\n'
        r'   owner: RenderView#[0-9a-f]{5}\n'
269
        r'   creator: RenderView\n'
270
        r'   engine layer: (TransformEngineLayer|PersistedTransform)#[0-9a-f]{5}\n'
271 272
        r'   offset: Offset\(0\.0, 0\.0\)\n'
        r'   transform:\n'
273 274
        r'     \[0] 3\.0,0\.0,0\.0,0\.0\n'
        r'     \[1] 0\.0,3\.0,0\.0,0\.0\n'
275 276 277 278 279 280 281 282
        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();
  });

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

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

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

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

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

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

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

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

400 401
  test('Service extensions - invertOversizedImages', () async {
    Map<String, dynamic> result;
402 403
    Future<Map<String, dynamic>> pendingResult;
    bool completed;
404 405 406 407 408 409

    expect(binding.frameScheduled, isFalse);
    expect(debugInvertOversizedImages, false);
    result = await binding.testExtension('invertOversizedImages', <String, String>{});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugInvertOversizedImages, false);
410 411 412 413 414 415 416 417 418 419 420 421 422
    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;
423 424
    expect(result, <String, String>{'enabled': 'true'});
    expect(debugInvertOversizedImages, true);
425

426 427 428
    result = await binding.testExtension('invertOversizedImages', <String, String>{});
    expect(result, <String, String>{'enabled': 'true'});
    expect(debugInvertOversizedImages, true);
429 430 431 432 433 434 435 436
    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;
437 438
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugInvertOversizedImages, false);
439

440 441 442 443 444 445
    result = await binding.testExtension('invertOversizedImages', <String, String>{});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugInvertOversizedImages, false);
    expect(binding.frameScheduled, isFalse);
  });

446 447 448 449 450 451 452
  test('Service extensions - profileWidgetBuilds', () async {
    Map<String, dynamic> result;

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

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

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

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

464 465
    result = await binding.testExtension('profileWidgetBuilds', <String, String>{'enabled': 'false'});
    expect(result, <String, String>{'enabled': 'false'});
466 467 468
    expect(debugProfileBuildsEnabled, false);

    result = await binding.testExtension('profileWidgetBuilds', <String, String>{});
469
    expect(result, <String, String>{'enabled': 'false'});
470 471 472 473 474
    expect(debugProfileBuildsEnabled, false);

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

475
  test('Service extensions - evict', () async {
476
    Map<String, dynamic> result;
477 478 479
    bool completed;

    completed = false;
480 481
    ServicesBinding.instance!.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', (ByteData? message) async {
      expect(utf8.decode(message!.buffer.asUint8List()), 'test');
482
      completed = true;
483
      return ByteData(5); // 0x0000000000
484 485
    });
    bool data;
486 487 488 489
    data = await rootBundle.loadStructuredData<bool>('test', (String value) async {
      expect(value, '\x00\x00\x00\x00\x00');
      return true;
    });
490 491 492
    expect(data, isTrue);
    expect(completed, isTrue);
    completed = false;
493
    data = await rootBundle.loadStructuredData('test', (String value) async {
494
      throw Error();
495
    });
496 497
    expect(data, isTrue);
    expect(completed, isFalse);
498 499
    result = await binding.testExtension('evict', <String, String>{'value': 'test'});
    expect(result, <String, String>{'value': ''});
500
    expect(completed, isFalse);
501 502 503 504
    data = await rootBundle.loadStructuredData<bool>('test', (String value) async {
      expect(value, '\x00\x00\x00\x00\x00');
      return false;
    });
505 506
    expect(data, isFalse);
    expect(completed, isTrue);
507
    ServicesBinding.instance!.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', null);
508 509 510 511
  });

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

516
  test('Service extensions - platformOverride', () async {
517 518
    final Iterable<Map<String, dynamic>> extensionChangedEvents = binding.getServiceExtensionStateChangedEvents('ext.flutter.platformOverride');
    Map<String, dynamic> extensionChangedEvent;
519
    Map<String, dynamic> result;
520 521 522 523 524 525

    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);
526
    expect(extensionChangedEvents, isEmpty);
527 528 529 530
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'iOS'}));
    expect(result, <String, String>{'value': 'iOS'});
    expect(binding.reassembled, 1);
    expect(defaultTargetPlatform, TargetPlatform.iOS);
531 532 533 534
    expect(extensionChangedEvents.length, 1);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'iOS');
Dan Field's avatar
Dan Field committed
535 536 537 538 539 540 541 542
    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');
543 544
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'android'}));
    expect(result, <String, String>{'value': 'android'});
Dan Field's avatar
Dan Field committed
545
    expect(binding.reassembled, 3);
546
    expect(defaultTargetPlatform, TargetPlatform.android);
Dan Field's avatar
Dan Field committed
547
    expect(extensionChangedEvents.length, 3);
548 549 550
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'android');
551 552
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'fuchsia'}));
    expect(result, <String, String>{'value': 'fuchsia'});
Dan Field's avatar
Dan Field committed
553
    expect(binding.reassembled, 4);
554
    expect(defaultTargetPlatform, TargetPlatform.fuchsia);
Dan Field's avatar
Dan Field committed
555
    expect(extensionChangedEvents.length, 4);
556 557 558
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'fuchsia');
559 560
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'default'}));
    expect(result, <String, String>{'value': 'android'});
Dan Field's avatar
Dan Field committed
561
    expect(binding.reassembled, 5);
562
    expect(defaultTargetPlatform, TargetPlatform.android);
Dan Field's avatar
Dan Field committed
563
    expect(extensionChangedEvents.length, 5);
564 565 566
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'android');
567 568
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'iOS'}));
    expect(result, <String, String>{'value': 'iOS'});
Dan Field's avatar
Dan Field committed
569
    expect(binding.reassembled, 6);
570
    expect(defaultTargetPlatform, TargetPlatform.iOS);
Dan Field's avatar
Dan Field committed
571
    expect(extensionChangedEvents.length, 6);
572 573 574
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'iOS');
575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
    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');
591 592
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'bogus'}));
    expect(result, <String, String>{'value': 'android'});
593
    expect(binding.reassembled, 9);
594
    expect(defaultTargetPlatform, TargetPlatform.android);
595
    expect(extensionChangedEvents.length, 9);
596 597 598
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'android');
599 600 601
    binding.reassembled = 0;
  });

602
  test('Service extensions - repaintRainbow', () async {
603 604
    Map<String, dynamic> result;
    Future<Map<String, dynamic>> pendingResult;
605 606 607 608 609
    bool completed;

    expect(binding.frameScheduled, isFalse);
    expect(debugRepaintRainbowEnabled, false);
    result = await binding.testExtension('repaintRainbow', <String, String>{});
610
    expect(result, <String, String>{'enabled': 'false'});
611 612
    expect(debugRepaintRainbowEnabled, false);
    expect(binding.frameScheduled, isFalse);
613
    pendingResult = binding.testExtension('repaintRainbow', <String, String>{'enabled': 'true'});
614 615 616 617 618 619
    completed = false;
    pendingResult.whenComplete(() { completed = true; });
    await binding.flushMicrotasks();
    expect(completed, true);
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
620
    expect(result, <String, String>{'enabled': 'true'});
621 622
    expect(debugRepaintRainbowEnabled, true);
    result = await binding.testExtension('repaintRainbow', <String, String>{});
623
    expect(result, <String, String>{'enabled': 'true'});
624 625
    expect(debugRepaintRainbowEnabled, true);
    expect(binding.frameScheduled, isFalse);
626
    pendingResult = binding.testExtension('repaintRainbow', <String, String>{'enabled': 'false'});
627 628 629 630 631
    completed = false;
    pendingResult.whenComplete(() { completed = true; });
    await binding.flushMicrotasks();
    expect(completed, false);
    expect(binding.frameScheduled, isTrue);
632
    await binding.doFrame();
633 634 635 636
    await binding.flushMicrotasks();
    expect(completed, true);
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
637
    expect(result, <String, String>{'enabled': 'false'});
638 639
    expect(debugRepaintRainbowEnabled, false);
    result = await binding.testExtension('repaintRainbow', <String, String>{});
640
    expect(result, <String, String>{'enabled': 'false'});
641 642 643 644 645
    expect(debugRepaintRainbowEnabled, false);
    expect(binding.frameScheduled, isFalse);
  });

  test('Service extensions - reassemble', () async {
646 647
    Map<String, dynamic> result;
    Future<Map<String, dynamic>> pendingResult;
648 649 650 651 652 653 654 655 656
    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);
657
    await binding.flushMicrotasks();
658
    await binding.doFrame();
659 660 661 662 663 664 665 666 667
    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 {
668
    Map<String, dynamic> result;
669

670 671 672 673 674 675
    // The performance overlay service extension is disabled on the web.
    if (kIsWeb) {
      expect(binding.extensions.containsKey('showPerformanceOverlay'), isFalse);
      return;
    }

676 677 678
    expect(binding.frameScheduled, isFalse);
    expect(WidgetsApp.showPerformanceOverlayOverride, false);
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{});
679
    expect(result, <String, String>{'enabled': 'false'});
680
    expect(WidgetsApp.showPerformanceOverlayOverride, false);
681 682
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{'enabled': 'true'});
    expect(result, <String, String>{'enabled': 'true'});
683 684
    expect(WidgetsApp.showPerformanceOverlayOverride, true);
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{});
685
    expect(result, <String, String>{'enabled': 'true'});
686
    expect(WidgetsApp.showPerformanceOverlayOverride, true);
687 688
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{'enabled': 'false'});
    expect(result, <String, String>{'enabled': 'false'});
689 690
    expect(WidgetsApp.showPerformanceOverlayOverride, false);
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{});
691
    expect(result, <String, String>{'enabled': 'false'});
692 693 694 695
    expect(WidgetsApp.showPerformanceOverlayOverride, false);
    expect(binding.frameScheduled, isFalse);
  });

696 697 698 699 700
  test('Service extensions - debugWidgetInspector', () async {
    Map<String, dynamic> result;
    expect(binding.frameScheduled, isFalse);
    expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
    result = await binding.testExtension('debugWidgetInspector', <String, String>{});
701
    expect(result, <String, String>{'enabled': 'false'});
702
    expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
703 704
    result = await binding.testExtension('debugWidgetInspector', <String, String>{'enabled': 'true'});
    expect(result, <String, String>{'enabled': 'true'});
705 706
    expect(WidgetsApp.debugShowWidgetInspectorOverride, true);
    result = await binding.testExtension('debugWidgetInspector', <String, String>{});
707
    expect(result, <String, String>{'enabled': 'true'});
708
    expect(WidgetsApp.debugShowWidgetInspectorOverride, true);
709 710
    result = await binding.testExtension('debugWidgetInspector', <String, String>{'enabled': 'false'});
    expect(result, <String, String>{'enabled': 'false'});
711 712
    expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
    result = await binding.testExtension('debugWidgetInspector', <String, String>{});
713
    expect(result, <String, String>{'enabled': 'false'});
714 715 716 717
    expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
    expect(binding.frameScheduled, isFalse);
  });

718
  test('Service extensions - timeDilation', () async {
719 720
    final Iterable<Map<String, dynamic>> extensionChangedEvents = binding.getServiceExtensionStateChangedEvents('ext.flutter.timeDilation');
    Map<String, dynamic> extensionChangedEvent;
721
    Map<String, dynamic> result;
722 723 724 725

    expect(binding.frameScheduled, isFalse);
    expect(timeDilation, 1.0);
    result = await binding.testExtension('timeDilation', <String, String>{});
726
    expect(result, <String, String>{'timeDilation': 1.0.toString()});
727
    expect(timeDilation, 1.0);
728
    expect(extensionChangedEvents, isEmpty);
729
    result = await binding.testExtension('timeDilation', <String, String>{'timeDilation': '100.0'});
730
    expect(result, <String, String>{'timeDilation': 100.0.toString()});
731
    expect(timeDilation, 100.0);
732 733 734
    expect(extensionChangedEvents.length, 1);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.timeDilation');
735
    expect(extensionChangedEvent['value'], 100.0.toString());
736
    result = await binding.testExtension('timeDilation', <String, String>{});
737
    expect(result, <String, String>{'timeDilation': 100.0.toString()});
738
    expect(timeDilation, 100.0);
739
    expect(extensionChangedEvents.length, 1);
740
    result = await binding.testExtension('timeDilation', <String, String>{'timeDilation': '1.0'});
741
    expect(result, <String, String>{'timeDilation': 1.0.toString()});
742
    expect(timeDilation, 1.0);
743 744 745
    expect(extensionChangedEvents.length, 2);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.timeDilation');
746
    expect(extensionChangedEvent['value'], 1.0.toString());
747
    result = await binding.testExtension('timeDilation', <String, String>{});
748
    expect(result, <String, String>{'timeDilation': 1.0.toString()});
749
    expect(timeDilation, 1.0);
750
    expect(extensionChangedEvents.length, 2);
751 752 753
    expect(binding.frameScheduled, isFalse);
  });

754
  test('Service extensions - brightnessOverride', () async {
755
    Map<String, dynamic> result;
756 757 758 759 760
    result = await binding.testExtension('brightnessOverride', <String, String>{});
    final String brightnessValue = result['value'] as String;

    expect(brightnessValue, 'Brightness.light');
  });
761
}