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.
5 #include "content/renderer/hyphenator/hyphenator.h"
7 #include "base/path_service.h"
8 #include "base/platform_file.h"
9 #include "base/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"
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
{
24 MockListener(Hyphenator
* hyphenator
, const string16
& locale
)
25 : hyphenator_(hyphenator
),
28 virtual ~MockListener() {
31 // IPC::ChannelProxy::MessageFilter implementation.
32 virtual bool OnMessageReceived(const IPC::Message
& message
) OVERRIDE
{
33 if (message
.type() != HyphenatorHostMsg_OpenDictionary::ID
)
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
40 PickleIterator
iter(message
);
42 EXPECT_TRUE(message
.ReadString16(&iter
, &locale
));
43 EXPECT_EQ(locale_
, locale
);
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
))
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
,
56 EXPECT_NE(base::kInvalidPlatformFileValue
, file
);
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
);
69 base::ProcessHandle
GetHandle() const {
70 return base::Process::Current().handle();
73 Hyphenator
* hyphenator_
;
79 // A unit test for our hyphenator. This class loads a sample hyphenation
80 // dictionary and hyphenates words.
81 class HyphenatorTest
: public testing::Test
{
87 base::FilePath dictionary_path
;
88 if (!PathService::Get(base::DIR_SOURCE_ROOT
, &dictionary_path
))
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
,
96 hyphenator_
.reset(new Hyphenator(file
));
97 return hyphenator_
->Initialize();
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
,
109 EXPECT_LT(new_position
, position
);
110 if (new_position
> 0)
111 hyphenated_word
.insert(new_position
, 1, '-');
112 position
= new_position
;
114 return hyphenated_word
;
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
);
125 size_t GetMessageCount() const {
126 return thread_
->sink().message_count();
130 scoped_ptr
<Hyphenator
> hyphenator_
;
131 scoped_ptr
<MockRenderThread
> thread_
;
132 scoped_ptr
<MockListener
> listener_
;
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 {
140 const char* expected
;
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" },
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" },
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
));
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
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());
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
));
191 } // namespace content