OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/renderer/hyphenator/hyphenator.h" | |
6 | |
7 #include "base/path_service.h" | |
8 #include "base/platform_file.h" | |
9 #include "base/strings/utf_string_conversions.h" | |
10 #include "content/common/hyphenator_messages.h" | |
11 #include "content/public/test/mock_render_thread.h" | |
12 #include "ipc/ipc_listener.h" | |
13 #include "testing/gtest/include/gtest/gtest.h" | |
14 #include "third_party/hyphen/hyphen.h" | |
15 | |
16 namespace content { | |
17 namespace { | |
18 | |
19 // A mock message listener that listens for HyphenatorHost messages. This class | |
20 // intercepts a HyphenatorHostMsg_OpenDictionary message sent to an | |
21 // IPC::TestSink object and emulates the HyphenatorMessageFilter class. | |
22 class MockListener : public IPC::Listener { | |
23 public: | |
24 MockListener(Hyphenator* hyphenator, const string16& locale) | |
25 : hyphenator_(hyphenator), | |
26 locale_(locale) { | |
27 } | |
28 virtual ~MockListener() { | |
29 } | |
30 | |
31 // IPC::ChannelProxy::MessageFilter implementation. | |
32 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { | |
33 if (message.type() != HyphenatorHostMsg_OpenDictionary::ID) | |
34 return false; | |
35 | |
36 // Retrieve the locale parameter directly because HyphenatorHost messages | |
37 // are internal messages and unit tests cannot access its member functions, | |
38 // i.e. unit tests cannot call the HyphenatorHostMsg_OpenDictionary::Read | |
39 // function. | |
40 PickleIterator iter(message); | |
41 string16 locale; | |
42 EXPECT_TRUE(message.ReadString16(&iter, &locale)); | |
43 EXPECT_EQ(locale_, locale); | |
44 | |
45 // Open the default dictionary and call the OnControllMessageReceived | |
46 // function with a HyphenatorMsg_SetDictionary message. | |
47 base::FilePath dictionary_path; | |
48 if (!PathService::Get(base::DIR_SOURCE_ROOT, &dictionary_path)) | |
49 return false; | |
50 dictionary_path = dictionary_path.AppendASCII("third_party"); | |
51 dictionary_path = dictionary_path.AppendASCII("hyphen"); | |
52 dictionary_path = dictionary_path.AppendASCII("hyph_en_US.dic"); | |
53 base::PlatformFile file = base::CreatePlatformFile( | |
54 dictionary_path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, | |
55 NULL, NULL); | |
56 EXPECT_NE(base::kInvalidPlatformFileValue, file); | |
57 | |
58 IPC::Message response( | |
59 0, HyphenatorMsg_SetDictionary::ID, IPC::Message::PRIORITY_NORMAL); | |
60 IPC::PlatformFileForTransit transit = IPC::GetFileHandleForProcess( | |
61 file, GetHandle(), false); | |
62 IPC::ParamTraits<IPC::PlatformFileForTransit>::Write(&response, transit); | |
63 hyphenator_->OnControlMessageReceived(response); | |
64 base::ClosePlatformFile(file); | |
65 return true; | |
66 } | |
67 | |
68 private: | |
69 base::ProcessHandle GetHandle() const { | |
70 return base::Process::Current().handle(); | |
71 } | |
72 | |
73 Hyphenator* hyphenator_; | |
74 string16 locale_; | |
75 }; | |
76 | |
77 } // namespace | |
78 | |
79 // A unit test for our hyphenator. This class loads a sample hyphenation | |
80 // dictionary and hyphenates words. | |
81 class HyphenatorTest : public testing::Test { | |
82 public: | |
83 HyphenatorTest() { | |
84 } | |
85 | |
86 bool Initialize() { | |
87 base::FilePath dictionary_path; | |
88 if (!PathService::Get(base::DIR_SOURCE_ROOT, &dictionary_path)) | |
89 return false; | |
90 dictionary_path = dictionary_path.AppendASCII("third_party"); | |
91 dictionary_path = dictionary_path.AppendASCII("hyphen"); | |
92 dictionary_path = dictionary_path.AppendASCII("hyph_en_US.dic"); | |
93 base::PlatformFile file = base::CreatePlatformFile( | |
94 dictionary_path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, | |
95 NULL, NULL); | |
96 hyphenator_.reset(new Hyphenator(file)); | |
97 return hyphenator_->Initialize(); | |
98 } | |
99 | |
100 // Creates a human-readable hyphenated word. This function inserts '-' | |
101 // characters to all places where we can insert hyphens to improve the | |
102 // readability of this unit test. | |
103 string16 Hyphenate(const string16& word) { | |
104 string16 hyphenated_word(word); | |
105 size_t position = word.length(); | |
106 while (position > 0) { | |
107 size_t new_position = hyphenator_->ComputeLastHyphenLocation(word, | |
108 position); | |
109 EXPECT_LT(new_position, position); | |
110 if (new_position > 0) | |
111 hyphenated_word.insert(new_position, 1, '-'); | |
112 position = new_position; | |
113 } | |
114 return hyphenated_word; | |
115 } | |
116 | |
117 bool OpenDictionary(const string16& locale) { | |
118 hyphenator_.reset(new Hyphenator(base::kInvalidPlatformFileValue)); | |
119 thread_.reset(new MockRenderThread()); | |
120 listener_.reset(new MockListener(hyphenator_.get(), locale)); | |
121 thread_->sink().AddFilter(listener_.get()); | |
122 return hyphenator_->Attach(thread_.get(), locale); | |
123 } | |
124 | |
125 size_t GetMessageCount() const { | |
126 return thread_->sink().message_count(); | |
127 } | |
128 | |
129 private: | |
130 scoped_ptr<Hyphenator> hyphenator_; | |
131 scoped_ptr<MockRenderThread> thread_; | |
132 scoped_ptr<MockListener> listener_; | |
133 }; | |
134 | |
135 // Verifies that our hyphenator yields the same hyphenated words as the original | |
136 // hyphen library does. | |
137 TEST_F(HyphenatorTest, HyphenateWords) { | |
138 static const struct { | |
139 const char* input; | |
140 const char* expected; | |
141 } kTestCases[] = { | |
142 { "and", "and" }, | |
143 { "concupiscent,", "con-cu-pis-cent," }, | |
144 { "evidence.", "ev-i-dence." }, | |
145 { "first", "first" }, | |
146 { "getting", "get-ting" }, | |
147 { "hedgehog", "hedge-hog" }, | |
148 { "remarkable", "re-mark-able" }, | |
149 { "straightened", "straight-ened" }, | |
150 { "undid", "un-did" }, | |
151 { "were", "were" }, | |
152 { "Simply", "Sim-ply" }, | |
153 { "Undone.", "Un-done." }, | |
154 { "comfortably", "com-fort-ably"}, | |
155 { "declination", "dec-li-na-tion" }, | |
156 { "flamingo:", "flamin-go:" }, | |
157 { "lination", "lina-tion" }, | |
158 { "reciprocity", "rec-i-proc-i-ty" }, | |
159 { "throughout", "through-out" }, | |
160 { "undid", "un-did" }, | |
161 { "undone.", "un-done." }, | |
162 { "unnecessary", "un-nec-es-sary" }, | |
163 }; | |
164 Initialize(); | |
165 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) { | |
166 string16 input = ASCIIToUTF16(kTestCases[i].input); | |
167 string16 expected = ASCIIToUTF16(kTestCases[i].expected); | |
168 EXPECT_EQ(expected, Hyphenate(input)); | |
169 } | |
170 } | |
171 | |
172 // Verifies that our hyphenator sends a HyphenatorHostMsg_OpenDictionary | |
173 // message to ask a browser to open a dictionary. Also, this test verifies that | |
174 // our hyphenator can hyphnate words when the Hyphenator::SetDictionary function | |
175 // is called. | |
176 TEST_F(HyphenatorTest, openDictionary) { | |
177 // Send a HyphenatorHostMsg_OpenDictionary message and verify it is handled by | |
178 // our MockListner class. | |
179 EXPECT_TRUE(OpenDictionary(string16())); | |
180 EXPECT_EQ(0U, GetMessageCount()); | |
181 | |
182 // Verify that we can now hyphenate words. When the MockListener class | |
183 // receives a HyphenatorHostMsg_OpenDictionary message, it calls the | |
184 // OnControlMessageReceived function with a HyphenatorMsg_SetDictionary | |
185 // message. So, the Hyphenate function should be able to hyphenate words now. | |
186 string16 input = ASCIIToUTF16("hyphenation"); | |
187 string16 expected = ASCIIToUTF16("hy-phen-ation"); | |
188 EXPECT_EQ(expected, Hyphenate(input)); | |
189 } | |
190 | |
191 } // namespace content | |
OLD | NEW |