From bf05e4389e0580e79230a0c981bc58c88defa13c Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Mon, 8 Mar 2021 15:28:06 +0100 Subject: [PATCH 1/3] Send -1 for empty composition range https://github.com/flutter/flutter/issues/77600 --- .../macos/framework/Source/FlutterTextInputPlugin.mm | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm index 159d7c74e52f9..4161c6b2d137e 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm @@ -227,13 +227,17 @@ - (void)updateEditState { NSString* const textAffinity = (self.textAffinity == FlutterTextAffinityUpstream) ? kTextAffinityUpstream : kTextAffinityDownstream; + + int composingBase = _activeModel->composing() ? _activeModel->composing_range().base() : -1; + int composingExtent = _activeModel->composing() ? _activeModel->composing_range().extent() : -1; + NSDictionary* state = @{ kSelectionBaseKey : @(_activeModel->selection().base()), kSelectionExtentKey : @(_activeModel->selection().extent()), kSelectionAffinityKey : textAffinity, kSelectionIsDirectionalKey : @NO, - kComposingBaseKey : @(_activeModel->composing_range().base()), - kComposingExtentKey : @(_activeModel->composing_range().extent()), + kComposingBaseKey : @(composingBase), + kComposingExtentKey : @(composingExtent), kTextKey : [NSString stringWithUTF8String:_activeModel->GetText().c_str()] }; From e3f96ce11b3b95f21c754262427cb42f90205661 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Wed, 10 Mar 2021 20:27:12 +0100 Subject: [PATCH 2/3] Add test --- ci/licenses_golden/licenses_flutter | 1 + shell/platform/darwin/macos/BUILD.gn | 1 + .../framework/Source/FlutterTextInputPlugin.h | 5 + .../Source/FlutterTextInputPluginTest.mm | 93 +++++++++++++++++++ 4 files changed, 100 insertions(+) create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index e968f9fd6854f..5d0fcecc0e90b 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1148,6 +1148,7 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfa FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextureRegistrar.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextureRegistrar.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn index 404ebc57303d8..e8e2223ce0286 100644 --- a/shell/platform/darwin/macos/BUILD.gn +++ b/shell/platform/darwin/macos/BUILD.gn @@ -165,6 +165,7 @@ executable("flutter_desktop_darwin_unittests") { "framework/Source/FlutterMetalSurfaceManagerTest.mm", "framework/Source/FlutterOpenGLRendererTest.mm", "framework/Source/FlutterPlatformNodeDelegateMacTest.mm", + "framework/Source/FlutterTextInputPluginTest.mm", "framework/Source/FlutterViewControllerTest.mm", "framework/Source/FlutterViewControllerTestUtils.h", "framework/Source/FlutterViewControllerTestUtils.mm", diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h index 2f4a5436e74d7..f82be5568a283 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h @@ -25,3 +25,8 @@ - (instancetype)initWithViewController:(FlutterViewController*)viewController; @end + +// Private methods made visible for testing +@interface FlutterTextInputPlugin (TestMethods) +- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result; +@end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm new file mode 100644 index 0000000000000..3cfdba07d4ad0 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm @@ -0,0 +1,93 @@ +// Copyright 2013 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. + +#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" + +#import +#import "flutter/testing/testing.h" + +@interface FlutterInputPluginTestObjc : NSObject +- (bool)testEmptyCompositionRange; +@end + +@implementation FlutterInputPluginTestObjc + +- (bool)testEmptyCompositionRange { + id engineMock = OCMClassMock([FlutterEngine class]); + id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); + OCMStub( // NOLINT(google-objc-avoid-throwing-exception) + [engineMock binaryMessenger]) + .andReturn(binaryMessengerMock); + + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock + nibName:@"" + bundle:nil]; + + FlutterTextInputPlugin* plugin = + [[FlutterTextInputPlugin alloc] initWithViewController:viewController]; + + [plugin handleMethodCall:[FlutterMethodCall + methodCallWithMethodName:@"TextInput.setClient" + arguments:@[ + @(1), @{ + @"inputAction" : @"action", + @"inputType" : @{@"name" : @"inputName"}, + } + ]] + result:^(id){ + }]; + + FlutterMethodCall* call = [FlutterMethodCall methodCallWithMethodName:@"TextInput.setEditingState" + arguments:@{ + @"text" : @"Text", + @"selectionBase" : @(0), + @"selectionExtent" : @(0), + @"composingBase" : @(-1), + @"composingExtent" : @(-1), + }]; + + NSDictionary* expectedState = @{ + @"selectionBase" : @(0), + @"selectionExtent" : @(0), + @"selectionAffinity" : @"TextAffinity.upstream", + @"selectionIsDirectional" : @(NO), + @"composingBase" : @(-1), + @"composingExtent" : @(-1), + @"text" : @"Text", + }; + + NSData* updateCall = [[FlutterJSONMethodCodec sharedInstance] + encodeMethodCall:[FlutterMethodCall + methodCallWithMethodName:@"TextInputClient.updateEditingState" + arguments:@[ @(1), expectedState ]]]; + + OCMExpect( // NOLINT(google-objc-avoid-throwing-exception) + [binaryMessengerMock sendOnChannel:@"flutter/textinput" message:updateCall]); + + [plugin handleMethodCall:call + result:^(id){ + }]; + + @try { + OCMVerify( // NOLINT(google-objc-avoid-throwing-exception) + [binaryMessengerMock sendOnChannel:@"flutter/textinput" message:updateCall]); + + } @catch (...) { + return false; + } + return true; +} + +@end + +namespace flutter::testing { + +TEST(FlutterTextInputPluginTest, TestEmptyCompositionRange) { + ASSERT_TRUE([[FlutterInputPluginTestObjc alloc] testEmptyCompositionRange]); +} + +} // namespace flutter::testing From 4f0b1ed940426eb8478dd74994071696657e18d9 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Thu, 11 Mar 2021 19:52:46 +0100 Subject: [PATCH 3/3] remove empty line --- .../darwin/macos/framework/Source/FlutterTextInputPluginTest.mm | 1 - 1 file changed, 1 deletion(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm index 3cfdba07d4ad0..5712b1615b60f 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm @@ -75,7 +75,6 @@ - (bool)testEmptyCompositionRange { @try { OCMVerify( // NOLINT(google-objc-avoid-throwing-exception) [binaryMessengerMock sendOnChannel:@"flutter/textinput" message:updateCall]); - } @catch (...) { return false; }