// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // 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']) 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'; import 'package:flutter_test/flutter_test.dart'; class TestServiceExtensionsBinding extends BindingBase with SchedulerBinding, ServicesBinding, GestureBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding, TestDefaultBinaryMessengerBinding { final Map<String, ServiceExtensionCallback> extensions = <String, ServiceExtensionCallback>{}; final Map<String, List<Map<String, dynamic>>> eventsDispatched = <String, List<Map<String, dynamic>>>{}; @override @protected void registerServiceExtension({ required String name, required ServiceExtensionCallback callback, }) { expect(extensions.containsKey(name), isFalse); extensions[name] = callback; } @override void postEvent(String eventKind, Map<String, 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); } Future<Map<String, dynamic>> testExtension(String name, Map<String, String> arguments) { expect(extensions.containsKey(name), isTrue); return extensions[name]!(arguments); } int reassembled = 0; bool pendingReassemble = false; @override Future<void> performReassemble() { reassembled += 1; pendingReassemble = true; return super.performReassemble(); } bool frameScheduled = false; @override void scheduleFrame() { ensureFrameCallbacksRegistered(); frameScheduled = true; } Future<void> doFrame() async { frameScheduled = false; ui.window.onBeginFrame?.call(Duration.zero); await flushMicrotasks(); ui.window.onDrawFrame?.call(); ui.window.onReportTimings?.call(<ui.FrameTiming>[]); } @override void scheduleForcedFrame() { expect(true, isFalse); } @override void scheduleWarmUpFrame() { expect(pendingReassemble, isTrue); pendingReassemble = false; } Future<void> flushMicrotasks() { final Completer<void> completer = Completer<void>(); Timer.run(completer.complete); return completer.future; } } late TestServiceExtensionsBinding binding; Future<Map<String, dynamic>> hasReassemble(Future<Map<String, dynamic>> pendingResult) async { bool completed = false; pendingResult.whenComplete(() { completed = true; }); expect(binding.frameScheduled, isFalse); await binding.flushMicrotasks(); expect(binding.frameScheduled, isTrue); expect(completed, isFalse); await binding.flushMicrotasks(); await binding.doFrame(); await binding.flushMicrotasks(); expect(completed, isTrue); expect(binding.frameScheduled, isFalse); return pendingResult; } void main() { final List<String?> console = <String?>[]; setUpAll(() async { binding = TestServiceExtensionsBinding()..scheduleFrame(); expect(binding.frameScheduled, isTrue); // 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>{}); expect(firstFrameResult, <String, String>{'enabled': 'false'}); expect(binding.firstFrameRasterized, isFalse); firstFrameResult = await binding.testExtension('didSendFirstFrameRasterizedEvent', <String, String>{}); expect(firstFrameResult, <String, String>{'enabled': 'false'}); await binding.doFrame(); expect(binding.debugDidSendFirstFrameEvent, isTrue); firstFrameResult = await binding.testExtension('didSendFirstFrameEvent', <String, String>{}); expect(firstFrameResult, <String, String>{'enabled': 'true'}); expect(binding.firstFrameRasterized, isTrue); firstFrameResult = await binding.testExtension('didSendFirstFrameRasterizedEvent', <String, String>{}); expect(firstFrameResult, <String, String>{'enabled': 'true'}); expect(binding.frameScheduled, isFalse); expect(debugPrint, equals(debugPrintThrottled)); debugPrint = (String? message, { int? wrapWidth }) { console.add(message); }; }); tearDownAll(() async { // See widget_inspector_test.dart for tests of the ext.flutter.inspector // service extensions included in this count. int widgetInspectorExtensionCount = 16; if (WidgetInspectorService.instance.isWidgetCreationTracked()) { // Some inspector extensions are only exposed if widget creation locations // are tracked. widgetInspectorExtensionCount += 2; } // The following service extensions are disabled in web: // 1. exit // 2. showPerformanceOverlay const int disabledExtensions = kIsWeb ? 2 : 0; // If you add a service extension... TEST IT! :-) // ...then increment this number. expect(binding.extensions.length, 35 + widgetInspectorExtensionCount - disabledExtensions); expect(console, isEmpty); debugPrint = debugPrintThrottled; }); // The following list is alphabetical, one test per extension. test('Service extensions - debugAllowBanner', () async { Map<String, dynamic> result; expect(binding.frameScheduled, isFalse); expect(WidgetsApp.debugAllowBannerOverride, true); result = await binding.testExtension('debugAllowBanner', <String, String>{}); expect(result, <String, String>{'enabled': 'true'}); expect(WidgetsApp.debugAllowBannerOverride, true); result = await binding.testExtension('debugAllowBanner', <String, String>{'enabled': 'false'}); expect(result, <String, String>{'enabled': 'false'}); expect(WidgetsApp.debugAllowBannerOverride, false); result = await binding.testExtension('debugAllowBanner', <String, String>{}); expect(result, <String, String>{'enabled': 'false'}); expect(WidgetsApp.debugAllowBannerOverride, false); result = await binding.testExtension('debugAllowBanner', <String, String>{'enabled': 'true'}); expect(result, <String, String>{'enabled': 'true'}); expect(WidgetsApp.debugAllowBannerOverride, true); result = await binding.testExtension('debugAllowBanner', <String, String>{}); expect(result, <String, String>{'enabled': 'true'}); expect(WidgetsApp.debugAllowBannerOverride, true); expect(binding.frameScheduled, isFalse); }); test('Service extensions - debugDumpApp', () async { final Map<String, dynamic> result = await binding.testExtension('debugDumpApp', <String, String>{}); expect(result, <String, dynamic>{ 'data': matches('TestServiceExtensionsBinding - DEBUG MODE\n<no tree currently mounted>'), }); }); test('Service extensions - debugDumpRenderTree', () async { await binding.doFrame(); final Map<String, dynamic> result = await binding.testExtension('debugDumpRenderTree', <String, String>{}); expect(result, <String, dynamic>{ 'data': matches( r'^' r'RenderView#[0-9a-f]{5}\n' r' debug mode enabled - [a-zA-Z]+\n' 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' r'$', ), }); }); test('Service extensions - debugDumpLayerTree', () async { await binding.doFrame(); final Map<String, dynamic> result = await binding.testExtension('debugDumpLayerTree', <String, String>{}); expect(result, <String, dynamic>{ 'data': matches( r'^' r'TransformLayer#[0-9a-f]{5}\n' r' owner: RenderView#[0-9a-f]{5}\n' r' creator: RenderView\n' r' engine layer: (TransformEngineLayer|PersistedTransform)#[0-9a-f]{5}\n' r' handles: 1\n' r' offset: Offset\(0\.0, 0\.0\)\n' r' transform:\n' r' \[0] 3\.0,0\.0,0\.0,0\.0\n' r' \[1] 0\.0,3\.0,0\.0,0\.0\n' r' \[2] 0\.0,0\.0,1\.0,0\.0\n' r' \[3] 0\.0,0\.0,0\.0,1\.0\n' r'$', ), }); }); test('Service extensions - debugDumpSemanticsTreeInTraversalOrder', () async { await binding.doFrame(); final Map<String, dynamic> result = await binding.testExtension('debugDumpSemanticsTreeInTraversalOrder', <String, String>{}); expect(result, <String, String>{ 'data': 'Semantics not collected.', }); }); test('Service extensions - debugDumpSemanticsTreeInInverseHitTestOrder', () async { await binding.doFrame(); final Map<String, dynamic> result = await binding.testExtension('debugDumpSemanticsTreeInInverseHitTestOrder', <String, String>{}); expect(result, <String, String>{ 'data': 'Semantics not collected.', }); }); test('Service extensions - debugPaint', () async { final Iterable<Map<String, dynamic>> extensionChangedEvents = binding.getServiceExtensionStateChangedEvents('ext.flutter.debugPaint'); Map<String, dynamic> extensionChangedEvent; Map<String, dynamic> result; Future<Map<String, dynamic>> pendingResult; bool completed; expect(binding.frameScheduled, isFalse); expect(debugPaintSizeEnabled, false); result = await binding.testExtension('debugPaint', <String, String>{}); expect(result, <String, String>{'enabled': 'false'}); expect(debugPaintSizeEnabled, false); expect(extensionChangedEvents, isEmpty); expect(binding.frameScheduled, isFalse); pendingResult = binding.testExtension('debugPaint', <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(debugPaintSizeEnabled, true); expect(extensionChangedEvents.length, 1); extensionChangedEvent = extensionChangedEvents.last; expect(extensionChangedEvent['extension'], 'ext.flutter.debugPaint'); expect(extensionChangedEvent['value'], 'true'); result = await binding.testExtension('debugPaint', <String, String>{}); expect(result, <String, String>{'enabled': 'true'}); expect(debugPaintSizeEnabled, true); expect(extensionChangedEvents.length, 1); expect(binding.frameScheduled, isFalse); pendingResult = binding.testExtension('debugPaint', <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(debugPaintSizeEnabled, false); expect(extensionChangedEvents.length, 2); extensionChangedEvent = extensionChangedEvents.last; expect(extensionChangedEvent['extension'], 'ext.flutter.debugPaint'); expect(extensionChangedEvent['value'], 'false'); result = await binding.testExtension('debugPaint', <String, String>{}); expect(result, <String, String>{'enabled': 'false'}); expect(debugPaintSizeEnabled, false); expect(extensionChangedEvents.length, 2); expect(binding.frameScheduled, isFalse); }); test('Service extensions - debugPaintBaselinesEnabled', () async { Map<String, dynamic> result; Future<Map<String, dynamic>> pendingResult; bool completed; expect(binding.frameScheduled, isFalse); expect(debugPaintBaselinesEnabled, false); result = await binding.testExtension('debugPaintBaselinesEnabled', <String, String>{}); expect(result, <String, String>{'enabled': 'false'}); expect(debugPaintBaselinesEnabled, false); expect(binding.frameScheduled, isFalse); pendingResult = binding.testExtension('debugPaintBaselinesEnabled', <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(debugPaintBaselinesEnabled, true); result = await binding.testExtension('debugPaintBaselinesEnabled', <String, String>{}); expect(result, <String, String>{'enabled': 'true'}); expect(debugPaintBaselinesEnabled, true); expect(binding.frameScheduled, isFalse); pendingResult = binding.testExtension('debugPaintBaselinesEnabled', <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(debugPaintBaselinesEnabled, false); result = await binding.testExtension('debugPaintBaselinesEnabled', <String, String>{}); expect(result, <String, String>{'enabled': 'false'}); expect(debugPaintBaselinesEnabled, false); expect(binding.frameScheduled, isFalse); }); test('Service extensions - invertOversizedImages', () async { Map<String, dynamic> result; Future<Map<String, dynamic>> pendingResult; bool completed; expect(binding.frameScheduled, isFalse); expect(debugInvertOversizedImages, false); result = await binding.testExtension('invertOversizedImages', <String, String>{}); expect(result, <String, String>{'enabled': 'false'}); expect(debugInvertOversizedImages, false); 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; expect(result, <String, String>{'enabled': 'true'}); expect(debugInvertOversizedImages, true); result = await binding.testExtension('invertOversizedImages', <String, String>{}); expect(result, <String, String>{'enabled': 'true'}); expect(debugInvertOversizedImages, true); 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; expect(result, <String, String>{'enabled': 'false'}); expect(debugInvertOversizedImages, false); result = await binding.testExtension('invertOversizedImages', <String, String>{}); expect(result, <String, String>{'enabled': 'false'}); expect(debugInvertOversizedImages, false); expect(binding.frameScheduled, isFalse); }); test('Service extensions - profileWidgetBuilds', () async { Map<String, dynamic> result; expect(binding.frameScheduled, isFalse); expect(debugProfileBuildsEnabled, false); result = await binding.testExtension('profileWidgetBuilds', <String, String>{}); expect(result, <String, String>{'enabled': 'false'}); expect(debugProfileBuildsEnabled, false); result = await binding.testExtension('profileWidgetBuilds', <String, String>{'enabled': 'true'}); expect(result, <String, String>{'enabled': 'true'}); expect(debugProfileBuildsEnabled, true); result = await binding.testExtension('profileWidgetBuilds', <String, String>{}); expect(result, <String, String>{'enabled': 'true'}); expect(debugProfileBuildsEnabled, true); result = await binding.testExtension('profileWidgetBuilds', <String, String>{'enabled': 'false'}); expect(result, <String, String>{'enabled': 'false'}); expect(debugProfileBuildsEnabled, false); result = await binding.testExtension('profileWidgetBuilds', <String, String>{}); expect(result, <String, String>{'enabled': 'false'}); expect(debugProfileBuildsEnabled, false); expect(binding.frameScheduled, isFalse); }); 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); }); test('Service extensions - evict', () async { Map<String, dynamic> result; bool completed; completed = false; TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', (ByteData? message) async { expect(utf8.decode(message!.buffer.asUint8List()), 'test'); completed = true; return ByteData(5); // 0x0000000000 }); 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 { throw Error(); }); expect(data, isTrue); expect(completed, isFalse); result = await binding.testExtension('evict', <String, String>{'value': 'test'}); expect(result, <String, String>{'value': ''}); 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); TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', null); }); test('Service extensions - exit', () async { // no test for _calling_ 'exit', because that should terminate the process! // Not expecting extension to be available for web platform. expect(binding.extensions.containsKey('exit'), !isBrowser); }); test('Service extensions - platformOverride', () async { final Iterable<Map<String, dynamic>> extensionChangedEvents = binding.getServiceExtensionStateChangedEvents('ext.flutter.platformOverride'); Map<String, dynamic> extensionChangedEvent; Map<String, dynamic> result; 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); expect(extensionChangedEvents, isEmpty); result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'iOS'})); expect(result, <String, String>{'value': 'iOS'}); expect(binding.reassembled, 1); expect(defaultTargetPlatform, TargetPlatform.iOS); expect(extensionChangedEvents.length, 1); extensionChangedEvent = extensionChangedEvents.last; expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride'); expect(extensionChangedEvent['value'], 'iOS'); 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'); result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'android'})); expect(result, <String, String>{'value': 'android'}); expect(binding.reassembled, 3); expect(defaultTargetPlatform, TargetPlatform.android); expect(extensionChangedEvents.length, 3); extensionChangedEvent = extensionChangedEvents.last; expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride'); expect(extensionChangedEvent['value'], 'android'); result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'fuchsia'})); expect(result, <String, String>{'value': 'fuchsia'}); expect(binding.reassembled, 4); expect(defaultTargetPlatform, TargetPlatform.fuchsia); expect(extensionChangedEvents.length, 4); extensionChangedEvent = extensionChangedEvents.last; expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride'); expect(extensionChangedEvent['value'], 'fuchsia'); result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'default'})); expect(result, <String, String>{'value': 'android'}); expect(binding.reassembled, 5); expect(defaultTargetPlatform, TargetPlatform.android); expect(extensionChangedEvents.length, 5); extensionChangedEvent = extensionChangedEvents.last; expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride'); expect(extensionChangedEvent['value'], 'android'); result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'iOS'})); expect(result, <String, String>{'value': 'iOS'}); expect(binding.reassembled, 6); expect(defaultTargetPlatform, TargetPlatform.iOS); expect(extensionChangedEvents.length, 6); extensionChangedEvent = extensionChangedEvents.last; expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride'); expect(extensionChangedEvent['value'], 'iOS'); 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'); result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'bogus'})); expect(result, <String, String>{'value': 'android'}); expect(binding.reassembled, 9); expect(defaultTargetPlatform, TargetPlatform.android); expect(extensionChangedEvents.length, 9); extensionChangedEvent = extensionChangedEvents.last; expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride'); expect(extensionChangedEvent['value'], 'android'); binding.reassembled = 0; }); test('Service extensions - repaintRainbow', () async { Map<String, dynamic> result; Future<Map<String, dynamic>> pendingResult; bool completed; expect(binding.frameScheduled, isFalse); expect(debugRepaintRainbowEnabled, false); result = await binding.testExtension('repaintRainbow', <String, String>{}); expect(result, <String, String>{'enabled': 'false'}); expect(debugRepaintRainbowEnabled, false); expect(binding.frameScheduled, isFalse); pendingResult = binding.testExtension('repaintRainbow', <String, String>{'enabled': 'true'}); completed = false; pendingResult.whenComplete(() { completed = true; }); await binding.flushMicrotasks(); expect(completed, true); expect(binding.frameScheduled, isFalse); result = await pendingResult; expect(result, <String, String>{'enabled': 'true'}); expect(debugRepaintRainbowEnabled, true); result = await binding.testExtension('repaintRainbow', <String, String>{}); expect(result, <String, String>{'enabled': 'true'}); expect(debugRepaintRainbowEnabled, true); expect(binding.frameScheduled, isFalse); pendingResult = binding.testExtension('repaintRainbow', <String, String>{'enabled': 'false'}); completed = false; pendingResult.whenComplete(() { completed = true; }); await binding.flushMicrotasks(); expect(completed, false); expect(binding.frameScheduled, isTrue); await binding.doFrame(); await binding.flushMicrotasks(); expect(completed, true); expect(binding.frameScheduled, isFalse); result = await pendingResult; expect(result, <String, String>{'enabled': 'false'}); expect(debugRepaintRainbowEnabled, false); result = await binding.testExtension('repaintRainbow', <String, String>{}); expect(result, <String, String>{'enabled': 'false'}); expect(debugRepaintRainbowEnabled, false); expect(binding.frameScheduled, isFalse); }); 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); }); test('Service extensions - reassemble', () async { Map<String, dynamic> result; Future<Map<String, dynamic>> pendingResult; 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); await binding.flushMicrotasks(); await binding.doFrame(); 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 { Map<String, dynamic> result; // The performance overlay service extension is disabled on the web. if (kIsWeb) { expect(binding.extensions.containsKey('showPerformanceOverlay'), isFalse); return; } expect(binding.frameScheduled, isFalse); expect(WidgetsApp.showPerformanceOverlayOverride, false); result = await binding.testExtension('showPerformanceOverlay', <String, String>{}); expect(result, <String, String>{'enabled': 'false'}); expect(WidgetsApp.showPerformanceOverlayOverride, false); result = await binding.testExtension('showPerformanceOverlay', <String, String>{'enabled': 'true'}); expect(result, <String, String>{'enabled': 'true'}); expect(WidgetsApp.showPerformanceOverlayOverride, true); result = await binding.testExtension('showPerformanceOverlay', <String, String>{}); expect(result, <String, String>{'enabled': 'true'}); expect(WidgetsApp.showPerformanceOverlayOverride, true); result = await binding.testExtension('showPerformanceOverlay', <String, String>{'enabled': 'false'}); expect(result, <String, String>{'enabled': 'false'}); expect(WidgetsApp.showPerformanceOverlayOverride, false); result = await binding.testExtension('showPerformanceOverlay', <String, String>{}); expect(result, <String, String>{'enabled': 'false'}); expect(WidgetsApp.showPerformanceOverlayOverride, false); expect(binding.frameScheduled, isFalse); }); test('Service extensions - debugWidgetInspector', () async { Map<String, dynamic> result; expect(binding.frameScheduled, isFalse); expect(WidgetsApp.debugShowWidgetInspectorOverride, false); result = await binding.testExtension('debugWidgetInspector', <String, String>{}); expect(result, <String, String>{'enabled': 'false'}); expect(WidgetsApp.debugShowWidgetInspectorOverride, false); result = await binding.testExtension('debugWidgetInspector', <String, String>{'enabled': 'true'}); expect(result, <String, String>{'enabled': 'true'}); expect(WidgetsApp.debugShowWidgetInspectorOverride, true); result = await binding.testExtension('debugWidgetInspector', <String, String>{}); expect(result, <String, String>{'enabled': 'true'}); expect(WidgetsApp.debugShowWidgetInspectorOverride, true); result = await binding.testExtension('debugWidgetInspector', <String, String>{'enabled': 'false'}); expect(result, <String, String>{'enabled': 'false'}); expect(WidgetsApp.debugShowWidgetInspectorOverride, false); result = await binding.testExtension('debugWidgetInspector', <String, String>{}); expect(result, <String, String>{'enabled': 'false'}); expect(WidgetsApp.debugShowWidgetInspectorOverride, false); expect(binding.frameScheduled, isFalse); }); test('Service extensions - timeDilation', () async { final Iterable<Map<String, dynamic>> extensionChangedEvents = binding.getServiceExtensionStateChangedEvents('ext.flutter.timeDilation'); Map<String, dynamic> extensionChangedEvent; Map<String, dynamic> result; expect(binding.frameScheduled, isFalse); expect(timeDilation, 1.0); result = await binding.testExtension('timeDilation', <String, String>{}); expect(result, <String, String>{'timeDilation': 1.0.toString()}); expect(timeDilation, 1.0); expect(extensionChangedEvents, isEmpty); result = await binding.testExtension('timeDilation', <String, String>{'timeDilation': '100.0'}); expect(result, <String, String>{'timeDilation': 100.0.toString()}); expect(timeDilation, 100.0); expect(extensionChangedEvents.length, 1); extensionChangedEvent = extensionChangedEvents.last; expect(extensionChangedEvent['extension'], 'ext.flutter.timeDilation'); expect(extensionChangedEvent['value'], 100.0.toString()); result = await binding.testExtension('timeDilation', <String, String>{}); expect(result, <String, String>{'timeDilation': 100.0.toString()}); expect(timeDilation, 100.0); expect(extensionChangedEvents.length, 1); result = await binding.testExtension('timeDilation', <String, String>{'timeDilation': '1.0'}); expect(result, <String, String>{'timeDilation': 1.0.toString()}); expect(timeDilation, 1.0); expect(extensionChangedEvents.length, 2); extensionChangedEvent = extensionChangedEvents.last; expect(extensionChangedEvent['extension'], 'ext.flutter.timeDilation'); expect(extensionChangedEvent['value'], 1.0.toString()); result = await binding.testExtension('timeDilation', <String, String>{}); expect(result, <String, String>{'timeDilation': 1.0.toString()}); expect(timeDilation, 1.0); expect(extensionChangedEvents.length, 2); expect(binding.frameScheduled, isFalse); }); test('Service extensions - brightnessOverride', () async { Map<String, dynamic> result; result = await binding.testExtension('brightnessOverride', <String, String>{}); final String brightnessValue = result['value'] as String; expect(brightnessValue, 'Brightness.light'); }); 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'); }); 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=/'); }); }