| 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
|
|
|