Index: ui/base/ime/android/cursor_anchor_info_controller_unittest.cc |
diff --git a/ui/base/ime/android/cursor_anchor_info_controller_unittest.cc b/ui/base/ime/android/cursor_anchor_info_controller_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8a7477dfefbfef509202cff03ff29222143682b9 |
--- /dev/null |
+++ b/ui/base/ime/android/cursor_anchor_info_controller_unittest.cc |
@@ -0,0 +1,927 @@ |
+// Copyright (c) 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "ui/base/ime/android/cursor_anchor_info_controller.h" |
+ |
+#include "base/strings/utf_string_conversions.h" |
+#include "base/tuple.h" |
+#include "cc/output/compositor_frame_metadata.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "ui/base/ime/android/cursor_anchor_info_builder.h" |
+#include "ui/base/ime/android/cursor_anchor_info_sender.h" |
+ |
+namespace ui { |
+namespace { |
+ |
+using ::testing::AssertionFailure; |
+using ::testing::AssertionResult; |
+using ::testing::AssertionSuccess; |
+using ::testing::FloatLE; |
+ |
+const uint32 kFlagHasVisibleRegion = |
+ CursorAnchorInfoBuilder::kFlagHasVisibleRegion; |
+const uint32 kFlagHasInvisibleRegion = |
+ CursorAnchorInfoBuilder::kFlagHasInvisibleRegion; |
+ |
+const uint32 kCursorUpdateModeImmediate = |
+ CursorAnchorInfoController::kCursorUpdateModeImmediate; |
+const uint32 kCursorUpdateModeMonitor = |
+ CursorAnchorInfoController::kCursorUpdateModeMonitor; |
+ |
+template <typename T> |
+std::vector<T> Vec() { |
+ return std::vector<T>(); |
+} |
+ |
+template <typename T> |
+std::vector<T> Vec(const T& param1) { |
+ std::vector<T> result; |
+ result.push_back(param1); |
+ return result; |
+} |
+ |
+template <typename T> |
+std::vector<T> Vec(const T& param1, const T& param2) { |
+ std::vector<T> result; |
+ result.push_back(param1); |
+ result.push_back(param2); |
+ return result; |
+} |
+ |
+template <typename T> |
+std::vector<T> Vec(const T& param1, const T& param2, const T& param3) { |
+ std::vector<T> result; |
+ result.push_back(param1); |
+ result.push_back(param2); |
+ result.push_back(param3); |
+ return result; |
+} |
+ |
+typedef Tuple<int, base::string16> SetComposingTextParams; |
+typedef Tuple<int, gfx::RectF, uint32> AddCharacterBoundsParams; |
+typedef Tuple<float, float, float, float, uint32> |
+SetInsertionMarkerLocationParams; |
+typedef Tuple<float, gfx::Vector2dF> SetScaleAndTranslateParams; |
+typedef Tuple<gfx::Range> SetSelectionRangeParams; |
+ |
+class CursorAnchorInfoBuilderMock final |
+ : public CursorAnchorInfoBuilder { |
+ public: |
+ |
+ CursorAnchorInfoBuilderMock() |
+ : build_count_(0), |
+ reset_count_(0) { |
+ } |
+ |
+ ~CursorAnchorInfoBuilderMock() override { |
+ } |
+ |
+ void AddCharacterBounds(int index, |
+ const gfx::RectF& rect, |
+ uint32 flags) override { |
+ add_character_bounds_call_log_.push_back(MakeTuple(index, rect, flags)); |
+ } |
+ |
+ base::android::ScopedJavaLocalRef<jobject> Build() override { |
+ ++build_count_; |
+ return base::android::ScopedJavaLocalRef<jobject>(); |
+ } |
+ |
+ void Reset() override { |
+ ++reset_count_; |
+ } |
+ |
+ void SetComposingText(int composing_text_start, |
+ base::StringPiece16 composing_text) override { |
+ set_composing_text_call_log_.push_back( |
+ MakeTuple(composing_text_start, |
+ composing_text.as_string())); |
+ } |
+ |
+ void SetInsertionMarkerLocation(float horizontal_position, |
+ float line_top, |
+ float line_baseline, |
+ float line_bottom, |
+ uint32 flags) override { |
+ set_insertion_marker_location_call_log_.push_back(MakeTuple( |
+ horizontal_position, line_top, line_baseline, line_bottom, flags)); |
+ } |
+ |
+ void SetScaleAndTranslate(float scale, |
+ const gfx::Vector2dF& translate) override { |
+ set_scale_and_translate_call_log_.push_back(MakeTuple( |
+ scale, translate)); |
+ } |
+ |
+ void SetSelectionRange(const gfx::Range& range) override { |
+ set_selection_range_call_log_.push_back(MakeTuple(range)); |
+ } |
+ |
+ size_t build_count() const { |
+ return build_count_; |
+ } |
+ |
+ size_t reset_count() const { |
+ return reset_count_; |
+ } |
+ |
+ const std::vector<SetComposingTextParams>& |
+ set_composing_text_call_log() const { |
+ return set_composing_text_call_log_; |
+ } |
+ |
+ const std::vector<SetInsertionMarkerLocationParams>& |
+ set_insertion_marker_location_call_log() const { |
+ return set_insertion_marker_location_call_log_; |
+ } |
+ |
+ const std::vector<SetScaleAndTranslateParams>& |
+ set_scale_and_translate_call_log() const { |
+ return set_scale_and_translate_call_log_; |
+ } |
+ |
+ const std::vector<AddCharacterBoundsParams>& |
+ add_character_bounds_call_log() const { |
+ return add_character_bounds_call_log_; |
+ } |
+ |
+ const std::vector<SetSelectionRangeParams>& |
+ set_selection_range_call_log() const { |
+ return set_selection_range_call_log_; |
+ } |
+ |
+ private: |
+ size_t build_count_; |
+ size_t reset_count_; |
+ std::vector<SetComposingTextParams> set_composing_text_call_log_; |
+ std::vector<SetInsertionMarkerLocationParams> |
+ set_insertion_marker_location_call_log_; |
+ std::vector<SetScaleAndTranslateParams> set_scale_and_translate_call_log_; |
+ std::vector<AddCharacterBoundsParams> add_character_bounds_call_log_; |
+ std::vector<SetSelectionRangeParams> set_selection_range_call_log_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(CursorAnchorInfoBuilderMock); |
+}; |
+ |
+class CursorAnchorInfoSenderMock final |
+ : public CursorAnchorInfoSender { |
+ public: |
+ |
+ CursorAnchorInfoSenderMock() |
+ : send_count_(0) { |
+ } |
+ |
+ ~CursorAnchorInfoSenderMock() override { |
+ } |
+ |
+ void SendCursorAnchorInfo(ui::CursorAnchorInfoBuilder* builder) override { |
+ ++send_count_; |
+ } |
+ |
+ size_t send_count() const { |
+ return send_count_; |
+ } |
+ |
+ private: |
+ size_t send_count_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(CursorAnchorInfoSenderMock); |
+}; |
+ |
+enum UpdateControllerType { |
+ kCallOnTextInputStateChanged = 1 << 0, |
+ kCallOnCompositionRangeChanged = 1 << 1, |
+ kCallOnFrameMetadataUpdated = 1 << 2, |
+}; |
+ |
+cc::ViewportSelectionBound CreateViewportSelectionBoundForTest( |
+ cc::SelectionBoundType type, |
+ const gfx::PointF& edge_top, |
+ const gfx::PointF& edge_bottom, |
+ bool visible) { |
+ cc::ViewportSelectionBound selection_bound; |
+ selection_bound.type = type; |
+ selection_bound.edge_top = edge_top; |
+ selection_bound.edge_bottom = edge_bottom; |
+ selection_bound.visible = visible; |
+ return selection_bound; |
+} |
+ |
+cc::CompositorFrameMetadata CreateFrameMetadataForTest( |
+ float device_scale_factor, |
+ const gfx::Vector2dF& root_scroll_offset, |
+ float page_scale_factor, |
+ const gfx::Vector2dF& location_bar_offset, |
+ const gfx::Vector2dF& location_bar_content_translation, |
+ const cc::ViewportSelectionBound& selection_start) { |
+ cc::CompositorFrameMetadata frame_metadata; |
+ frame_metadata.device_scale_factor = device_scale_factor; |
+ frame_metadata.root_scroll_offset = root_scroll_offset; |
+ frame_metadata.page_scale_factor = page_scale_factor; |
+ frame_metadata.location_bar_offset = location_bar_offset; |
+ frame_metadata.location_bar_content_translation = |
+ location_bar_content_translation; |
+ frame_metadata.selection_start = selection_start; |
+ return frame_metadata; |
+} |
+ |
+void UpdateController(CursorAnchorInfoController* controller, |
+ int update_type, |
+ int salt) { |
+ if ((update_type & kCallOnTextInputStateChanged) != 0) { |
+ const base::string16 kDummyText = base::ASCIIToUTF16("0123456789"); |
+ const gfx::Range kDummySelectionRange(0, salt); |
+ const gfx::Range kDummyCompositionRange(3, 5); |
+ controller->OnTextInputStateChanged(kDummyText, |
+ kDummySelectionRange, |
+ kDummyCompositionRange); |
+ } |
+ |
+ if ((update_type & kCallOnCompositionRangeChanged) != 0) { |
+ const gfx::Range kDummyRange(0, salt); |
+ std::vector<gfx::Rect> dummyCompositionRects; |
+ dummyCompositionRects.push_back(gfx::Rect(10, 3, 5, 8)); |
+ dummyCompositionRects.push_back(gfx::Rect(15, 3, 5, 8)); |
+ controller->OnCompositionRangeChanged(kDummyRange, dummyCompositionRects); |
+ } |
+ |
+ if ((update_type & kCallOnFrameMetadataUpdated) != 0) { |
+ const auto selection_start = CreateViewportSelectionBoundForTest( |
+ cc::SELECTION_BOUND_CENTER, |
+ gfx::PointF(240.0f, 41.0f), |
+ gfx::PointF(240.0f, 64.0f), |
+ true); |
+ const cc::CompositorFrameMetadata dummy_frame_metadata = |
+ CreateFrameMetadataForTest( |
+ 2.0f, |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ 1.0f, |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ selection_start); |
+ const gfx::Vector2d kDummyViewOriginOffset(0, salt); |
+ controller->OnFrameMetadataUpdated(dummy_frame_metadata, |
+ kDummyViewOriginOffset); |
+ } |
+} |
+ |
+bool FloatAlmostEqual(float a, float b) { |
+ return FloatLE("a", "b", a, b) && FloatLE("b", "a", b, a); |
+} |
+ |
+AssertionResult AssertSetInsertionMarkerLocation( |
+ const std::vector<SetInsertionMarkerLocationParams>& expected_call_log, |
+ const cc::ViewportSelectionBound& selection_start) { |
+ |
+ CursorAnchorInfoSenderMock sender_mock; |
+ // This object will be owned by |controller|. |
+ CursorAnchorInfoBuilderMock* builder_mock = new CursorAnchorInfoBuilderMock; |
+ auto controller = CursorAnchorInfoController::CreateForTest( |
+ &sender_mock, |
+ scoped_ptr<CursorAnchorInfoBuilder>(builder_mock)); |
+ // Mark as editable. |
+ controller->OnFocusedNodeChanged(true); |
+ |
+ // Enable monitor. |
+ controller->OnRequestCursorUpdates(kCursorUpdateModeMonitor); |
+ |
+ controller->OnFrameMetadataUpdated( |
+ CreateFrameMetadataForTest( |
+ 1.0f, |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ 1.0f, |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ selection_start), |
+ gfx::Vector2d(0, 0)); |
+ |
+ const auto& actual_call_log = |
+ builder_mock->set_insertion_marker_location_call_log(); |
+ if (expected_call_log.size() != actual_call_log.size()) { |
+ return AssertionFailure() |
+ << "Call count of SetInsertionMarkerLocation is unexpected." |
+ << "\nExpected call count: " << expected_call_log.size() |
+ << "\n Actual call count: " << actual_call_log.size(); |
+ } |
+ |
+ for (size_t i = 0; i < expected_call_log.size(); ++i) { |
+ const auto& expected = expected_call_log[i]; |
+ const auto& actual = actual_call_log[i]; |
+ if (!FloatAlmostEqual(get<0>(expected), get<0>(actual))) { |
+ return AssertionFailure() |
+ << "SetInsertionMarkerLocation is called with an unexpected " |
+ << "parameter." |
+ << "\n parameter: horizontal_position" |
+ << "\n expected: " << get<0>(expected) |
+ << "\n actual: " << get<0>(actual); |
+ } |
+ if (!FloatAlmostEqual(get<1>(expected), get<1>(actual))) { |
+ return AssertionFailure() |
+ << "SetInsertionMarkerLocation is called with an unexpected " |
+ << "parameter." |
+ << "\n parameter: line_top" |
+ << "\n expected: " << get<1>(expected) |
+ << "\n actual: " << get<1>(actual); |
+ } |
+ if (!FloatAlmostEqual(get<2>(expected), get<2>(actual))) { |
+ return AssertionFailure() |
+ << "SetInsertionMarkerLocation is called with an unexpected " |
+ << "parameter." |
+ << "\n parameter: line_baseline" |
+ << "\n expected: " << get<2>(expected) |
+ << "\n actual: " << get<2>(actual); |
+ } |
+ if (!FloatAlmostEqual(get<3>(expected), get<3>(actual))) { |
+ return AssertionFailure() |
+ << "SetInsertionMarkerLocation is called with an unexpected parameter." |
+ << "\n parameter: line_bottom" |
+ << "\n expected: " << get<3>(expected) |
+ << "\n actual: " << get<3>(actual); |
+ } |
+ if (get<4>(expected) != get<4>(actual)) { |
+ return AssertionFailure() |
+ << "SetInsertionMarkerLocation is called with an unexpected " |
+ << "parameter." |
+ << "\n parameter: flags" |
+ << "\n expected: " << get<4>(expected) |
+ << "\n actual: " << get<4>(actual); |
+ } |
+ } |
+ return AssertionSuccess(); |
+} |
+ |
+AssertionResult AssertSetScaleAndTranslate( |
+ float expected_scale, |
+ const gfx::Vector2dF& expected_translate, |
+ const cc::CompositorFrameMetadata& frame_metadata, |
+ const gfx::Vector2d& view_origin_offset) { |
+ CursorAnchorInfoSenderMock sender_mock; |
+ // This object will be owned by |controller|. |
+ CursorAnchorInfoBuilderMock* builder_mock = new CursorAnchorInfoBuilderMock; |
+ auto controller = CursorAnchorInfoController::CreateForTest( |
+ &sender_mock, |
+ scoped_ptr<CursorAnchorInfoBuilder>(builder_mock)); |
+ |
+ controller->OnFocusedNodeChanged(true); |
+ controller->OnRequestCursorUpdates(kCursorUpdateModeMonitor); |
+ |
+ controller->OnFrameMetadataUpdated(frame_metadata, view_origin_offset); |
+ |
+ const auto& log = builder_mock->set_scale_and_translate_call_log(); |
+ if (log.size() != 1) { |
+ return AssertionFailure() |
+ << "SetScaleAndTranslate is not called just once. " |
+ << "Actual call count: " << log.size(); |
+ } |
+ |
+ const float actual_scale = get<0>(log[0]); |
+ if (!FloatAlmostEqual(expected_scale, actual_scale)) { |
+ return AssertionFailure() |
+ << "SetScaleAndTranslate is called with an unexpected parameter." |
+ << "\n parameter: scale" |
+ << "\n expected: " << expected_scale |
+ << "\n actual: " << get<0>(log[0]); |
+ } |
+ |
+ const auto& actual_translate = get<1>(log[0]); |
+ if (!FloatAlmostEqual(expected_translate.x(), actual_translate.x()) || |
+ !FloatAlmostEqual(expected_translate.y(), actual_translate.y())) { |
+ return AssertionFailure() |
+ << "SetScaleAndTranslate is called with an unexpected parameter." |
+ << "\n parameter: translate" |
+ << "\n expected: " << expected_translate.ToString() |
+ << "\n actual: " << actual_translate.ToString(); |
+ } |
+ |
+ return AssertionSuccess(); |
+} |
+ |
+AssertionResult AssertOnTextInputStateChanged( |
+ const std::vector<SetSelectionRangeParams>& |
+ expected_set_selection_range_call_log, |
+ const std::vector<SetComposingTextParams>& |
+ expected_set_composing_text_call_log, |
+ const base::string16& actual_text, |
+ const gfx::Range& actual_selection_range, |
+ const gfx::Range& actual_composition_range) { |
+ CursorAnchorInfoSenderMock sender_mock; |
+ // This object will be owned by |controller|. |
+ CursorAnchorInfoBuilderMock* builder_mock = new CursorAnchorInfoBuilderMock; |
+ auto controller = CursorAnchorInfoController::CreateForTest( |
+ &sender_mock, |
+ scoped_ptr<CursorAnchorInfoBuilder>(builder_mock)); |
+ |
+ controller->OnFocusedNodeChanged(true); |
+ controller->OnRequestCursorUpdates(kCursorUpdateModeMonitor); |
+ |
+ controller->OnTextInputStateChanged(actual_text, |
+ actual_selection_range, |
+ actual_composition_range); |
+ // Calling OnFrameMetadataUpdated is mandatory to build an object. |
+ UpdateController(controller.get(), kCallOnFrameMetadataUpdated, 0); |
+ |
+ { |
+ const auto& expected_call_log = expected_set_selection_range_call_log; |
+ const auto& actual_call_log = builder_mock->set_selection_range_call_log(); |
+ if (expected_call_log.size() != actual_call_log.size()) { |
+ return AssertionFailure() |
+ << "Call count of SetSelectionRange is unexpected." |
+ << "\nExpected call count: " << expected_call_log.size() |
+ << "\n Actual call count: " << actual_call_log.size(); |
+ } |
+ |
+ for (size_t i = 0; i < expected_call_log.size(); ++i) { |
+ const auto& expected = expected_call_log[i]; |
+ const auto& actual = actual_call_log[i]; |
+ if (get<0>(expected) != get<0>(actual)) { |
+ return AssertionFailure() |
+ << "SetSelectionRange is called with an unexpected parameter." |
+ << "\n parameter: range" |
+ << "\n expected: " << get<0>(expected) |
+ << "\n actual: " << get<0>(actual); |
+ } |
+ } |
+ } |
+ |
+ { |
+ const auto& expected_call_log = expected_set_composing_text_call_log; |
+ const auto& actual_call_log = builder_mock->set_composing_text_call_log(); |
+ if (expected_call_log.size() != actual_call_log.size()) { |
+ return AssertionFailure() |
+ << "Call count of SetComposingText is unexpected." |
+ << "\nExpected call count: " << expected_call_log.size() |
+ << "\n Actual call count: " << actual_call_log.size(); |
+ } |
+ |
+ for (size_t i = 0; i < expected_call_log.size(); ++i) { |
+ const auto& expected = expected_call_log[i]; |
+ const auto& actual = actual_call_log[i]; |
+ if (get<0>(expected) != get<0>(actual)) { |
+ return AssertionFailure() |
+ << "SetComposingText is called with an unexpected parameter." |
+ << "\n parameter: range" |
+ << "\n expected: " << get<0>(expected) |
+ << "\n actual: " << get<0>(actual); |
+ } |
+ if (get<1>(expected) != get<1>(actual)) { |
+ return AssertionFailure() |
+ << "SetComposingText is called with an unexpected parameter." |
+ << "\n parameter: range" |
+ << "\n expected: " << get<1>(expected) |
+ << "\n actual: " << get<1>(actual); |
+ } |
+ } |
+ } |
+ |
+ return AssertionSuccess(); |
+} |
+ |
+AssertionResult AssertOnCompositionRangeChanged( |
+ const std::vector<AddCharacterBoundsParams>& expected_call_log, |
+ const gfx::Range& actual_range, |
+ const std::vector<gfx::Rect>& actual_character_bounds) { |
+ |
+ CursorAnchorInfoSenderMock sender_mock; |
+ // This object will be owned by |controller|. |
+ CursorAnchorInfoBuilderMock* builder_mock = new CursorAnchorInfoBuilderMock; |
+ auto controller = CursorAnchorInfoController::CreateForTest( |
+ &sender_mock, |
+ scoped_ptr<CursorAnchorInfoBuilder>(builder_mock)); |
+ |
+ controller->OnFocusedNodeChanged(true); |
+ controller->OnRequestCursorUpdates(kCursorUpdateModeMonitor); |
+ |
+ controller->OnCompositionRangeChanged(actual_range, actual_character_bounds); |
+ |
+ // Calling OnFrameMetadataUpdated is mandatory to build an object. |
+ UpdateController(controller.get(), kCallOnFrameMetadataUpdated, 0); |
+ |
+ { |
+ const auto& actual_call_log = builder_mock->add_character_bounds_call_log(); |
+ if (expected_call_log.size() != actual_call_log.size()) { |
+ return AssertionFailure() |
+ << "Call count of AddCharacterBounds is unexpected." |
+ << "\nExpected call count: " << expected_call_log.size() |
+ << "\n Actual call count: " << actual_call_log.size(); |
+ } |
+ |
+ for (size_t i = 0; i < expected_call_log.size(); ++i) { |
+ const auto& expected = expected_call_log[i]; |
+ const auto& actual = actual_call_log[i]; |
+ if (get<0>(expected) != get<0>(actual)) { |
+ return AssertionFailure() |
+ << "AddCharacterBounds is called with an unexpected parameter." |
+ << "\n parameter: index" |
+ << "\n expected: " << get<0>(expected) |
+ << "\n actual: " << get<0>(actual); |
+ } |
+ if (get<1>(expected) != get<1>(actual)) { |
+ return AssertionFailure() |
+ << "AddCharacterBounds is called with an unexpected parameter." |
+ << "\n parameter: rect" |
+ << "\n expected: " << get<1>(expected).ToString() |
+ << "\n actual: " << get<1>(actual).ToString(); |
+ } |
+ if (get<2>(expected) != get<2>(actual)) { |
+ return AssertionFailure() |
+ << "AddCharacterBounds is called with an unexpected parameter." |
+ << "\n parameter: flags" |
+ << "\n expected: " << get<2>(expected) |
+ << "\n actual: " << get<2>(actual); |
+ } |
+ } |
+ |
+ } |
+ |
+ return AssertionSuccess(); |
+} |
+ |
+///////////////////////////////////////////////////// |
+ |
+TEST(CursorAnchorInfoControllerTest, OnFocusedNodeChanged) { |
+ CursorAnchorInfoSenderMock sender_mock; |
+ // This object will be owned by |controller|. |
+ CursorAnchorInfoBuilderMock* builder_mock = new CursorAnchorInfoBuilderMock; |
+ auto controller = CursorAnchorInfoController::CreateForTest( |
+ &sender_mock, |
+ scoped_ptr<CursorAnchorInfoBuilder>(builder_mock)); |
+ |
+ // Check the initial state. |
+ ASSERT_EQ(0u, sender_mock.send_count()); |
+ |
+ ASSERT_TRUE(controller->OnRequestCursorUpdates(kCursorUpdateModeMonitor)); |
+ |
+ int salt = 0; |
+ ASSERT_EQ(0u, sender_mock.send_count()) |
+ << "Data must not be sent when the node is not editable."; |
+ UpdateController(controller.get(), kCallOnFrameMetadataUpdated, salt); |
+ ASSERT_EQ(0u, sender_mock.send_count()) |
+ << "Data must not be sent when the node is not editable."; |
+ |
+ controller->OnFocusedNodeChanged(true); |
+ |
+ UpdateController(controller.get(), kCallOnFrameMetadataUpdated, salt); |
+ ASSERT_EQ(1u, sender_mock.send_count()) |
+ << "Data can be sent when the node is editable."; |
+ |
+ controller->OnFocusedNodeChanged(false); |
+ ASSERT_TRUE(controller->OnRequestCursorUpdates(kCursorUpdateModeMonitor)); |
+ |
+ UpdateController(controller.get(), kCallOnFrameMetadataUpdated, salt); |
+ ASSERT_EQ(1u, sender_mock.send_count()) |
+ << "Data must not be sent when the node is not editable."; |
+ |
+ controller->OnFocusedNodeChanged(true); |
+ ASSERT_TRUE(controller->OnRequestCursorUpdates(kCursorUpdateModeMonitor)); |
+ |
+ UpdateController(controller.get(), kCallOnFrameMetadataUpdated, salt); |
+ ASSERT_EQ(2u, sender_mock.send_count()) << |
+ "Data must be sent even with the same data (derived from the same salt)" |
+ " because OnFocusedNodeChanged resets internal state."; |
+} |
+ |
+TEST(CursorAnchorInfoControllerTest, CursorUpdateModeImmediate) { |
+ CursorAnchorInfoSenderMock sender_mock; |
+ // This object will be owned by |controller|. |
+ CursorAnchorInfoBuilderMock* builder_mock = new CursorAnchorInfoBuilderMock; |
+ auto controller = CursorAnchorInfoController::CreateForTest( |
+ &sender_mock, |
+ scoped_ptr<CursorAnchorInfoBuilder>(builder_mock)); |
+ controller->OnFocusedNodeChanged(true); |
+ |
+ // Check the initial state. |
+ ASSERT_EQ(0u, sender_mock.send_count()); |
+ |
+ int salt = 0; |
+ |
+ ASSERT_TRUE(controller->OnRequestCursorUpdates(kCursorUpdateModeImmediate)); |
+ ASSERT_EQ(0u, sender_mock.send_count()) |
+ << "Data must not be sent except for |OnFrameMetadataUpdated|."; |
+ ++salt; |
+ UpdateController(controller.get(), kCallOnTextInputStateChanged, salt); |
+ ASSERT_EQ(0u, sender_mock.send_count()) |
+ << "Data must not be sent except for |OnFrameMetadataUpdated|."; |
+ ++salt; |
+ UpdateController(controller.get(), kCallOnCompositionRangeChanged, salt); |
+ ASSERT_EQ(0u, sender_mock.send_count()) |
+ << "Data must not be sent except for |OnFrameMetadataUpdated|."; |
+ ++salt; |
+ UpdateController(controller.get(), kCallOnFrameMetadataUpdated, salt); |
+ ASSERT_EQ(1u, sender_mock.send_count()) |
+ << "Data must be sent from |OnFrameMetadataUpdated| when requested."; |
+ |
+ ++salt; |
+ UpdateController(controller.get(), kCallOnFrameMetadataUpdated, salt); |
+ ASSERT_EQ(1u, sender_mock.send_count()) |
+ << "Don't need to send data twice in the immediate mode."; |
+} |
+ |
+TEST(CursorAnchorInfoControllerTest, CursorUpdateModeMonitor) { |
+ CursorAnchorInfoSenderMock sender_mock; |
+ // This object will be owned by |controller|. |
+ CursorAnchorInfoBuilderMock* builder_mock = new CursorAnchorInfoBuilderMock; |
+ auto controller = CursorAnchorInfoController::CreateForTest( |
+ &sender_mock, |
+ scoped_ptr<CursorAnchorInfoBuilder>(builder_mock)); |
+ controller->OnFocusedNodeChanged(true); |
+ |
+ // Check the initial state. |
+ ASSERT_EQ(0u, sender_mock.send_count()); |
+ |
+ int frame_metadata_salt = 0; |
+ int salt = 0; |
+ |
+ // Test monitor mode: |
+ ASSERT_TRUE(controller->OnRequestCursorUpdates(kCursorUpdateModeMonitor)); |
+ ASSERT_EQ(0u, sender_mock.send_count()); |
+ |
+ ++salt; |
+ UpdateController(controller.get(), kCallOnTextInputStateChanged, salt); |
+ ASSERT_EQ(0u, sender_mock.send_count()) |
+ << "Data must not be sent except for |OnFrameMetadataUpdated|."; |
+ ++salt; |
+ UpdateController(controller.get(), kCallOnCompositionRangeChanged, salt); |
+ ASSERT_EQ(0u, sender_mock.send_count()) |
+ << "Data must not be sent except for |OnFrameMetadataUpdated|."; |
+ ++salt; |
+ UpdateController(controller.get(), kCallOnFrameMetadataUpdated, salt); |
+ ASSERT_EQ(1u, sender_mock.send_count()) |
+ << "Data must be sent from |OnFrameMetadataUpdated| when requested."; |
+ frame_metadata_salt = salt; |
+ |
+ UpdateController(controller.get(), |
+ kCallOnFrameMetadataUpdated, |
+ frame_metadata_salt); |
+ ASSERT_EQ(1u, sender_mock.send_count()) |
+ << "Data must not be sent because no data is changed."; |
+ |
+ ++salt; |
+ UpdateController(controller.get(), kCallOnTextInputStateChanged, salt); |
+ ASSERT_EQ(1u, sender_mock.send_count()) |
+ << "Data must not be sent except for |OnFrameMetadataUpdated|."; |
+ UpdateController(controller.get(), |
+ kCallOnFrameMetadataUpdated, |
+ frame_metadata_salt); |
+ ASSERT_EQ(2u, sender_mock.send_count()) |
+ << "Data must be sent because |OnTextInputStateChanged| changed data."; |
+ UpdateController(controller.get(), kCallOnTextInputStateChanged, salt); |
+ ASSERT_EQ(2u, sender_mock.send_count()) |
+ << "Data must not be sent except for |OnFrameMetadataUpdated|."; |
+ UpdateController(controller.get(), |
+ kCallOnFrameMetadataUpdated, |
+ frame_metadata_salt); |
+ ASSERT_EQ(2u, sender_mock.send_count()) |
+ << "Data must not be sent because no data is changed."; |
+ |
+ ++salt; |
+ UpdateController(controller.get(), kCallOnCompositionRangeChanged, salt); |
+ ASSERT_EQ(2u, sender_mock.send_count()) |
+ << "Data must not be sent except for |OnFrameMetadataUpdated|."; |
+ UpdateController(controller.get(), |
+ kCallOnFrameMetadataUpdated, |
+ frame_metadata_salt); |
+ ASSERT_EQ(3u, sender_mock.send_count()) |
+ << "Data must be sent because |OnCompositionRangeChanged| changed data."; |
+ UpdateController(controller.get(), kCallOnCompositionRangeChanged, salt); |
+ UpdateController(controller.get(), |
+ kCallOnFrameMetadataUpdated, |
+ frame_metadata_salt); |
+ ASSERT_EQ(3u, sender_mock.send_count()) |
+ << "Data must not be sent because no data is changed."; |
+ |
+ // Test if we can turn off the monitor mode. |
+ ASSERT_TRUE(controller->OnRequestCursorUpdates(0)); |
+ |
+ ++salt; |
+ UpdateController(controller.get(), kCallOnFrameMetadataUpdated, salt); |
+ ASSERT_EQ(3u, sender_mock.send_count()) |
+ << "Data must not be sent because the monitor mode is turned off."; |
+ |
+ // Test if we can turn on the monitor mode and immediate mode at the same |
+ // time. |
+ ASSERT_TRUE(controller->OnRequestCursorUpdates( |
+ kCursorUpdateModeMonitor | kCursorUpdateModeImmediate)); |
+ ASSERT_EQ(4u, sender_mock.send_count()) |
+ << "Data must be sent because the immediate mode is requested."; |
+ |
+ ++salt; |
+ UpdateController(controller.get(), kCallOnFrameMetadataUpdated, salt); |
+ ASSERT_EQ(5u, sender_mock.send_count()) |
+ << "Data must be sent from |OnFrameMetadataUpdated| when requested."; |
+ |
+ |
+ // Test if |OnFocusedNodeChanged(true)| resets the monitor mode. |
+ controller->OnFocusedNodeChanged(true); |
+ |
+ ++salt; |
+ UpdateController(controller.get(), kCallOnFrameMetadataUpdated, salt); |
+ ASSERT_EQ(6u, sender_mock.send_count()) |
+ << "Data must be sent because |Reset| doesn't reset the monitor " |
+ << "mode"; |
+ |
+} |
+ |
+TEST(CursorAnchorInfoControllerTest, SetInsertionMarkerLocation) { |
+ // Case 1: The insertion marker is visible |
+ EXPECT_TRUE(AssertSetInsertionMarkerLocation( |
+ Vec(MakeTuple(3.0f, 4.0f, 14.0f, 14.0f, kFlagHasVisibleRegion)), |
+ CreateViewportSelectionBoundForTest( |
+ cc::SELECTION_BOUND_CENTER, |
+ gfx::PointF(3.0f, 4.0f), |
+ gfx::PointF(3.0f, 14.0f), |
+ true))); |
+ |
+ // Case 2: The insertion marker is invisible |
+ EXPECT_TRUE(AssertSetInsertionMarkerLocation( |
+ Vec(MakeTuple(3.0f, 4.0f, 14.0f, 14.0f, kFlagHasInvisibleRegion)), |
+ CreateViewportSelectionBoundForTest( |
+ cc::SELECTION_BOUND_CENTER, |
+ gfx::PointF(3.0f, 4.0f), |
+ gfx::PointF(3.0f, 14.0f), |
+ false))); |
+ |
+ // Case 3: No insertion marker |
+ EXPECT_TRUE(AssertSetInsertionMarkerLocation( |
+ Vec<SetInsertionMarkerLocationParams>(), |
+ CreateViewportSelectionBoundForTest( |
+ cc::SELECTION_BOUND_EMPTY, |
+ gfx::PointF(0.0f, 0.0f), |
+ gfx::PointF(0.0f, 0.0f), |
+ false))); |
+} |
+ |
+TEST(CursorAnchorInfoControllerTest, SetScaleAndTranslate) { |
+ const auto empty_selection_start = CreateViewportSelectionBoundForTest( |
+ cc::SELECTION_BOUND_EMPTY, |
+ gfx::PointF(0.0f, 0.0f), |
+ gfx::PointF(0.0f, 0.0f), |
+ false); |
+ |
+ // No transformation |
+ EXPECT_TRUE(AssertSetScaleAndTranslate( |
+ 1.0f, |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ CreateFrameMetadataForTest( |
+ 1.0f, |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ 1.0f, |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ empty_selection_start), |
+ gfx::Vector2d(0, 0))); |
+ |
+ // device_scale_factor == 2.0 |
+ EXPECT_TRUE(AssertSetScaleAndTranslate( |
+ 2.0f, |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ CreateFrameMetadataForTest( |
+ 2.0f, |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ 1.0f, |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ empty_selection_start), |
+ gfx::Vector2d(0, 0))); |
+ |
+ // device_scale_factor == 2.0f |
+ // view_origin_offset == (10, 141) |
+ EXPECT_TRUE(AssertSetScaleAndTranslate( |
+ 2.0f, |
+ gfx::Vector2dF(10.0f, 141.0f), |
+ CreateFrameMetadataForTest( |
+ 2.0f, |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ 1.0f, |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ empty_selection_start), |
+ gfx::Vector2d(10, 141))); |
+ |
+ // device_scale_factor == 2.0f |
+ // root_scroll_offset == (1.0f, 2.0f) |
+ // view_origin_offset == (10.0f, 141.0f) |
+ EXPECT_TRUE(AssertSetScaleAndTranslate( |
+ 2.0f, |
+ gfx::Vector2dF(10.0f - 2.0f, 141.0f - 4.0f), |
+ CreateFrameMetadataForTest( |
+ 2.0f, |
+ gfx::Vector2dF(1.0f, 2.0f), |
+ 1.0f, |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ empty_selection_start), |
+ gfx::Vector2d(10, 141))); |
+ |
+ // device_scale_factor == 2.0f |
+ // page_scale_factor == 3.0f |
+ // root_scroll_offset == (1.0f, 2.0f) |
+ // view_origin_offset == (10.0f, 141.0f) |
+ EXPECT_TRUE(AssertSetScaleAndTranslate( |
+ 6.0f, |
+ gfx::Vector2dF(10.0f - 6.0f, 141.0f - 12.0f), |
+ CreateFrameMetadataForTest( |
+ 2.0f, |
+ gfx::Vector2dF(1.0f, 2.0f), |
+ 3.0f, |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ empty_selection_start), |
+ gfx::Vector2d(10, 141))); |
+ |
+ // device_scale_factor == 2.0f |
+ // page_scale_factor == 3.0f |
+ // root_scroll_offset == (1.0f, 2.0f) |
+ // location_bar_offset == (20.0f, 30.0f) |
+ // view_origin_offset == (10.0f, 141.0f) |
+ EXPECT_TRUE(AssertSetScaleAndTranslate( |
+ 6.0f, |
+ gfx::Vector2dF(30.0f - 6.0f, 171.0f - 12.0f), |
+ CreateFrameMetadataForTest( |
+ 2.0f, |
+ gfx::Vector2dF(1.0f, 2.0f), |
+ 3.0f, |
+ gfx::Vector2dF(20.0f, 30.0f), |
+ gfx::Vector2dF(0.0f, 0.0f), |
+ empty_selection_start), |
+ gfx::Vector2d(10, 141))); |
+ |
+ // device_scale_factor == 2.0f |
+ // page_scale_factor == 3.0f |
+ // root_scroll_offset == (1.0f, 2.0f) |
+ // location_bar_offset == (10.0f, 20.0f) |
+ // location_bar_content_translation == (30.0f, 40.0f) |
+ // view_origin_offset == (10.0f, 141.0f) |
+ EXPECT_TRUE(AssertSetScaleAndTranslate( |
+ 6.0f, |
+ gfx::Vector2dF(70.0f - 6.0f, 221.0f - 12.0f), |
+ CreateFrameMetadataForTest( |
+ 2.0f, |
+ gfx::Vector2dF(1.0f, 2.0f), |
+ 3.0f, |
+ gfx::Vector2dF(20.0f, 30.0f), |
+ gfx::Vector2dF(40.0f, 50.0f), |
+ empty_selection_start), |
+ gfx::Vector2d(10, 141))); |
+} |
+ |
+TEST(CursorAnchorInfoControllerTest, OnTextInputStateChanged) { |
+ EXPECT_TRUE(AssertOnTextInputStateChanged( |
+ Vec(MakeTuple(gfx::Range(1, 9))), |
+ Vec(MakeTuple(3, base::ASCIIToUTF16("34"))), |
+ base::ASCIIToUTF16("0123456789"), |
+ gfx::Range(1, 9), |
+ gfx::Range(3, 5))); |
+ |
+ // Test if the composing range is out of range. |
+ EXPECT_TRUE(AssertOnTextInputStateChanged( |
+ Vec(MakeTuple(gfx::Range(1, 9))), |
+ Vec(MakeTuple(100, base::ASCIIToUTF16(""))), |
+ base::ASCIIToUTF16("0123456789"), |
+ gfx::Range(1, 9), |
+ gfx::Range(100, 0))); |
+ |
+ // Test if the composing range is out of range. |
+ EXPECT_TRUE(AssertOnTextInputStateChanged( |
+ Vec(MakeTuple(gfx::Range(1, 9))), |
+ Vec(MakeTuple(-1, base::ASCIIToUTF16(""))), |
+ base::ASCIIToUTF16(""), |
+ gfx::Range(1, 9), |
+ gfx::Range(-1, 1))); |
+} |
+ |
+TEST(CursorAnchorInfoControllerTest, OnCompositionRangeChanged) { |
+ EXPECT_TRUE(AssertOnCompositionRangeChanged( |
+ Vec<AddCharacterBoundsParams>(), |
+ gfx::Range(0, 0), |
+ Vec<gfx::Rect>())); |
+ |
+ EXPECT_TRUE(AssertOnCompositionRangeChanged( |
+ Vec(MakeTuple(1, |
+ gfx::RectF(1.0f, 2.0f, 3.0f, 4.0f), |
+ kFlagHasVisibleRegion), |
+ MakeTuple(2, |
+ gfx::RectF(5.0f, 6.0f, 7.0f, 8.0f), |
+ kFlagHasVisibleRegion)), |
+ gfx::Range(1, 3), |
+ Vec(gfx::Rect(1, 2, 3, 4), |
+ gfx::Rect(5, 6, 7, 8)))); |
+ |
+ EXPECT_TRUE(AssertOnCompositionRangeChanged( |
+ Vec(MakeTuple(5, |
+ gfx::RectF(1.0f, 2.0f, 3.0f, 4.0f), |
+ kFlagHasVisibleRegion), |
+ MakeTuple(6, |
+ gfx::RectF(5.0f, 6.0f, 7.0f, 8.0f), |
+ kFlagHasVisibleRegion), |
+ MakeTuple(7, |
+ gfx::RectF(9.0f, 10.0f, 11.0f, 12.0f), |
+ kFlagHasVisibleRegion)), |
+ gfx::Range(5, 8), |
+ Vec(gfx::Rect(1, 2, 3, 4), |
+ gfx::Rect(5, 6, 7, 8), |
+ gfx::Rect(9, 10, 11, 12)))); |
+} |
+ |
+} // namespace |
+} // namespace ui |