| // Copyright (c) 2011 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 "views/ime/character_composer.h" |
| |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/gtk+/gdk/gdkkeysyms.h" |
| #include "ui/base/gtk/gtk_integers.h" |
| |
| namespace views { |
| |
| namespace { |
| |
| // Expects key is not filtered and no character is composed. |
| void ExpectKeyNotFiltered(CharacterComposer* character_composer, uint key) { |
| EXPECT_FALSE(character_composer->FilterKeyPress(key)); |
| EXPECT_TRUE(character_composer->composed_character().empty()); |
| } |
| |
| // Expects key is filtered and no character is composed. |
| void ExpectKeyFiltered(CharacterComposer* character_composer, uint key) { |
| EXPECT_TRUE(character_composer->FilterKeyPress(key)); |
| EXPECT_TRUE(character_composer->composed_character().empty()); |
| } |
| |
| // Expects |expected_character| is composed after sequence [key1, key2]. |
| void ExpectCharacterComposed(CharacterComposer* character_composer, |
| uint key1, |
| uint key2, |
| const string16& expected_character) { |
| ExpectKeyFiltered(character_composer, key1); |
| EXPECT_TRUE(character_composer->FilterKeyPress(key2)); |
| EXPECT_EQ(character_composer->composed_character(), expected_character); |
| } |
| |
| // Expects |expected_character| is composed after sequence [key1, key2, key3]. |
| void ExpectCharacterComposed(CharacterComposer* character_composer, |
| uint key1, |
| uint key2, |
| uint key3, |
| const string16& expected_character) { |
| ExpectKeyFiltered(character_composer, key1); |
| ExpectCharacterComposed(character_composer, key2, key3, expected_character); |
| } |
| |
| // Expects |expected_character| is composed after sequence [key1, key2, key3, |
| // key 4]. |
| void ExpectCharacterComposed(CharacterComposer* character_composer, |
| uint key1, |
| uint key2, |
| uint key3, |
| uint key4, |
| const string16& expected_character) { |
| ExpectKeyFiltered(character_composer, key1); |
| ExpectCharacterComposed(character_composer, key2, key3, key4, |
| expected_character); |
| } |
| |
| } // namespace |
| |
| TEST(CharacterComposerTest, InitialState) { |
| CharacterComposer character_composer; |
| EXPECT_TRUE(character_composer.composed_character().empty()); |
| } |
| |
| TEST(CharacterComposerTest, NormalKeyIsNotFiltered) { |
| CharacterComposer character_composer; |
| ExpectKeyNotFiltered(&character_composer, GDK_KEY_B); |
| ExpectKeyNotFiltered(&character_composer, GDK_KEY_Z); |
| ExpectKeyNotFiltered(&character_composer, GDK_KEY_c); |
| ExpectKeyNotFiltered(&character_composer, GDK_KEY_m); |
| ExpectKeyNotFiltered(&character_composer, GDK_KEY_0); |
| ExpectKeyNotFiltered(&character_composer, GDK_KEY_1); |
| ExpectKeyNotFiltered(&character_composer, GDK_KEY_8); |
| } |
| |
| TEST(CharacterComposerTest, PartiallyMatchingSequence) { |
| CharacterComposer character_composer; |
| |
| // Composition with sequence ['dead acute', '1'] will fail. |
| ExpectKeyFiltered(&character_composer, GDK_KEY_dead_acute); |
| EXPECT_TRUE(character_composer.FilterKeyPress(GDK_KEY_1)); |
| EXPECT_TRUE(character_composer.composed_character().empty()); |
| |
| // Composition with sequence ['dead acute', 'dead circumflex', '1'] will fail. |
| ExpectKeyFiltered(&character_composer, GDK_KEY_dead_acute); |
| ExpectKeyFiltered(&character_composer, GDK_KEY_dead_circumflex); |
| EXPECT_TRUE(character_composer.FilterKeyPress(GDK_KEY_1)); |
| EXPECT_TRUE(character_composer.composed_character().empty()); |
| } |
| |
| TEST(CharacterComposerTest, FullyMatchingSequences) { |
| CharacterComposer character_composer; |
| // LATIN SMALL LETTER A WITH ACUTE |
| ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, GDK_KEY_a, |
| string16(1, 0x00E1)); |
| // LATIN CAPITAL LETTER A WITH ACUTE |
| ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, GDK_KEY_A, |
| string16(1, 0x00C1)); |
| // GRAVE ACCENT |
| ExpectCharacterComposed(&character_composer, GDK_KEY_dead_grave, |
| GDK_KEY_dead_grave, string16(1, 0x0060)); |
| // LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE |
| ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, |
| GDK_KEY_dead_circumflex, GDK_KEY_a, |
| string16(1, 0x1EA5)); |
| // LATIN CAPITAL LETTER U WITH HORN AND GRAVE |
| ExpectCharacterComposed(&character_composer, GDK_KEY_dead_grave, |
| GDK_KEY_dead_horn, GDK_KEY_U, string16(1, 0x1EEA)); |
| // LATIN CAPITAL LETTER C WITH CEDILLA |
| ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, GDK_KEY_C, |
| string16(1, 0x00C7)); |
| // LATIN SMALL LETTER C WITH CEDILLA |
| ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, GDK_KEY_c, |
| string16(1, 0x00E7)); |
| } |
| |
| TEST(CharacterComposerTest, FullyMatchingSequencesAfterMatchingFailure) { |
| CharacterComposer character_composer; |
| // Composition with sequence ['dead acute', 'dead circumflex', '1'] will fail. |
| ExpectKeyFiltered(&character_composer, GDK_KEY_dead_acute); |
| ExpectKeyFiltered(&character_composer, GDK_KEY_dead_circumflex); |
| EXPECT_TRUE(character_composer.FilterKeyPress(GDK_KEY_1)); |
| EXPECT_TRUE(character_composer.composed_character().empty()); |
| // LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE |
| ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, |
| GDK_KEY_dead_circumflex, GDK_KEY_a, |
| string16(1, 0x1EA5)); |
| } |
| |
| TEST(CharacterComposerTest, ComposedCharacterIsClearedAfterReset) { |
| CharacterComposer character_composer; |
| ExpectCharacterComposed(&character_composer, GDK_KEY_dead_acute, GDK_KEY_a, |
| string16(1, 0x00E1)); |
| character_composer.Reset(); |
| EXPECT_TRUE(character_composer.composed_character().empty()); |
| } |
| |
| TEST(CharacterComposerTest, CompositionStateIsClearedAfterReset) { |
| CharacterComposer character_composer; |
| // Even though sequence ['dead acute', 'a'] will compose 'a with acute', |
| // no character is composed here because of reset. |
| ExpectKeyFiltered(&character_composer, GDK_KEY_dead_acute); |
| character_composer.Reset(); |
| EXPECT_FALSE(character_composer.FilterKeyPress(GDK_KEY_a)); |
| EXPECT_TRUE(character_composer.composed_character().empty()); |
| } |
| |
| // ComposeCheckerWithCompactTable in character_composer.cc is depending on the |
| // assumption that the data in gtkimcontextsimpleseqs.h is correctly ordered. |
| TEST(CharacterComposerTest, MainTableIsCorrectlyOrdered) { |
| // This file is included here intentionally, instead of the top of the file, |
| // because including this file at the top of the file will define a |
| // global constant and contaminate the global namespace. |
| #include "third_party/gtk+/gtk/gtkimcontextsimpleseqs.h" |
| const int index_size = 26; |
| const int index_stride = 6; |
| |
| // Verify that the index is correctly ordered |
| for (int i = 1; i < index_size; ++i) { |
| const int index_key_prev = gtk_compose_seqs_compact[(i - 1)*index_stride]; |
| const int index_key = gtk_compose_seqs_compact[i*index_stride]; |
| EXPECT_TRUE(index_key > index_key_prev); |
| } |
| |
| // Verify that the sequenes are correctly ordered |
| struct { |
| int operator()(const uint16* l, const uint16* r, int length) const{ |
| for (int i = 0; i < length; ++i) { |
| if (l[i] > r[i]) |
| return 1; |
| if (l[i] < r[i]) |
| return -1; |
| } |
| return 0; |
| } |
| } compare_sequence; |
| |
| for (int i = 0; i < index_size; ++i) { |
| for (int length = 1; length < index_stride - 1; ++length) { |
| const int index_begin = gtk_compose_seqs_compact[i*index_stride + length]; |
| const int index_end = |
| gtk_compose_seqs_compact[i*index_stride + length + 1]; |
| const int stride = length + 1; |
| for (int index = index_begin + stride; index < index_end; |
| index += stride) { |
| const uint16* sequence = >k_compose_seqs_compact[index]; |
| const uint16* sequence_prev = sequence - stride; |
| EXPECT_EQ(compare_sequence(sequence, sequence_prev, length), 1); |
| } |
| } |
| } |
| } |
| |
| } // namespace views |