summaryrefslogtreecommitdiffstats
path: root/content/renderer/hyphenator/hyphenator_unittest.cc
blob: 1a830e2e516975be7ea788053a3b5ab044656b57 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// Copyright (c) 2012 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 "content/renderer/hyphenator/hyphenator.h"

#include "base/path_service.h"
#include "base/platform_file.h"
#include "base/utf_string_conversions.h"
#include "content/common/hyphenator_messages.h"
#include "content/public/test/mock_render_thread.h"
#include "ipc/ipc_listener.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/hyphen/hyphen.h"

namespace {

// A mock message listener that listens for HyphenatorHost messages. This class
// intercepts a HyphenatorHostMsg_OpenDictionary message sent to an
// IPC::TestSink object and emulates the HyphenatorMessageFilter class.
class MockListener : public IPC::Listener {
 public:
  MockListener(content::Hyphenator* hyphenator, const string16& locale)
      : hyphenator_(hyphenator),
        locale_(locale) {
  }
  virtual ~MockListener() {
  }

  // IPC::ChannelProxy::MessageFilter implementation.
  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
    if (message.type() != HyphenatorHostMsg_OpenDictionary::ID)
      return false;

    // Retrieve the locale parameter directly because HyphenatorHost messages
    // are internal messages and unit tests cannot access its member functions,
    // i.e. unit tests cannot call the HyphenatorHostMsg_OpenDictionary::Read
    // function.
    PickleIterator iter(message);
    string16 locale;
    EXPECT_TRUE(message.ReadString16(&iter, &locale));
    EXPECT_EQ(locale_, locale);

    // Open the default dictionary and call the OnControllMessageReceived
    // function with a HyphenatorMsg_SetDictionary message.
    FilePath dictionary_path;
    if (!PathService::Get(base::DIR_SOURCE_ROOT, &dictionary_path))
      return false;
    dictionary_path = dictionary_path.AppendASCII("third_party");
    dictionary_path = dictionary_path.AppendASCII("hyphen");
    dictionary_path = dictionary_path.AppendASCII("hyph_en_US.dic");
    base::PlatformFile file = base::CreatePlatformFile(
        dictionary_path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
        NULL, NULL);
    EXPECT_NE(base::kInvalidPlatformFileValue, file);

    IPC::Message response(
        0, HyphenatorMsg_SetDictionary::ID, IPC::Message::PRIORITY_NORMAL);
    IPC::PlatformFileForTransit transit = IPC::GetFileHandleForProcess(
        file, GetHandle(), false);
    IPC::ParamTraits<IPC::PlatformFileForTransit>::Write(&response, transit);
    hyphenator_->OnControlMessageReceived(response);
    base::ClosePlatformFile(file);
    return true;
  }

 private:
  base::ProcessHandle GetHandle() const {
    return base::Process::Current().handle();
  }

  content::Hyphenator* hyphenator_;
  string16 locale_;
};

}  // namespace

// A unit test for our hyphenator. This class loads a sample hyphenation
// dictionary and hyphenates words.
class HyphenatorTest : public testing::Test {
 public:
  HyphenatorTest() {
  }

  bool Initialize() {
    FilePath dictionary_path;
    if (!PathService::Get(base::DIR_SOURCE_ROOT, &dictionary_path))
      return false;
    dictionary_path = dictionary_path.AppendASCII("third_party");
    dictionary_path = dictionary_path.AppendASCII("hyphen");
    dictionary_path = dictionary_path.AppendASCII("hyph_en_US.dic");
    base::PlatformFile file = base::CreatePlatformFile(
        dictionary_path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
        NULL, NULL);
    hyphenator_.reset(new content::Hyphenator(file));
    return hyphenator_->Initialize();
  }

  // Creates a human-readable hyphenated word. This function inserts '-'
  // characters to all places where we can insert hyphens to improve the
  // readability of this unit test.
  string16 Hyphenate(const string16& word) {
    string16 hyphenated_word(word);
    size_t position = word.length();
    while (position > 0) {
      size_t new_position = hyphenator_->ComputeLastHyphenLocation(word,
                                                                   position);
      EXPECT_LT(new_position, position);
      if (new_position > 0)
        hyphenated_word.insert(new_position, 1, '-');
      position = new_position;
    }
    return hyphenated_word;
  }

  bool OpenDictionary(const string16& locale) {
    hyphenator_.reset(new content::Hyphenator(base::kInvalidPlatformFileValue));
    thread_.reset(new content::MockRenderThread());
    listener_.reset(new MockListener(hyphenator_.get(), locale));
    thread_->sink().AddFilter(listener_.get());
    return hyphenator_->Attach(thread_.get(), locale);
  }

  size_t GetMessageCount() const {
    return thread_->sink().message_count();
  }

 private:
  scoped_ptr<content::Hyphenator> hyphenator_;
  scoped_ptr<content::MockRenderThread> thread_;
  scoped_ptr<MockListener> listener_;
};

// Verifies that our hyphenator yields the same hyphenated words as the original
// hyphen library does.
TEST_F(HyphenatorTest, HyphenateWords) {
  static const struct {
    const char* input;
    const char* expected;
  } kTestCases[] = {
    { "and", "and" },
    { "concupiscent,", "con-cu-pis-cent," },
    { "evidence.", "ev-i-dence." },
    { "first", "first" },
    { "getting", "get-ting" },
    { "hedgehog", "hedge-hog" },
    { "remarkable", "re-mark-able" },
    { "straightened", "straight-ened" },
    { "undid", "un-did" },
    { "were", "were" },
    { "Simply", "Sim-ply" },
    { "Undone.", "Un-done." },
    { "comfortably", "com-fort-ably"},
    { "declination", "dec-li-na-tion" },
    { "flamingo:", "flamin-go:" },
    { "lination", "lina-tion" },
    { "reciprocity", "rec-i-proc-i-ty" },
    { "throughout", "through-out" },
    { "undid", "un-did" },
    { "undone.", "un-done." },
    { "unnecessary", "un-nec-es-sary" },
  };
  Initialize();
  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
    string16 input = ASCIIToUTF16(kTestCases[i].input);
    string16 expected = ASCIIToUTF16(kTestCases[i].expected);
    EXPECT_EQ(expected, Hyphenate(input));
  }
}

// Verifies that our hyphenator sends a HyphenatorHostMsg_OpenDictionary
// message to ask a browser to open a dictionary. Also, this test verifies that
// our hyphenator can hyphnate words when the Hyphenator::SetDictionary function
// is called.
TEST_F(HyphenatorTest, openDictionary) {
  // Send a HyphenatorHostMsg_OpenDictionary message and verify it is handled by
  // our MockListner class.
  EXPECT_TRUE(OpenDictionary(string16()));
  EXPECT_EQ(0U, GetMessageCount());

  // Verify that we can now hyphenate words. When the MockListener class
  // receives a HyphenatorHostMsg_OpenDictionary message, it calls the
  // OnControlMessageReceived function with a HyphenatorMsg_SetDictionary
  // message. So, the Hyphenate function should be able to hyphenate words now.
  string16 input = ASCIIToUTF16("hyphenation");
  string16 expected = ASCIIToUTF16("hy-phen-ation");
  EXPECT_EQ(expected, Hyphenate(input));
}