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

5 6 7 8 9 10
// TODO(gspencergoog): Remove this tag once this test's state leaks/test
// dependencies have been fixed.
// https://github.com/flutter/flutter/issues/85160
// Fails with "flutter test --test-randomize-ordering-seed=123"
@Tags(<String>['no-shuffle'])

11 12 13 14 15 16 17 18 19 20
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';
21
import 'package:flutter_test/flutter_test.dart';
22 23

class TestServiceExtensionsBinding extends BindingBase
24 25
  with SchedulerBinding,
       ServicesBinding,
26
       GestureBinding,
27
       PaintingBinding,
28
       SemanticsBinding,
29
       RendererBinding,
30 31
       WidgetsBinding,
       TestDefaultBinaryMessengerBinding {
32 33 34

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

35 36
  final Map<String, List<Map<String, dynamic>>> eventsDispatched = <String, List<Map<String, dynamic>>>{};

37
  @override
38
  @protected
39
  void registerServiceExtension({
40 41
    required String name,
    required ServiceExtensionCallback callback,
42 43 44 45 46
  }) {
    expect(extensions.containsKey(name), isFalse);
    extensions[name] = callback;
  }

47
  @override
48
  void postEvent(String eventKind, Map<String, dynamic> eventData) {
49 50 51 52 53 54 55 56 57 58 59 60
    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);
  }

61
  Future<Map<String, dynamic>> testExtension(String name, Map<String, String> arguments) {
62
    expect(extensions.containsKey(name), isTrue);
63
    return extensions[name]!(arguments);
64 65 66
  }

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

  bool frameScheduled = false;
  @override
  void scheduleFrame() {
78
    ensureFrameCallbacksRegistered();
79 80
    frameScheduled = true;
  }
81
  Future<void> doFrame() async {
82
    frameScheduled = false;
83
    binding.platformDispatcher.onBeginFrame?.call(Duration.zero);
84
    await flushMicrotasks();
85 86
    binding.platformDispatcher.onDrawFrame?.call();
    binding.platformDispatcher.onReportTimings?.call(<ui.FrameTiming>[]);
87 88
  }

89 90 91 92 93 94 95 96 97 98 99
  @override
  void scheduleForcedFrame() {
    expect(true, isFalse);
  }

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

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

107
late TestServiceExtensionsBinding binding;
108

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

124
void main() {
125
  final List<String?> console = <String?>[];
126

127
  setUpAll(() async {
128
    binding = TestServiceExtensionsBinding()..scheduleFrame();
129
    expect(binding.frameScheduled, isTrue);
130 131 132 133 134 135

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

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

142
    await binding.doFrame();
143 144 145

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

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

152 153 154
    expect(binding.frameScheduled, isFalse);

    expect(debugPrint, equals(debugPrintThrottled));
155
    debugPrint = (String? message, { int? wrapWidth }) {
156 157 158 159
      console.add(message);
    };
  });

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

170 171
    // The following service extensions are disabled in web:
    // 1. exit
172 173
    // 2. showPerformanceOverlay
    const int disabledExtensions = kIsWeb ? 2 : 0;
174 175 176 177 178

    // The expected number of registered service extensions in the Flutter
    // framework, excluding any that are for the widget inspector
    // (see widget_inspector_test.dart for tests of the ext.flutter.inspector
    // service extensions).
179
    const int serviceExtensionCount = 38;
180 181

    expect(binding.extensions.length, serviceExtensionCount + widgetInspectorExtensionCount - disabledExtensions);
182 183 184 185 186

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

187 188 189
  // The following list is alphabetical, one test per extension.

  test('Service extensions - debugAllowBanner', () async {
190
    Map<String, dynamic> result;
191 192 193 194

    expect(binding.frameScheduled, isFalse);
    expect(WidgetsApp.debugAllowBannerOverride, true);
    result = await binding.testExtension('debugAllowBanner', <String, String>{});
195
    expect(result, <String, String>{'enabled': 'true'});
196
    expect(WidgetsApp.debugAllowBannerOverride, true);
197 198
    result = await binding.testExtension('debugAllowBanner', <String, String>{'enabled': 'false'});
    expect(result, <String, String>{'enabled': 'false'});
199 200
    expect(WidgetsApp.debugAllowBannerOverride, false);
    result = await binding.testExtension('debugAllowBanner', <String, String>{});
201
    expect(result, <String, String>{'enabled': 'false'});
202
    expect(WidgetsApp.debugAllowBannerOverride, false);
203 204
    result = await binding.testExtension('debugAllowBanner', <String, String>{'enabled': 'true'});
    expect(result, <String, String>{'enabled': 'true'});
205 206
    expect(WidgetsApp.debugAllowBannerOverride, true);
    result = await binding.testExtension('debugAllowBanner', <String, String>{});
207
    expect(result, <String, String>{'enabled': 'true'});
208 209 210 211 212
    expect(WidgetsApp.debugAllowBannerOverride, true);
    expect(binding.frameScheduled, isFalse);
  });

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

215 216 217
    expect(result, <String, dynamic>{
      'data': matches('TestServiceExtensionsBinding - DEBUG MODE\n<no tree currently mounted>'),
    });
218 219 220
  });

  test('Service extensions - debugDumpRenderTree', () async {
221
    await binding.doFrame();
222 223 224 225
    final Map<String, dynamic> result = await binding.testExtension('debugDumpRenderTree', <String, String>{});

    expect(result, <String, dynamic>{
      'data': matches(
226
        r'^'
227
        r'RenderView#[0-9a-f]{5}\n'
228
        r'   debug mode enabled - [a-zA-Z]+\n'
229 230 231
        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'
232
        r'$',
233
      ),
234
    });
235 236
  });

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

    expect(result, <String, dynamic>{
      'data': matches(
243
        r'^'
244 245
        r'TransformLayer#[0-9a-f]{5}\n'
        r'   owner: RenderView#[0-9a-f]{5}\n'
246
        r'   creator: RenderView\n'
247
        r'   engine layer: (TransformEngineLayer|PersistedTransform)#[0-9a-f]{5}\n'
248
        r'   handles: 1\n'
249 250
        r'   offset: Offset\(0\.0, 0\.0\)\n'
        r'   transform:\n'
251 252
        r'     \[0] 3\.0,0\.0,0\.0,0\.0\n'
        r'     \[1] 0\.0,3\.0,0\.0,0\.0\n'
253 254
        r'     \[2] 0\.0,0\.0,1\.0,0\.0\n'
        r'     \[3] 0\.0,0\.0,0\.0,1\.0\n'
255
        r'$',
256
      ),
257
    });
258 259
  });

260
  test('Service extensions - debugDumpSemanticsTreeInTraversalOrder', () async {
261
    await binding.doFrame();
262 263 264
    final Map<String, dynamic> result = await binding.testExtension('debugDumpSemanticsTreeInTraversalOrder', <String, String>{});

    expect(result, <String, String>{
265 266 267 268
      'data': 'Semantics not generated.\n'
        'For performance reasons, the framework only generates semantics when asked to do so by the platform.\n'
        'Usually, platforms only ask for semantics when assistive technologies (like screen readers) are running.\n'
        'To generate semantics, try turning on an assistive technology (like VoiceOver or TalkBack) on your device.'
269
    });
270 271 272 273
  });

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

    expect(result, <String, String>{
277 278 279 280
      'data': 'Semantics not generated.\n'
        'For performance reasons, the framework only generates semantics when asked to do so by the platform.\n'
        'Usually, platforms only ask for semantics when assistive technologies (like screen readers) are running.\n'
        'To generate semantics, try turning on an assistive technology (like VoiceOver or TalkBack) on your device.'
281
    });
282 283
  });

284
  test('Service extensions - debugPaint', () async {
285 286
    final Iterable<Map<String, dynamic>> extensionChangedEvents = binding.getServiceExtensionStateChangedEvents('ext.flutter.debugPaint');
    Map<String, dynamic> extensionChangedEvent;
287 288
    Map<String, dynamic> result;
    Future<Map<String, dynamic>> pendingResult;
289 290 291 292 293
    bool completed;

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

339
  test('Service extensions - debugPaintBaselinesEnabled', () async {
340 341
    Map<String, dynamic> result;
    Future<Map<String, dynamic>> pendingResult;
342 343 344 345 346
    bool completed;

    expect(binding.frameScheduled, isFalse);
    expect(debugPaintBaselinesEnabled, false);
    result = await binding.testExtension('debugPaintBaselinesEnabled', <String, String>{});
347
    expect(result, <String, String>{'enabled': 'false'});
348 349
    expect(debugPaintBaselinesEnabled, false);
    expect(binding.frameScheduled, isFalse);
350
    pendingResult = binding.testExtension('debugPaintBaselinesEnabled', <String, String>{'enabled': 'true'});
351 352 353 354 355 356 357 358 359 360
    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;
361
    expect(result, <String, String>{'enabled': 'true'});
362 363
    expect(debugPaintBaselinesEnabled, true);
    result = await binding.testExtension('debugPaintBaselinesEnabled', <String, String>{});
364
    expect(result, <String, String>{'enabled': 'true'});
365 366
    expect(debugPaintBaselinesEnabled, true);
    expect(binding.frameScheduled, isFalse);
367
    pendingResult = binding.testExtension('debugPaintBaselinesEnabled', <String, String>{'enabled': 'false'});
368 369 370 371 372
    await binding.flushMicrotasks();
    expect(binding.frameScheduled, isTrue);
    await binding.doFrame();
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
373
    expect(result, <String, String>{'enabled': 'false'});
374 375
    expect(debugPaintBaselinesEnabled, false);
    result = await binding.testExtension('debugPaintBaselinesEnabled', <String, String>{});
376
    expect(result, <String, String>{'enabled': 'false'});
377 378 379 380
    expect(debugPaintBaselinesEnabled, false);
    expect(binding.frameScheduled, isFalse);
  });

381 382
  test('Service extensions - invertOversizedImages', () async {
    Map<String, dynamic> result;
383 384
    Future<Map<String, dynamic>> pendingResult;
    bool completed;
385 386 387 388 389 390

    expect(binding.frameScheduled, isFalse);
    expect(debugInvertOversizedImages, false);
    result = await binding.testExtension('invertOversizedImages', <String, String>{});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugInvertOversizedImages, false);
391 392 393 394 395 396 397 398 399 400 401 402 403
    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;
404 405
    expect(result, <String, String>{'enabled': 'true'});
    expect(debugInvertOversizedImages, true);
406

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

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

427 428 429 430 431 432 433
  test('Service extensions - profileWidgetBuilds', () async {
    Map<String, dynamic> result;

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

    result = await binding.testExtension('profileWidgetBuilds', <String, String>{});
434
    expect(result, <String, String>{'enabled': 'false'});
435 436
    expect(debugProfileBuildsEnabled, false);

437 438
    result = await binding.testExtension('profileWidgetBuilds', <String, String>{'enabled': 'true'});
    expect(result, <String, String>{'enabled': 'true'});
439 440 441
    expect(debugProfileBuildsEnabled, true);

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

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

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

453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
    expect(binding.frameScheduled, isFalse);
  });

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

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

    result = await binding.testExtension('profileUserWidgetBuilds', <String, String>{});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugProfileBuildsEnabledUserWidgets, false);

    result = await binding.testExtension('profileUserWidgetBuilds', <String, String>{'enabled': 'true'});
    expect(result, <String, String>{'enabled': 'true'});
    expect(debugProfileBuildsEnabledUserWidgets, true);

    result = await binding.testExtension('profileUserWidgetBuilds', <String, String>{});
    expect(result, <String, String>{'enabled': 'true'});
    expect(debugProfileBuildsEnabledUserWidgets, true);

    result = await binding.testExtension('profileUserWidgetBuilds', <String, String>{'enabled': 'false'});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugProfileBuildsEnabledUserWidgets, false);

    result = await binding.testExtension('profileUserWidgetBuilds', <String, String>{});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugProfileBuildsEnabledUserWidgets, false);

482 483 484
    expect(binding.frameScheduled, isFalse);
  });

485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
  test('Service extensions - profileRenderObjectPaints', () async {
    Map<String, dynamic> result;

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

    result = await binding.testExtension('profileRenderObjectPaints', <String, String>{});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugProfilePaintsEnabled, false);

    result = await binding.testExtension('profileRenderObjectPaints', <String, String>{'enabled': 'true'});
    expect(result, <String, String>{'enabled': 'true'});
    expect(debugProfilePaintsEnabled, true);

    result = await binding.testExtension('profileRenderObjectPaints', <String, String>{});
    expect(result, <String, String>{'enabled': 'true'});
    expect(debugProfilePaintsEnabled, true);

    result = await binding.testExtension('profileRenderObjectPaints', <String, String>{'enabled': 'false'});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugProfilePaintsEnabled, false);

    result = await binding.testExtension('profileRenderObjectPaints', <String, String>{});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugProfilePaintsEnabled, false);

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

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

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

    result = await binding.testExtension('profileRenderObjectLayouts', <String, String>{});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugProfileLayoutsEnabled, false);

    result = await binding.testExtension('profileRenderObjectLayouts', <String, String>{'enabled': 'true'});
    expect(result, <String, String>{'enabled': 'true'});
    expect(debugProfileLayoutsEnabled, true);

    result = await binding.testExtension('profileRenderObjectLayouts', <String, String>{});
    expect(result, <String, String>{'enabled': 'true'});
    expect(debugProfileLayoutsEnabled, true);

    result = await binding.testExtension('profileRenderObjectLayouts', <String, String>{'enabled': 'false'});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugProfileLayoutsEnabled, false);

    result = await binding.testExtension('profileRenderObjectLayouts', <String, String>{});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugProfileLayoutsEnabled, false);

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

543
  test('Service extensions - evict', () async {
544
    Map<String, dynamic> result;
545 546 547
    bool completed;

    completed = false;
548
    TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', (ByteData? message) async {
549
      expect(utf8.decode(message!.buffer.asUint8List()), 'test');
550
      completed = true;
551
      return ByteData(5); // 0x0000000000
552 553
    });
    bool data;
554 555 556 557
    data = await rootBundle.loadStructuredData<bool>('test', (String value) async {
      expect(value, '\x00\x00\x00\x00\x00');
      return true;
    });
558 559 560
    expect(data, isTrue);
    expect(completed, isTrue);
    completed = false;
561
    data = await rootBundle.loadStructuredData('test', (String value) async {
562
      throw Error();
563
    });
564 565
    expect(data, isTrue);
    expect(completed, isFalse);
566 567
    result = await binding.testExtension('evict', <String, String>{'value': 'test'});
    expect(result, <String, String>{'value': ''});
568
    expect(completed, isFalse);
569 570 571 572
    data = await rootBundle.loadStructuredData<bool>('test', (String value) async {
      expect(value, '\x00\x00\x00\x00\x00');
      return false;
    });
573 574
    expect(data, isFalse);
    expect(completed, isTrue);
575
    TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', null);
576 577 578 579
  });

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

584
  test('Service extensions - platformOverride', () async {
585 586
    final Iterable<Map<String, dynamic>> extensionChangedEvents = binding.getServiceExtensionStateChangedEvents('ext.flutter.platformOverride');
    Map<String, dynamic> extensionChangedEvent;
587
    Map<String, dynamic> result;
588 589 590 591 592 593

    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);
594
    expect(extensionChangedEvents, isEmpty);
595 596 597 598
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'iOS'}));
    expect(result, <String, String>{'value': 'iOS'});
    expect(binding.reassembled, 1);
    expect(defaultTargetPlatform, TargetPlatform.iOS);
599 600 601 602
    expect(extensionChangedEvents.length, 1);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'iOS');
Dan Field's avatar
Dan Field committed
603 604 605 606 607 608 609 610
    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');
611 612
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'android'}));
    expect(result, <String, String>{'value': 'android'});
Dan Field's avatar
Dan Field committed
613
    expect(binding.reassembled, 3);
614
    expect(defaultTargetPlatform, TargetPlatform.android);
Dan Field's avatar
Dan Field committed
615
    expect(extensionChangedEvents.length, 3);
616 617 618
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'android');
619 620
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'fuchsia'}));
    expect(result, <String, String>{'value': 'fuchsia'});
Dan Field's avatar
Dan Field committed
621
    expect(binding.reassembled, 4);
622
    expect(defaultTargetPlatform, TargetPlatform.fuchsia);
Dan Field's avatar
Dan Field committed
623
    expect(extensionChangedEvents.length, 4);
624 625 626
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'fuchsia');
627 628
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'default'}));
    expect(result, <String, String>{'value': 'android'});
Dan Field's avatar
Dan Field committed
629
    expect(binding.reassembled, 5);
630
    expect(defaultTargetPlatform, TargetPlatform.android);
Dan Field's avatar
Dan Field committed
631
    expect(extensionChangedEvents.length, 5);
632 633 634
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'android');
635 636
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'iOS'}));
    expect(result, <String, String>{'value': 'iOS'});
Dan Field's avatar
Dan Field committed
637
    expect(binding.reassembled, 6);
638
    expect(defaultTargetPlatform, TargetPlatform.iOS);
Dan Field's avatar
Dan Field committed
639
    expect(extensionChangedEvents.length, 6);
640 641 642
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'iOS');
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
    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');
659 660
    result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'bogus'}));
    expect(result, <String, String>{'value': 'android'});
661
    expect(binding.reassembled, 9);
662
    expect(defaultTargetPlatform, TargetPlatform.android);
663
    expect(extensionChangedEvents.length, 9);
664 665 666
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
    expect(extensionChangedEvent['value'], 'android');
667 668 669
    binding.reassembled = 0;
  });

670
  test('Service extensions - repaintRainbow', () async {
671 672
    Map<String, dynamic> result;
    Future<Map<String, dynamic>> pendingResult;
673 674 675 676 677
    bool completed;

    expect(binding.frameScheduled, isFalse);
    expect(debugRepaintRainbowEnabled, false);
    result = await binding.testExtension('repaintRainbow', <String, String>{});
678
    expect(result, <String, String>{'enabled': 'false'});
679 680
    expect(debugRepaintRainbowEnabled, false);
    expect(binding.frameScheduled, isFalse);
681
    pendingResult = binding.testExtension('repaintRainbow', <String, String>{'enabled': 'true'});
682 683 684 685 686 687
    completed = false;
    pendingResult.whenComplete(() { completed = true; });
    await binding.flushMicrotasks();
    expect(completed, true);
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
688
    expect(result, <String, String>{'enabled': 'true'});
689 690
    expect(debugRepaintRainbowEnabled, true);
    result = await binding.testExtension('repaintRainbow', <String, String>{});
691
    expect(result, <String, String>{'enabled': 'true'});
692 693
    expect(debugRepaintRainbowEnabled, true);
    expect(binding.frameScheduled, isFalse);
694
    pendingResult = binding.testExtension('repaintRainbow', <String, String>{'enabled': 'false'});
695 696 697 698 699
    completed = false;
    pendingResult.whenComplete(() { completed = true; });
    await binding.flushMicrotasks();
    expect(completed, false);
    expect(binding.frameScheduled, isTrue);
700
    await binding.doFrame();
701 702 703 704
    await binding.flushMicrotasks();
    expect(completed, true);
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
705
    expect(result, <String, String>{'enabled': 'false'});
706 707
    expect(debugRepaintRainbowEnabled, false);
    result = await binding.testExtension('repaintRainbow', <String, String>{});
708
    expect(result, <String, String>{'enabled': 'false'});
709 710 711 712
    expect(debugRepaintRainbowEnabled, false);
    expect(binding.frameScheduled, isFalse);
  });

713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838
  test('Service extensions - debugDisableClipLayers', () async {
    Map<String, dynamic> result;
    Future<Map<String, dynamic>> pendingResult;
    bool completed;

    expect(binding.frameScheduled, isFalse);
    expect(debugDisableClipLayers, false);
    result = await binding.testExtension('debugDisableClipLayers', <String, String>{});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugDisableClipLayers, false);
    expect(binding.frameScheduled, isFalse);
    pendingResult = binding.testExtension('debugDisableClipLayers', <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;
    expect(result, <String, String>{'enabled': 'true'});
    expect(debugDisableClipLayers, true);
    result = await binding.testExtension('debugDisableClipLayers', <String, String>{});
    expect(result, <String, String>{'enabled': 'true'});
    expect(debugDisableClipLayers, true);
    expect(binding.frameScheduled, isFalse);
    pendingResult = binding.testExtension('debugDisableClipLayers', <String, String>{'enabled': 'false'});
    await binding.flushMicrotasks();
    expect(binding.frameScheduled, isTrue);
    await binding.doFrame();
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugDisableClipLayers, false);
    result = await binding.testExtension('debugDisableClipLayers', <String, String>{});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugDisableClipLayers, false);
    expect(binding.frameScheduled, isFalse);
  });

  test('Service extensions - debugDisablePhysicalShapeLayers', () async {
    Map<String, dynamic> result;
    Future<Map<String, dynamic>> pendingResult;
    bool completed;

    expect(binding.frameScheduled, isFalse);
    expect(debugDisablePhysicalShapeLayers, false);
    result = await binding.testExtension('debugDisablePhysicalShapeLayers', <String, String>{});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugDisablePhysicalShapeLayers, false);
    expect(binding.frameScheduled, isFalse);
    pendingResult = binding.testExtension('debugDisablePhysicalShapeLayers', <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;
    expect(result, <String, String>{'enabled': 'true'});
    expect(debugDisablePhysicalShapeLayers, true);
    result = await binding.testExtension('debugDisablePhysicalShapeLayers', <String, String>{});
    expect(result, <String, String>{'enabled': 'true'});
    expect(debugDisablePhysicalShapeLayers, true);
    expect(binding.frameScheduled, isFalse);
    pendingResult = binding.testExtension('debugDisablePhysicalShapeLayers', <String, String>{'enabled': 'false'});
    await binding.flushMicrotasks();
    expect(binding.frameScheduled, isTrue);
    await binding.doFrame();
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugDisablePhysicalShapeLayers, false);
    result = await binding.testExtension('debugDisablePhysicalShapeLayers', <String, String>{});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugDisablePhysicalShapeLayers, false);
    expect(binding.frameScheduled, isFalse);
  });

  test('Service extensions - debugDisableOpacityLayers', () async {
    Map<String, dynamic> result;
    Future<Map<String, dynamic>> pendingResult;
    bool completed;

    expect(binding.frameScheduled, isFalse);
    expect(debugDisableOpacityLayers, false);
    result = await binding.testExtension('debugDisableOpacityLayers', <String, String>{});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugDisableOpacityLayers, false);
    expect(binding.frameScheduled, isFalse);
    pendingResult = binding.testExtension('debugDisableOpacityLayers', <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;
    expect(result, <String, String>{'enabled': 'true'});
    expect(debugDisableOpacityLayers, true);
    result = await binding.testExtension('debugDisableOpacityLayers', <String, String>{});
    expect(result, <String, String>{'enabled': 'true'});
    expect(debugDisableOpacityLayers, true);
    expect(binding.frameScheduled, isFalse);
    pendingResult = binding.testExtension('debugDisableOpacityLayers', <String, String>{'enabled': 'false'});
    await binding.flushMicrotasks();
    expect(binding.frameScheduled, isTrue);
    await binding.doFrame();
    expect(binding.frameScheduled, isFalse);
    result = await pendingResult;
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugDisableOpacityLayers, false);
    result = await binding.testExtension('debugDisableOpacityLayers', <String, String>{});
    expect(result, <String, String>{'enabled': 'false'});
    expect(debugDisableOpacityLayers, false);
    expect(binding.frameScheduled, isFalse);
  });

839
  test('Service extensions - reassemble', () async {
840 841
    Map<String, dynamic> result;
    Future<Map<String, dynamic>> pendingResult;
842 843 844 845 846 847 848 849 850
    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);
851
    await binding.flushMicrotasks();
852
    await binding.doFrame();
853 854 855 856 857 858 859 860 861
    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 {
862
    Map<String, dynamic> result;
863

864 865 866 867 868 869
    // The performance overlay service extension is disabled on the web.
    if (kIsWeb) {
      expect(binding.extensions.containsKey('showPerformanceOverlay'), isFalse);
      return;
    }

870 871 872
    expect(binding.frameScheduled, isFalse);
    expect(WidgetsApp.showPerformanceOverlayOverride, false);
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{});
873
    expect(result, <String, String>{'enabled': 'false'});
874
    expect(WidgetsApp.showPerformanceOverlayOverride, false);
875 876
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{'enabled': 'true'});
    expect(result, <String, String>{'enabled': 'true'});
877 878
    expect(WidgetsApp.showPerformanceOverlayOverride, true);
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{});
879
    expect(result, <String, String>{'enabled': 'true'});
880
    expect(WidgetsApp.showPerformanceOverlayOverride, true);
881 882
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{'enabled': 'false'});
    expect(result, <String, String>{'enabled': 'false'});
883 884
    expect(WidgetsApp.showPerformanceOverlayOverride, false);
    result = await binding.testExtension('showPerformanceOverlay', <String, String>{});
885
    expect(result, <String, String>{'enabled': 'false'});
886 887 888 889
    expect(WidgetsApp.showPerformanceOverlayOverride, false);
    expect(binding.frameScheduled, isFalse);
  });

890 891 892 893 894
  test('Service extensions - debugWidgetInspector', () async {
    Map<String, dynamic> result;
    expect(binding.frameScheduled, isFalse);
    expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
    result = await binding.testExtension('debugWidgetInspector', <String, String>{});
895
    expect(result, <String, String>{'enabled': 'false'});
896
    expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
897 898
    result = await binding.testExtension('debugWidgetInspector', <String, String>{'enabled': 'true'});
    expect(result, <String, String>{'enabled': 'true'});
899 900
    expect(WidgetsApp.debugShowWidgetInspectorOverride, true);
    result = await binding.testExtension('debugWidgetInspector', <String, String>{});
901
    expect(result, <String, String>{'enabled': 'true'});
902
    expect(WidgetsApp.debugShowWidgetInspectorOverride, true);
903 904
    result = await binding.testExtension('debugWidgetInspector', <String, String>{'enabled': 'false'});
    expect(result, <String, String>{'enabled': 'false'});
905 906
    expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
    result = await binding.testExtension('debugWidgetInspector', <String, String>{});
907
    expect(result, <String, String>{'enabled': 'false'});
908 909 910 911
    expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
    expect(binding.frameScheduled, isFalse);
  });

912
  test('Service extensions - timeDilation', () async {
913 914
    final Iterable<Map<String, dynamic>> extensionChangedEvents = binding.getServiceExtensionStateChangedEvents('ext.flutter.timeDilation');
    Map<String, dynamic> extensionChangedEvent;
915
    Map<String, dynamic> result;
916 917 918 919

    expect(binding.frameScheduled, isFalse);
    expect(timeDilation, 1.0);
    result = await binding.testExtension('timeDilation', <String, String>{});
920
    expect(result, <String, String>{'timeDilation': 1.0.toString()});
921
    expect(timeDilation, 1.0);
922
    expect(extensionChangedEvents, isEmpty);
923
    result = await binding.testExtension('timeDilation', <String, String>{'timeDilation': '100.0'});
924
    expect(result, <String, String>{'timeDilation': 100.0.toString()});
925
    expect(timeDilation, 100.0);
926 927 928
    expect(extensionChangedEvents.length, 1);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.timeDilation');
929
    expect(extensionChangedEvent['value'], 100.0.toString());
930
    result = await binding.testExtension('timeDilation', <String, String>{});
931
    expect(result, <String, String>{'timeDilation': 100.0.toString()});
932
    expect(timeDilation, 100.0);
933
    expect(extensionChangedEvents.length, 1);
934
    result = await binding.testExtension('timeDilation', <String, String>{'timeDilation': '1.0'});
935
    expect(result, <String, String>{'timeDilation': 1.0.toString()});
936
    expect(timeDilation, 1.0);
937 938 939
    expect(extensionChangedEvents.length, 2);
    extensionChangedEvent = extensionChangedEvents.last;
    expect(extensionChangedEvent['extension'], 'ext.flutter.timeDilation');
940
    expect(extensionChangedEvent['value'], 1.0.toString());
941
    result = await binding.testExtension('timeDilation', <String, String>{});
942
    expect(result, <String, String>{'timeDilation': 1.0.toString()});
943
    expect(timeDilation, 1.0);
944
    expect(extensionChangedEvents.length, 2);
945 946 947
    expect(binding.frameScheduled, isFalse);
  });

948
  test('Service extensions - brightnessOverride', () async {
949
    Map<String, dynamic> result;
950 951 952 953 954
    result = await binding.testExtension('brightnessOverride', <String, String>{});
    final String brightnessValue = result['value'] as String;

    expect(brightnessValue, 'Brightness.light');
  });
955 956 957 958 959 960 961 962 963 964 965 966 967

  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');
  });
968 969 970 971 972 973 974 975 976 977 978 979 980

  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=/');
  });
981
}