Unverified Commit a172de01 authored by Kenzie Schmoll's avatar Kenzie Schmoll Committed by GitHub

Send ServiceExtensionToggled event when service extension is set to a value. (#26426)

parent 7c434a5c
......@@ -129,8 +129,9 @@ abstract class BindingBase {
}
assert(() {
const String platformOverrideExtensionName = 'platformOverride';
registerServiceExtension(
name: 'platformOverride',
name: platformOverrideExtensionName,
callback: (Map<String, String> parameters) async {
if (parameters.containsKey('value')) {
switch (parameters['value']) {
......@@ -147,6 +148,10 @@ abstract class BindingBase {
default:
debugDefaultTargetPlatformOverride = null;
}
_postExtensionStateChangedEvent(
platformOverrideExtensionName,
defaultTargetPlatform.toString().substring('$TargetPlatform.'.length),
);
await reassembleApplication();
}
return <String, dynamic>{
......@@ -295,8 +300,10 @@ abstract class BindingBase {
registerServiceExtension(
name: name,
callback: (Map<String, String> parameters) async {
if (parameters.containsKey('enabled'))
if (parameters.containsKey('enabled')) {
await setter(parameters['enabled'] == 'true');
_postExtensionStateChangedEvent(name, await getter() ? 'true' : 'false');
}
return <String, dynamic>{ 'enabled': await getter() ? 'true' : 'false' };
}
);
......@@ -327,13 +334,44 @@ abstract class BindingBase {
registerServiceExtension(
name: name,
callback: (Map<String, String> parameters) async {
if (parameters.containsKey(name))
if (parameters.containsKey(name)) {
await setter(double.parse(parameters[name]));
_postExtensionStateChangedEvent(name, (await getter()).toString());
}
return <String, dynamic>{ name: (await getter()).toString() };
}
);
}
/// Sends an event when a service extension's state is changed.
///
/// Clients should listen for this event to stay aware of the current service
/// extension state. Any service extension that manages a state should call
/// this method on state change.
///
/// `value` reflects the newly updated service extension value.
///
/// This will be called automatically for service extensions registered via
/// [registerBoolServiceExtension], [registerNumericServiceExtension], or
/// [registerStringServiceExtension].
void _postExtensionStateChangedEvent(String name, dynamic value) {
postEvent(
'Flutter.ServiceExtensionStateChanged',
<String, dynamic>{
'extension': 'ext.flutter.$name',
'value': value,
},
);
}
/// All events dispatched by a [BindingBase] use this method instead of
/// calling [developer.postEvent] directly so that tests for [BindingBase]
/// can track which events were dispatched by overriding this method.
@protected
void postEvent(String eventKind, Map<String, dynamic> eventData) {
developer.postEvent(eventKind, eventData);
}
/// Registers a service extension method with the given name (full name
/// "ext.flutter.name"), which optionally takes a single argument with the
/// name "value". If the argument is omitted, the value is to be read,
......@@ -358,8 +396,10 @@ abstract class BindingBase {
registerServiceExtension(
name: name,
callback: (Map<String, String> parameters) async {
if (parameters.containsKey('value'))
if (parameters.containsKey('value')) {
await setter(parameters['value']);
_postExtensionStateChangedEvent(name, await getter());
}
return <String, dynamic>{ 'value': await getter() };
}
);
......
......@@ -868,13 +868,36 @@ mixin WidgetInspectorService {
registerServiceExtension(
name: name,
callback: (Map<String, String> parameters) async {
if (parameters.containsKey('enabled'))
await setter(parameters['enabled'] == 'true');
if (parameters.containsKey('enabled')) {
final bool value = parameters['enabled'] == 'true';
await setter(value);
_postExtensionStateChangedEvent(name, value);
}
return <String, dynamic>{ 'enabled': await getter() ? 'true' : 'false' };
},
);
}
/// Sends an event when a service extension's state is changed.
///
/// Clients should listen for this event to stay aware of the current service
/// extension state. Any service extension that manages a state should call
/// this method on state change.
///
/// `value` reflects the newly updated service extension value.
///
/// This will be called automatically for service extensions registered via
/// [registerBoolServiceExtension].
void _postExtensionStateChangedEvent(String name, dynamic value) {
postEvent(
'Flutter.ServiceExtensionStateChanged',
<String, dynamic>{
'extension': 'ext.flutter.inspector.$name',
'value': value,
},
);
}
/// Registers a service extension method with the given name (full
/// name "ext.flutter.inspector.name") which takes an optional parameter named
/// "arg" and a required parameter named "objectGroup" used to control the
......
......@@ -26,6 +26,8 @@ class TestServiceExtensionsBinding extends BindingBase
final Map<String, ServiceExtensionCallback> extensions = <String, ServiceExtensionCallback>{};
final Map<String, List<Map<String, dynamic>>> eventsDispatched = <String, List<Map<String, dynamic>>>{};
@override
void registerServiceExtension({
@required String name,
......@@ -35,6 +37,20 @@ class TestServiceExtensionsBinding extends BindingBase
extensions[name] = callback;
}
@override
void postEvent(String eventKind, Map<dynamic, dynamic> eventData) {
getEventsDispatched(eventKind).add(eventData);
}
List<Map<String, dynamic>> getEventsDispatched(String eventKind) {
return eventsDispatched.putIfAbsent(eventKind, () => <Map<String, dynamic>>[]);
}
Iterable<Map<String, dynamic>> getServiceExtensionStateChangedEvents(String extensionName) {
return getEventsDispatched('Flutter.ServiceExtensionStateChanged')
.where((Map<String, dynamic> event) => event['extension'] == extensionName);
}
Future<Map<String, dynamic>> testExtension(String name, Map<String, String> arguments) {
expect(extensions.containsKey(name), isTrue);
return extensions[name](arguments);
......@@ -228,6 +244,8 @@ void main() {
});
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;
......@@ -237,6 +255,7 @@ void main() {
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;
......@@ -251,9 +270,14 @@ void main() {
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();
......@@ -263,9 +287,14 @@ void main() {
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);
});
......@@ -373,6 +402,8 @@ void main() {
});
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);
......@@ -380,30 +411,55 @@ void main() {
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': 'android'}));
expect(result, <String, String>{'value': 'android'});
expect(binding.reassembled, 2);
expect(defaultTargetPlatform, TargetPlatform.android);
expect(extensionChangedEvents.length, 2);
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, 3);
expect(defaultTargetPlatform, TargetPlatform.fuchsia);
expect(extensionChangedEvents.length, 3);
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, 4);
expect(defaultTargetPlatform, TargetPlatform.android);
expect(extensionChangedEvents.length, 4);
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, 5);
expect(defaultTargetPlatform, TargetPlatform.iOS);
expect(extensionChangedEvents.length, 5);
extensionChangedEvent = extensionChangedEvents.last;
expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
expect(extensionChangedEvent['value'], 'iOS');
result = await hasReassemble(binding.testExtension('platformOverride', <String, String>{'value': 'bogus'}));
expect(result, <String, String>{'value': 'android'});
expect(binding.reassembled, 6);
expect(defaultTargetPlatform, TargetPlatform.android);
expect(extensionChangedEvents.length, 6);
extensionChangedEvent = extensionChangedEvents.last;
expect(extensionChangedEvent['extension'], 'ext.flutter.platformOverride');
expect(extensionChangedEvent['value'], 'android');
binding.reassembled = 0;
});
......@@ -518,6 +574,8 @@ void main() {
});
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);
......@@ -525,18 +583,29 @@ void main() {
result = await binding.testExtension('timeDilation', <String, String>{});
expect(result, <String, String>{ 'timeDilation': '1.0' });
expect(timeDilation, 1.0);
expect(extensionChangedEvents, isEmpty);
result = await binding.testExtension('timeDilation', <String, String>{ 'timeDilation': '100.0' });
expect(result, <String, String>{ 'timeDilation': '100.0' });
expect(timeDilation, 100.0);
expect(extensionChangedEvents.length, 1);
extensionChangedEvent = extensionChangedEvents.last;
expect(extensionChangedEvent['extension'], 'ext.flutter.timeDilation');
expect(extensionChangedEvent['value'], '100.0');
result = await binding.testExtension('timeDilation', <String, String>{});
expect(result, <String, String>{ 'timeDilation': '100.0' });
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' });
expect(timeDilation, 1.0);
expect(extensionChangedEvents.length, 2);
extensionChangedEvent = extensionChangedEvents.last;
expect(extensionChangedEvent['extension'], 'ext.flutter.timeDilation');
expect(extensionChangedEvent['value'], '1.0');
result = await binding.testExtension('timeDilation', <String, String>{});
expect(result, <String, String>{ 'timeDilation': '1.0' });
expect(timeDilation, 1.0);
expect(extensionChangedEvents.length, 2);
expect(binding.frameScheduled, isFalse);
});
......
......@@ -194,8 +194,7 @@ void main() {
class TestWidgetInspectorService extends Object with WidgetInspectorService {
final Map<String, InspectorServiceExtensionCallback> extensions = <String, InspectorServiceExtensionCallback>{};
final Map<String, List<Map<Object, Object>>> eventsDispatched =
<String, List<Map<Object, Object>>>{};
final Map<String, List<Map<Object, Object>>> eventsDispatched = <String, List<Map<Object, Object>>>{};
@override
void registerServiceExtension({
......@@ -215,6 +214,11 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService {
return eventsDispatched.putIfAbsent(eventKind, () => <Map<Object, Object>>[]);
}
Iterable<Map<Object, Object>> getServiceExtensionStateChangedEvents(String extensionName) {
return getEventsDispatched('Flutter.ServiceExtensionStateChanged')
.where((Map<Object, Object> event) => event['extension'] == extensionName);
}
Future<Object> testExtension(String name, Map<String, String> arguments) async {
expect(extensions.containsKey(name), isTrue);
// Encode and decode to JSON to match behavior using a real service
......@@ -1725,15 +1729,33 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService {
}, skip: !WidgetInspectorService.instance.isWidgetCreationTracked()); // Test requires --track-widget-creation flag.
testWidgets('ext.flutter.inspector.show', (WidgetTester tester) async {
final Iterable<Map<Object, Object>> extensionChangedEvents = service.getServiceExtensionStateChangedEvents('ext.flutter.inspector.show');
Map<Object, Object> extensionChangedEvent;
service.rebuildCount = 0;
expect(extensionChangedEvents, isEmpty);
expect(await service.testBoolExtension('show', <String, String>{'enabled': 'true'}), equals('true'));
expect(extensionChangedEvents.length, equals(1));
extensionChangedEvent = extensionChangedEvents.last;
expect(extensionChangedEvent['extension'], equals('ext.flutter.inspector.show'));
expect(extensionChangedEvent['value'], isTrue);
expect(service.rebuildCount, equals(1));
expect(await service.testBoolExtension('show', <String, String>{}), equals('true'));
expect(WidgetsApp.debugShowWidgetInspectorOverride, isTrue);
expect(extensionChangedEvents.length, equals(1));
expect(await service.testBoolExtension('show', <String, String>{'enabled': 'true'}), equals('true'));
expect(extensionChangedEvents.length, equals(2));
extensionChangedEvent = extensionChangedEvents.last;
expect(extensionChangedEvent['extension'], equals('ext.flutter.inspector.show'));
expect(extensionChangedEvent['value'], isTrue);
expect(service.rebuildCount, equals(1));
expect(await service.testBoolExtension('show', <String, String>{'enabled': 'false'}), equals('false'));
expect(extensionChangedEvents.length, equals(3));
extensionChangedEvent = extensionChangedEvents.last;
expect(extensionChangedEvent['extension'], equals('ext.flutter.inspector.show'));
expect(extensionChangedEvent['value'], isFalse);
expect(await service.testBoolExtension('show', <String, String>{}), equals('false'));
expect(extensionChangedEvents.length, equals(3));
expect(service.rebuildCount, equals(2));
expect(WidgetsApp.debugShowWidgetInspectorOverride, isFalse);
});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment