| // 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. |
| |
| // Included first as it collides with the X11 headers. |
| #include "gtest/gtest.h" |
| |
| #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" |
| #include "flutter/shell/platform/linux/fl_accessible_text_field.h" |
| #include "flutter/shell/platform/linux/fl_engine_private.h" |
| #include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h" |
| #include "flutter/shell/platform/linux/testing/fl_test.h" |
| #include "flutter/shell/platform/linux/testing/mock_signal_handler.h" |
| |
| // MOCK_ENGINE_PROC is leaky by design |
| // NOLINTBEGIN(clang-analyzer-core.StackAddressEscape) |
| |
| static FlValue* decode_semantic_data(const uint8_t* data, size_t data_length) { |
| g_autoptr(GBytes) bytes = g_bytes_new(data, data_length); |
| g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new(); |
| return fl_message_codec_decode_message(FL_MESSAGE_CODEC(codec), bytes, |
| nullptr); |
| } |
| |
| // Tests that semantic node value updates from Flutter emit AtkText::text-insert |
| // and AtkText::text-remove signals as expected. |
| TEST(FlAccessibleTextFieldTest, SetValue) { |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1); |
| |
| // "" -> "Flutter" |
| { |
| flutter::testing::MockSignalHandler2<int, int> text_inserted(node, |
| "text-insert"); |
| flutter::testing::MockSignalHandler text_removed(node, "text-remove"); |
| |
| EXPECT_SIGNAL2(text_inserted, ::testing::Eq(0), ::testing::Eq(7)); |
| EXPECT_SIGNAL(text_removed).Times(0); |
| |
| fl_accessible_node_set_value(node, "Flutter"); |
| } |
| |
| // "Flutter" -> "Flutter" |
| { |
| flutter::testing::MockSignalHandler text_inserted(node, "text-insert"); |
| flutter::testing::MockSignalHandler text_removed(node, "text-remove"); |
| |
| EXPECT_SIGNAL(text_inserted).Times(0); |
| EXPECT_SIGNAL(text_removed).Times(0); |
| |
| fl_accessible_node_set_value(node, "Flutter"); |
| } |
| |
| // "Flutter" -> "engine" |
| { |
| flutter::testing::MockSignalHandler2<int, int> text_inserted(node, |
| "text-insert"); |
| flutter::testing::MockSignalHandler2<int, int> text_removed(node, |
| "text-remove"); |
| |
| EXPECT_SIGNAL2(text_inserted, ::testing::Eq(0), ::testing::Eq(6)); |
| EXPECT_SIGNAL2(text_removed, ::testing::Eq(0), ::testing::Eq(7)); |
| |
| fl_accessible_node_set_value(node, "engine"); |
| } |
| |
| // "engine" -> "" |
| { |
| flutter::testing::MockSignalHandler text_inserted(node, "text-insert"); |
| flutter::testing::MockSignalHandler2<int, int> text_removed(node, |
| "text-remove"); |
| |
| EXPECT_SIGNAL(text_inserted).Times(0); |
| EXPECT_SIGNAL2(text_removed, ::testing::Eq(0), ::testing::Eq(6)); |
| |
| fl_accessible_node_set_value(node, ""); |
| } |
| } |
| |
| // Tests that semantic node selection updates from Flutter emit |
| // AtkText::text-selection-changed and AtkText::text-caret-moved signals as |
| // expected. |
| TEST(FlAccessibleTextFieldTest, SetTextSelection) { |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1); |
| |
| // [-1,-1] -> [2,3] |
| { |
| flutter::testing::MockSignalHandler text_selection_changed( |
| node, "text-selection-changed"); |
| flutter::testing::MockSignalHandler1<int> text_caret_moved( |
| node, "text-caret-moved"); |
| |
| EXPECT_SIGNAL(text_selection_changed); |
| EXPECT_SIGNAL1(text_caret_moved, ::testing::Eq(3)); |
| |
| fl_accessible_node_set_text_selection(node, 2, 3); |
| } |
| |
| // [2,3] -> [3,3] |
| { |
| flutter::testing::MockSignalHandler text_selection_changed( |
| node, "text-selection-changed"); |
| flutter::testing::MockSignalHandler text_caret_moved(node, |
| "text-caret-moved"); |
| |
| EXPECT_SIGNAL(text_selection_changed); |
| EXPECT_SIGNAL(text_caret_moved).Times(0); |
| |
| fl_accessible_node_set_text_selection(node, 3, 3); |
| } |
| |
| // [3,3] -> [3,3] |
| { |
| flutter::testing::MockSignalHandler text_selection_changed( |
| node, "text-selection-changed"); |
| flutter::testing::MockSignalHandler text_caret_moved(node, |
| "text-caret-moved"); |
| |
| EXPECT_SIGNAL(text_selection_changed).Times(0); |
| EXPECT_SIGNAL(text_caret_moved).Times(0); |
| |
| fl_accessible_node_set_text_selection(node, 3, 3); |
| } |
| |
| // [3,3] -> [4,4] |
| { |
| flutter::testing::MockSignalHandler text_selection_changed( |
| node, "text-selection-changed"); |
| flutter::testing::MockSignalHandler1<int> text_caret_moved( |
| node, "text-caret-moved"); |
| |
| EXPECT_SIGNAL(text_selection_changed).Times(0); |
| EXPECT_SIGNAL1(text_caret_moved, ::testing::Eq(4)); |
| |
| fl_accessible_node_set_text_selection(node, 4, 4); |
| } |
| } |
| |
| // Tests that fl_accessible_text_field_perform_action() passes the required |
| // "expandSelection" argument for semantic cursor move actions. |
| TEST(FlAccessibleTextFieldTest, PerformAction) { |
| g_autoptr(GPtrArray) action_datas = g_ptr_array_new_with_free_func( |
| reinterpret_cast<GDestroyNotify>(fl_value_unref)); |
| |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| fl_engine_get_embedder_api(engine)->DispatchSemanticsAction = |
| MOCK_ENGINE_PROC( |
| DispatchSemanticsAction, |
| ([&action_datas](auto engine, uint64_t id, |
| FlutterSemanticsAction action, const uint8_t* data, |
| size_t data_length) { |
| g_ptr_array_add(action_datas, |
| decode_semantic_data(data, data_length)); |
| return kSuccess; |
| })); |
| |
| g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1); |
| fl_accessible_node_set_actions( |
| node, static_cast<FlutterSemanticsAction>( |
| kFlutterSemanticsActionMoveCursorForwardByCharacter | |
| kFlutterSemanticsActionMoveCursorBackwardByCharacter | |
| kFlutterSemanticsActionMoveCursorForwardByWord | |
| kFlutterSemanticsActionMoveCursorBackwardByWord)); |
| |
| g_autoptr(FlValue) expand_selection = fl_value_new_bool(false); |
| |
| for (int i = 0; i < 4; ++i) { |
| atk_action_do_action(ATK_ACTION(node), i); |
| |
| FlValue* data = static_cast<FlValue*>(g_ptr_array_index(action_datas, i)); |
| EXPECT_NE(data, nullptr); |
| EXPECT_TRUE(fl_value_equal(data, expand_selection)); |
| } |
| } |
| |
| // Tests AtkText::get_character_count. |
| TEST(FlAccessibleTextFieldTest, GetCharacterCount) { |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1); |
| |
| EXPECT_EQ(atk_text_get_character_count(ATK_TEXT(node)), 0); |
| |
| fl_accessible_node_set_value(node, "Flutter!"); |
| |
| EXPECT_EQ(atk_text_get_character_count(ATK_TEXT(node)), 8); |
| } |
| |
| // Tests AtkText::get_text. |
| TEST(FlAccessibleTextFieldTest, GetText) { |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1); |
| |
| g_autofree gchar* empty = atk_text_get_text(ATK_TEXT(node), 0, -1); |
| EXPECT_STREQ(empty, ""); |
| |
| flutter::testing::MockSignalHandler text_inserted(node, "text-insert"); |
| EXPECT_SIGNAL(text_inserted).Times(1); |
| |
| fl_accessible_node_set_value(node, "Flutter!"); |
| |
| g_autofree gchar* flutter = atk_text_get_text(ATK_TEXT(node), 0, -1); |
| EXPECT_STREQ(flutter, "Flutter!"); |
| |
| g_autofree gchar* tt = atk_text_get_text(ATK_TEXT(node), 3, 5); |
| EXPECT_STREQ(tt, "tt"); |
| } |
| |
| // Tests AtkText::get_caret_offset. |
| TEST(FlAccessibleTextFieldTest, GetCaretOffset) { |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1); |
| |
| EXPECT_EQ(atk_text_get_caret_offset(ATK_TEXT(node)), -1); |
| |
| fl_accessible_node_set_text_selection(node, 1, 2); |
| |
| EXPECT_EQ(atk_text_get_caret_offset(ATK_TEXT(node)), 2); |
| } |
| |
| // Tests AtkText::set_caret_offset. |
| TEST(FlAccessibleTextFieldTest, SetCaretOffset) { |
| int base = -1; |
| int extent = -1; |
| |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| fl_engine_get_embedder_api(engine)->DispatchSemanticsAction = |
| MOCK_ENGINE_PROC( |
| DispatchSemanticsAction, |
| ([&base, &extent](auto engine, uint64_t id, |
| FlutterSemanticsAction action, const uint8_t* data, |
| size_t data_length) { |
| EXPECT_EQ(action, kFlutterSemanticsActionSetSelection); |
| g_autoptr(FlValue) value = decode_semantic_data(data, data_length); |
| EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP); |
| base = fl_value_get_int(fl_value_lookup_string(value, "base")); |
| extent = fl_value_get_int(fl_value_lookup_string(value, "extent")); |
| return kSuccess; |
| })); |
| |
| g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1); |
| |
| EXPECT_TRUE(atk_text_set_caret_offset(ATK_TEXT(node), 3)); |
| EXPECT_EQ(base, 3); |
| EXPECT_EQ(extent, 3); |
| } |
| |
| // Tests AtkText::get_n_selections. |
| TEST(FlAccessibleTextFieldTest, GetNSelections) { |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1); |
| |
| EXPECT_EQ(atk_text_get_n_selections(ATK_TEXT(node)), 0); |
| |
| fl_accessible_node_set_text_selection(node, 1, 2); |
| |
| EXPECT_EQ(atk_text_get_n_selections(ATK_TEXT(node)), 1); |
| } |
| |
| // Tests AtkText::get_selection. |
| TEST(FlAccessibleTextFieldTest, GetSelection) { |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1); |
| |
| EXPECT_EQ(atk_text_get_selection(ATK_TEXT(node), 0, nullptr, nullptr), |
| nullptr); |
| |
| fl_accessible_node_set_value(node, "Flutter"); |
| fl_accessible_node_set_text_selection(node, 2, 5); |
| |
| gint start, end; |
| g_autofree gchar* selection = |
| atk_text_get_selection(ATK_TEXT(node), 0, &start, &end); |
| EXPECT_STREQ(selection, "utt"); |
| EXPECT_EQ(start, 2); |
| EXPECT_EQ(end, 5); |
| |
| // reverse |
| fl_accessible_node_set_text_selection(node, 5, 2); |
| g_autofree gchar* reverse = |
| atk_text_get_selection(ATK_TEXT(node), 0, &start, &end); |
| EXPECT_STREQ(reverse, "utt"); |
| EXPECT_EQ(start, 2); |
| EXPECT_EQ(end, 5); |
| |
| // empty |
| fl_accessible_node_set_text_selection(node, 5, 5); |
| EXPECT_EQ(atk_text_get_selection(ATK_TEXT(node), 0, &start, &end), nullptr); |
| |
| // selection num != 0 |
| EXPECT_EQ(atk_text_get_selection(ATK_TEXT(node), 1, &start, &end), nullptr); |
| } |
| |
| // Tests AtkText::add_selection. |
| TEST(FlAccessibleTextFieldTest, AddSelection) { |
| int base = -1; |
| int extent = -1; |
| |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| fl_engine_get_embedder_api(engine)->DispatchSemanticsAction = |
| MOCK_ENGINE_PROC( |
| DispatchSemanticsAction, |
| ([&base, &extent](auto engine, uint64_t id, |
| FlutterSemanticsAction action, const uint8_t* data, |
| size_t data_length) { |
| EXPECT_EQ(action, kFlutterSemanticsActionSetSelection); |
| g_autoptr(FlValue) value = decode_semantic_data(data, data_length); |
| EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP); |
| base = fl_value_get_int(fl_value_lookup_string(value, "base")); |
| extent = fl_value_get_int(fl_value_lookup_string(value, "extent")); |
| return kSuccess; |
| })); |
| |
| g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1); |
| |
| EXPECT_TRUE(atk_text_add_selection(ATK_TEXT(node), 2, 4)); |
| EXPECT_EQ(base, 2); |
| EXPECT_EQ(extent, 4); |
| |
| fl_accessible_node_set_text_selection(node, 2, 4); |
| |
| // already has selection |
| EXPECT_FALSE(atk_text_add_selection(ATK_TEXT(node), 6, 7)); |
| EXPECT_EQ(base, 2); |
| EXPECT_EQ(extent, 4); |
| } |
| |
| // Tests AtkText::remove_selection. |
| TEST(FlAccessibleTextFieldTest, RemoveSelection) { |
| int base = -1; |
| int extent = -1; |
| |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| fl_engine_get_embedder_api(engine)->DispatchSemanticsAction = |
| MOCK_ENGINE_PROC( |
| DispatchSemanticsAction, |
| ([&base, &extent](auto engine, uint64_t id, |
| FlutterSemanticsAction action, const uint8_t* data, |
| size_t data_length) { |
| EXPECT_EQ(action, kFlutterSemanticsActionSetSelection); |
| g_autoptr(FlValue) value = decode_semantic_data(data, data_length); |
| EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP); |
| base = fl_value_get_int(fl_value_lookup_string(value, "base")); |
| extent = fl_value_get_int(fl_value_lookup_string(value, "extent")); |
| return kSuccess; |
| })); |
| |
| g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1); |
| |
| // no selection |
| EXPECT_FALSE(atk_text_remove_selection(ATK_TEXT(node), 0)); |
| EXPECT_EQ(base, -1); |
| EXPECT_EQ(extent, -1); |
| |
| fl_accessible_node_set_text_selection(node, 2, 4); |
| |
| // selection num != 0 |
| EXPECT_FALSE(atk_text_remove_selection(ATK_TEXT(node), 1)); |
| EXPECT_EQ(base, -1); |
| EXPECT_EQ(extent, -1); |
| |
| // ok, collapses selection |
| EXPECT_TRUE(atk_text_remove_selection(ATK_TEXT(node), 0)); |
| EXPECT_EQ(base, 4); |
| EXPECT_EQ(extent, 4); |
| } |
| |
| // Tests AtkText::set_selection. |
| TEST(FlAccessibleTextFieldTest, SetSelection) { |
| int base = -1; |
| int extent = -1; |
| |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| fl_engine_get_embedder_api(engine)->DispatchSemanticsAction = |
| MOCK_ENGINE_PROC( |
| DispatchSemanticsAction, |
| ([&base, &extent](auto engine, uint64_t id, |
| FlutterSemanticsAction action, const uint8_t* data, |
| size_t data_length) { |
| EXPECT_EQ(action, kFlutterSemanticsActionSetSelection); |
| g_autoptr(FlValue) value = decode_semantic_data(data, data_length); |
| EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP); |
| base = fl_value_get_int(fl_value_lookup_string(value, "base")); |
| extent = fl_value_get_int(fl_value_lookup_string(value, "extent")); |
| return kSuccess; |
| })); |
| |
| g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1); |
| |
| // selection num != 0 |
| EXPECT_FALSE(atk_text_set_selection(ATK_TEXT(node), 1, 2, 4)); |
| EXPECT_EQ(base, -1); |
| EXPECT_EQ(extent, -1); |
| |
| EXPECT_TRUE(atk_text_set_selection(ATK_TEXT(node), 0, 2, 4)); |
| EXPECT_EQ(base, 2); |
| EXPECT_EQ(extent, 4); |
| |
| EXPECT_TRUE(atk_text_set_selection(ATK_TEXT(node), 0, 5, 1)); |
| EXPECT_EQ(base, 5); |
| EXPECT_EQ(extent, 1); |
| } |
| |
| // Tests AtkEditableText::set_text_contents. |
| TEST(FlAccessibleTextFieldTest, SetTextContents) { |
| g_autofree gchar* text = nullptr; |
| |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| fl_engine_get_embedder_api(engine)->DispatchSemanticsAction = |
| MOCK_ENGINE_PROC( |
| DispatchSemanticsAction, |
| ([&text](auto engine, uint64_t id, FlutterSemanticsAction action, |
| const uint8_t* data, size_t data_length) { |
| EXPECT_EQ(action, kFlutterSemanticsActionSetText); |
| g_autoptr(FlValue) value = decode_semantic_data(data, data_length); |
| EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_STRING); |
| text = g_strdup(fl_value_get_string(value)); |
| return kSuccess; |
| })); |
| |
| g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1); |
| |
| atk_editable_text_set_text_contents(ATK_EDITABLE_TEXT(node), "Flutter"); |
| EXPECT_STREQ(text, "Flutter"); |
| } |
| |
| // Tests AtkEditableText::insert/delete_text. |
| TEST(FlAccessibleTextFieldTest, InsertDeleteText) { |
| g_autofree gchar* text = nullptr; |
| int base = -1; |
| int extent = -1; |
| |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| fl_engine_get_embedder_api(engine)->DispatchSemanticsAction = |
| MOCK_ENGINE_PROC( |
| DispatchSemanticsAction, |
| ([&text, &base, &extent](auto engine, uint64_t id, |
| FlutterSemanticsAction action, |
| const uint8_t* data, size_t data_length) { |
| EXPECT_THAT(action, |
| ::testing::AnyOf(kFlutterSemanticsActionSetText, |
| kFlutterSemanticsActionSetSelection)); |
| if (action == kFlutterSemanticsActionSetText) { |
| g_autoptr(FlValue) value = |
| decode_semantic_data(data, data_length); |
| EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_STRING); |
| g_free(text); |
| text = g_strdup(fl_value_get_string(value)); |
| } else { |
| g_autoptr(FlValue) value = |
| decode_semantic_data(data, data_length); |
| EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP); |
| base = fl_value_get_int(fl_value_lookup_string(value, "base")); |
| extent = |
| fl_value_get_int(fl_value_lookup_string(value, "extent")); |
| } |
| return kSuccess; |
| })); |
| |
| g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1); |
| fl_accessible_node_set_value(node, "Fler"); |
| |
| gint pos = 2; |
| atk_editable_text_insert_text(ATK_EDITABLE_TEXT(node), "utt", 3, &pos); |
| EXPECT_EQ(pos, 5); |
| EXPECT_STREQ(text, "Flutter"); |
| EXPECT_EQ(base, pos); |
| EXPECT_EQ(extent, pos); |
| |
| atk_editable_text_delete_text(ATK_EDITABLE_TEXT(node), 2, 5); |
| EXPECT_STREQ(text, "Fler"); |
| EXPECT_EQ(base, 2); |
| EXPECT_EQ(extent, 2); |
| } |
| |
| // Tests AtkEditableText::copy/cut/paste_text. |
| TEST(FlAccessibleTextFieldTest, CopyCutPasteText) { |
| int base = -1; |
| int extent = -1; |
| FlutterSemanticsAction act = kFlutterSemanticsActionCustomAction; |
| |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| fl_engine_get_embedder_api(engine)->DispatchSemanticsAction = |
| MOCK_ENGINE_PROC( |
| DispatchSemanticsAction, |
| ([&act, &base, &extent](auto engine, uint64_t id, |
| FlutterSemanticsAction action, |
| const uint8_t* data, size_t data_length) { |
| EXPECT_THAT(action, |
| ::testing::AnyOf(kFlutterSemanticsActionCut, |
| kFlutterSemanticsActionCopy, |
| kFlutterSemanticsActionPaste, |
| kFlutterSemanticsActionSetSelection)); |
| act = action; |
| if (action == kFlutterSemanticsActionSetSelection) { |
| g_autoptr(FlValue) value = |
| decode_semantic_data(data, data_length); |
| EXPECT_EQ(fl_value_get_type(value), FL_VALUE_TYPE_MAP); |
| base = fl_value_get_int(fl_value_lookup_string(value, "base")); |
| extent = |
| fl_value_get_int(fl_value_lookup_string(value, "extent")); |
| } |
| return kSuccess; |
| })); |
| |
| g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1); |
| |
| atk_editable_text_copy_text(ATK_EDITABLE_TEXT(node), 2, 5); |
| EXPECT_EQ(base, 2); |
| EXPECT_EQ(extent, 5); |
| EXPECT_EQ(act, kFlutterSemanticsActionCopy); |
| |
| atk_editable_text_cut_text(ATK_EDITABLE_TEXT(node), 1, 4); |
| EXPECT_EQ(base, 1); |
| EXPECT_EQ(extent, 4); |
| EXPECT_EQ(act, kFlutterSemanticsActionCut); |
| |
| atk_editable_text_paste_text(ATK_EDITABLE_TEXT(node), 3); |
| EXPECT_EQ(base, 3); |
| EXPECT_EQ(extent, 3); |
| EXPECT_EQ(act, kFlutterSemanticsActionPaste); |
| } |
| |
| TEST(FlAccessibleTextFieldTest, TextBoundary) { |
| g_autoptr(FlEngine) engine = make_mock_engine(); |
| g_autoptr(FlAccessibleNode) node = fl_accessible_text_field_new(engine, 1); |
| |
| fl_accessible_node_set_value(node, |
| "Lorem ipsum.\nDolor sit amet. Praesent commodo?" |
| "\n\nPraesent et felis dui."); |
| |
| // |Lorem |
| gint start_offset = -1, end_offset = -1; |
| g_autofree gchar* lorem_char = atk_text_get_string_at_offset( |
| ATK_TEXT(node), 0, ATK_TEXT_GRANULARITY_CHAR, &start_offset, &end_offset); |
| EXPECT_STREQ(lorem_char, "L"); |
| EXPECT_EQ(start_offset, 0); |
| EXPECT_EQ(end_offset, 1); |
| |
| g_autofree gchar* lorem_word = atk_text_get_string_at_offset( |
| ATK_TEXT(node), 0, ATK_TEXT_GRANULARITY_WORD, &start_offset, &end_offset); |
| EXPECT_STREQ(lorem_word, "Lorem"); |
| EXPECT_EQ(start_offset, 0); |
| EXPECT_EQ(end_offset, 5); |
| |
| g_autofree gchar* lorem_sentence = atk_text_get_string_at_offset( |
| ATK_TEXT(node), 0, ATK_TEXT_GRANULARITY_SENTENCE, &start_offset, |
| &end_offset); |
| EXPECT_STREQ(lorem_sentence, "Lorem ipsum."); |
| EXPECT_EQ(start_offset, 0); |
| EXPECT_EQ(end_offset, 12); |
| |
| g_autofree gchar* lorem_line = atk_text_get_string_at_offset( |
| ATK_TEXT(node), 0, ATK_TEXT_GRANULARITY_LINE, &start_offset, &end_offset); |
| EXPECT_STREQ(lorem_line, "Lorem ipsum."); |
| EXPECT_EQ(start_offset, 0); |
| EXPECT_EQ(end_offset, 12); |
| |
| g_autofree gchar* lorem_paragraph = atk_text_get_string_at_offset( |
| ATK_TEXT(node), 0, ATK_TEXT_GRANULARITY_PARAGRAPH, &start_offset, |
| &end_offset); |
| EXPECT_STREQ(lorem_paragraph, |
| "Lorem ipsum.\nDolor sit amet. Praesent commodo?"); |
| EXPECT_EQ(start_offset, 0); |
| EXPECT_EQ(end_offset, 46); |
| |
| // Pra|esent |
| g_autofree gchar* praesent_char = atk_text_get_string_at_offset( |
| ATK_TEXT(node), 32, ATK_TEXT_GRANULARITY_CHAR, &start_offset, |
| &end_offset); |
| EXPECT_STREQ(praesent_char, "e"); |
| EXPECT_EQ(start_offset, 32); |
| EXPECT_EQ(end_offset, 33); |
| |
| g_autofree gchar* praesent_word = atk_text_get_string_at_offset( |
| ATK_TEXT(node), 32, ATK_TEXT_GRANULARITY_WORD, &start_offset, |
| &end_offset); |
| EXPECT_STREQ(praesent_word, "Praesent"); |
| EXPECT_EQ(start_offset, 29); |
| EXPECT_EQ(end_offset, 37); |
| |
| g_autofree gchar* praesent_sentence = atk_text_get_string_at_offset( |
| ATK_TEXT(node), 32, ATK_TEXT_GRANULARITY_SENTENCE, &start_offset, |
| &end_offset); |
| EXPECT_STREQ(praesent_sentence, "Praesent commodo?"); |
| EXPECT_EQ(start_offset, 29); |
| EXPECT_EQ(end_offset, 46); |
| |
| g_autofree gchar* praesent_line = atk_text_get_string_at_offset( |
| ATK_TEXT(node), 32, ATK_TEXT_GRANULARITY_LINE, &start_offset, |
| &end_offset); |
| EXPECT_STREQ(praesent_line, "Dolor sit amet. Praesent commodo?"); |
| EXPECT_EQ(start_offset, 13); |
| EXPECT_EQ(end_offset, 46); |
| |
| g_autofree gchar* praesent_paragraph = atk_text_get_string_at_offset( |
| ATK_TEXT(node), 32, ATK_TEXT_GRANULARITY_PARAGRAPH, &start_offset, |
| &end_offset); |
| EXPECT_STREQ(praesent_paragraph, |
| "Lorem ipsum.\nDolor sit amet. Praesent commodo?"); |
| EXPECT_EQ(start_offset, 0); |
| EXPECT_EQ(end_offset, 46); |
| |
| // feli|s |
| g_autofree gchar* felis_char = atk_text_get_string_at_offset( |
| ATK_TEXT(node), 64, ATK_TEXT_GRANULARITY_CHAR, &start_offset, |
| &end_offset); |
| EXPECT_STREQ(felis_char, "s"); |
| EXPECT_EQ(start_offset, 64); |
| EXPECT_EQ(end_offset, 65); |
| |
| g_autofree gchar* felis_word = atk_text_get_string_at_offset( |
| ATK_TEXT(node), 64, ATK_TEXT_GRANULARITY_WORD, &start_offset, |
| &end_offset); |
| EXPECT_STREQ(felis_word, "felis"); |
| EXPECT_EQ(start_offset, 60); |
| EXPECT_EQ(end_offset, 65); |
| |
| g_autofree gchar* felis_sentence = atk_text_get_string_at_offset( |
| ATK_TEXT(node), 64, ATK_TEXT_GRANULARITY_SENTENCE, &start_offset, |
| &end_offset); |
| EXPECT_STREQ(felis_sentence, "Praesent et felis dui."); |
| EXPECT_EQ(start_offset, 48); |
| EXPECT_EQ(end_offset, 70); |
| |
| g_autofree gchar* felis_line = atk_text_get_string_at_offset( |
| ATK_TEXT(node), 64, ATK_TEXT_GRANULARITY_LINE, &start_offset, |
| &end_offset); |
| EXPECT_STREQ(felis_line, "Praesent et felis dui."); |
| EXPECT_EQ(start_offset, 48); |
| EXPECT_EQ(end_offset, 70); |
| |
| g_autofree gchar* felis_paragraph = atk_text_get_string_at_offset( |
| ATK_TEXT(node), 64, ATK_TEXT_GRANULARITY_PARAGRAPH, &start_offset, |
| &end_offset); |
| EXPECT_STREQ(felis_paragraph, "\nPraesent et felis dui."); |
| EXPECT_EQ(start_offset, 47); |
| EXPECT_EQ(end_offset, 70); |
| } |
| |
| // NOLINTEND(clang-analyzer-core.StackAddressEscape) |