1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "renderer/unix/infolist_window.h"
31
32 #include <string>
33
34 #include "renderer/unix/cairo_factory_mock.h"
35 #include "renderer/unix/const.h"
36 #include "renderer/unix/draw_tool_mock.h"
37 #include "renderer/unix/gtk_wrapper_mock.h"
38 #include "renderer/unix/text_renderer_mock.h"
39 #include "testing/base/public/gmock.h"
40 #include "testing/base/public/gunit.h"
41
42 using ::testing::Return;
43 using ::testing::StrEq;
44 using ::testing::StrictMock;
45 using ::testing::_;
46
47 namespace mozc {
48 namespace renderer {
49 namespace gtk {
50
51 namespace {
52 // Following variable is used testing and it is contant but due to API
53 // restriction, can not modify const modifier.
54 GtkWidget *kDummyWindow = reinterpret_cast<GtkWidget*>(0x12345678);
55 GtkWidget *kDummyCanvas = reinterpret_cast<GtkWidget*>(0x87654321);
56
57 const char kSampleTitle[] = "TITLE";
58 const char kSampleDescription[] = "DESCRIPTION";
59
SetInformations(int count,commands::InformationList * usages)60 void SetInformations(int count, commands::InformationList *usages) {
61 usages->Clear();
62 for (int i = 0; i < count; ++i) {
63 commands::Information *info = usages->add_information();
64
65 const string id_str = std::to_string(i);
66 info->set_title(kSampleTitle + id_str);
67 info->set_description(kSampleDescription + id_str);
68 }
69 }
70
GetExpectedTitle(int row)71 string GetExpectedTitle(int row) {
72 const string id_str = std::to_string(row);
73 return kSampleTitle + id_str;
74 }
75
GetExpectedDescription(int row)76 string GetExpectedDescription(int row) {
77 const string id_str = std::to_string(row);
78 return kSampleDescription + id_str;
79 }
80
81 MATCHER_P(RectEq, expected_rect, "The expected rect does not match") {
82 return (arg.origin.x == expected_rect.origin.x) &&
83 (arg.origin.y == expected_rect.origin.y) &&
84 (arg.size.width == expected_rect.size.width) &&
85 (arg.size.height == expected_rect.size.height);
86 }
87
88 MATCHER_P(RGBAEq, expected_rgba, "The expected RGBA does not match") {
89 return (arg.red == expected_rgba.red) &&
90 (arg.green == expected_rgba.green) &&
91 (arg.blue == expected_rgba.blue) &&
92 (arg.alpha == expected_rgba.alpha);
93 }
94
StyleColorToRGBA(const RendererStyle::RGBAColor & rgbacolor)95 RGBA StyleColorToRGBA(
96 const RendererStyle::RGBAColor &rgbacolor) {
97 const RGBA rgba = {
98 static_cast<uint8>(rgbacolor.r()),
99 static_cast<uint8>(rgbacolor.g()),
100 static_cast<uint8>(rgbacolor.b()),
101 0xFF };
102 return rgba;
103 }
104 } // namespace
105
106 class InfolistWindowTest : public testing::Test {
107 protected:
108 struct InfolistWindowTestKit {
109 GtkWrapperMock *gtk_mock;
110 TextRendererMock *text_renderer_mock;
111 DrawToolMock *draw_tool_mock;
112 CairoFactoryMock *cairo_factory_mock;
113 InfolistWindow *window;
114 };
115
SetUpInfolistWindowConstractorCallExpectations(InfolistWindowTestKit * testkit)116 void SetUpInfolistWindowConstractorCallExpectations(
117 InfolistWindowTestKit *testkit) {
118 // Following functions are expected to be called by constructor.
119 EXPECT_CALL(*testkit->gtk_mock, GtkWindowNew(GTK_WINDOW_POPUP))
120 .WillOnce(Return(kDummyWindow));
121 EXPECT_CALL(*testkit->gtk_mock, GtkDrawingAreaNew())
122 .WillOnce(Return(kDummyCanvas));
123 EXPECT_CALL(*testkit->gtk_mock, GSignalConnect(
124 kDummyWindow, StrEq("destroy"),
125 G_CALLBACK(GtkWindowBase::OnDestroyThunk), _));
126 EXPECT_CALL(*testkit->gtk_mock, GSignalConnect(
127 kDummyWindow, StrEq("button-press-event"),
128 G_CALLBACK(GtkWindowBase::OnMouseDownThunk), _));
129 EXPECT_CALL(*testkit->gtk_mock, GSignalConnect(
130 kDummyWindow, StrEq("button-release-event"),
131 G_CALLBACK(GtkWindowBase::OnMouseUpThunk), _));
132 EXPECT_CALL(*testkit->gtk_mock, GSignalConnect(
133 kDummyCanvas, StrEq("expose-event"),
134 G_CALLBACK(GtkWindowBase::OnPaintThunk), _));
135 EXPECT_CALL(*testkit->gtk_mock,
136 GtkContainerAdd(kDummyWindow, kDummyCanvas));
137 EXPECT_CALL(*testkit->gtk_mock,
138 GtkWidgetAddEvents(kDummyWindow, GDK_BUTTON_PRESS_MASK));
139 EXPECT_CALL(*testkit->gtk_mock,
140 GtkWidgetAddEvents(kDummyWindow, GDK_BUTTON_RELEASE_MASK));
141 EXPECT_CALL(*testkit->gtk_mock, GtkWidgetRealize(kDummyWindow));
142 EXPECT_CALL(*testkit->gtk_mock,
143 GdkWindowSetTypeHint(kDummyWindow,
144 GDK_WINDOW_TYPE_HINT_POPUP_MENU));
145 }
146
SetUpInfolistWindow()147 InfolistWindowTestKit SetUpInfolistWindow() {
148 InfolistWindowTestKit testkit;
149 testkit.gtk_mock = new GtkWrapperMock();
150 testkit.text_renderer_mock = new TextRendererMock();
151 testkit.draw_tool_mock = new DrawToolMock();
152 testkit.cairo_factory_mock = new CairoFactoryMock();
153
154 SetUpInfolistWindowConstractorCallExpectations(&testkit);
155
156 testkit.window = new InfolistWindow(testkit.text_renderer_mock,
157 testkit.draw_tool_mock,
158 testkit.gtk_mock,
159 testkit.cairo_factory_mock);
160 return testkit;
161 }
162
SetUpInfolistWindowWithStrictMock()163 InfolistWindowTestKit SetUpInfolistWindowWithStrictMock() {
164 InfolistWindowTestKit testkit;
165 testkit.gtk_mock = new StrictMock<GtkWrapperMock>();
166 testkit.text_renderer_mock = new StrictMock<TextRendererMock>();
167 testkit.draw_tool_mock = new StrictMock<DrawToolMock>();
168 testkit.cairo_factory_mock = new StrictMock<CairoFactoryMock>();
169
170 SetUpInfolistWindowConstractorCallExpectations(&testkit);
171
172 testkit.window = new InfolistWindow(testkit.text_renderer_mock,
173 testkit.draw_tool_mock,
174 testkit.gtk_mock,
175 testkit.cairo_factory_mock);
176 return testkit;
177 }
178
FinalizeTestKit(InfolistWindowTestKit * testkit)179 void FinalizeTestKit(InfolistWindowTestKit *testkit) {
180 delete testkit->window;
181 }
182 };
183
TEST_F(InfolistWindowTest,DrawFrameTest)184 TEST_F(InfolistWindowTest, DrawFrameTest) {
185 InfolistWindowTestKit testkit = SetUpInfolistWindow();
186 const RendererStyle::InfolistStyle &infostyle
187 = testkit.window->style_->infolist_style();
188 const int height = 1234;
189 const Rect expected_rect(0, 0, infostyle.window_width(), height);
190 EXPECT_CALL(*testkit.draw_tool_mock,
191 FrameRect(RectEq(expected_rect),
192 RGBAEq(StyleColorToRGBA(infostyle.border_color())),
193 1));
194 testkit.window->DrawFrame(height);
195 FinalizeTestKit(&testkit);
196 }
197
TEST_F(InfolistWindowTest,GetRowRectsTest)198 TEST_F(InfolistWindowTest, GetRowRectsTest) {
199 InfolistWindowTestKit testkit = SetUpInfolistWindow();
200 const RendererStyle::InfolistStyle &infostyle
201 = testkit.window->style_->infolist_style();
202 const renderer::RendererStyle::TextStyle title_style
203 = infostyle.title_style();
204 const renderer::RendererStyle::TextStyle description_style
205 = infostyle.description_style();
206 const int ypos = 123;
207 const FontSpecInterface::FONT_TYPE infolist_font_type
208 = FontSpecInterface::FONTSET_INFOLIST_TITLE;
209 const FontSpecInterface::FONT_TYPE description_font_type
210 = FontSpecInterface::FONTSET_INFOLIST_DESCRIPTION;
211 const Size text_size(10, 20);
212
213 SetInformations(10, testkit.window->candidates_.mutable_usages());
214
215 EXPECT_CALL(*testkit.text_renderer_mock,
216 GetMultiLinePixelSize(infolist_font_type,
217 GetExpectedTitle(0), _))
218 .WillOnce(Return(text_size));
219 EXPECT_CALL(*testkit.text_renderer_mock,
220 GetMultiLinePixelSize(description_font_type,
221 GetExpectedDescription(0), _))
222 .WillOnce(Return(text_size));
223
224 InfolistWindow::RenderingRowRects row_rects
225 = testkit.window->GetRowRects(0, ypos);
226
227 EXPECT_EQ(row_rects.title_back_rect.Height(),
228 row_rects.desc_back_rect.Top() - row_rects.title_back_rect.Top());
229 EXPECT_EQ(row_rects.title_back_rect.Top(), row_rects.whole_rect.Top());
230 EXPECT_EQ(row_rects.title_back_rect.Left(), row_rects.whole_rect.Left());
231 EXPECT_EQ(row_rects.title_back_rect.Width(), row_rects.whole_rect.Width());
232 EXPECT_EQ(row_rects.title_back_rect.Height()
233 + row_rects.desc_back_rect.Height(),
234 row_rects.whole_rect.Height());
235
236 FinalizeTestKit(&testkit);
237 }
238
TEST_F(InfolistWindowTest,DrawRowTest)239 TEST_F(InfolistWindowTest, DrawRowTest) {
240 {
241 SCOPED_TRACE("Draw focused area");
242 for (int i = 0; i < 10; ++i) {
243 InfolistWindowTestKit testkit = SetUpInfolistWindow();
244 const RendererStyle::InfolistStyle &infostyle
245 = testkit.window->style_->infolist_style();
246 const FontSpecInterface::FONT_TYPE infolist_font_type
247 = FontSpecInterface::FONTSET_INFOLIST_TITLE;
248 const FontSpecInterface::FONT_TYPE description_font_type
249 = FontSpecInterface::FONTSET_INFOLIST_DESCRIPTION;
250 const Size text_size(10, 20);
251 SetInformations(10, testkit.window->candidates_.mutable_usages());
252 const int ypos = i * 15;
253
254 testkit.window->candidates_.mutable_usages()->set_focused_index(i);
255
256 EXPECT_CALL(*testkit.text_renderer_mock,
257 GetMultiLinePixelSize(infolist_font_type,
258 GetExpectedTitle(i), _))
259 .WillRepeatedly(Return(text_size));
260 EXPECT_CALL(*testkit.text_renderer_mock,
261 GetMultiLinePixelSize(description_font_type,
262 GetExpectedDescription(i), _))
263 .WillRepeatedly(Return(text_size));
264
265 const InfolistWindow::RenderingRowRects sample_row_rects =
266 testkit.window->GetRowRects(i, ypos);
267
268 EXPECT_CALL(*testkit.draw_tool_mock,
269 FillRect(RectEq(sample_row_rects.whole_rect),
270 RGBAEq(StyleColorToRGBA(
271 infostyle.focused_background_color()))));
272 EXPECT_CALL(*testkit.draw_tool_mock,
273 FrameRect(RectEq(sample_row_rects.whole_rect),
274 RGBAEq(StyleColorToRGBA(
275 infostyle.focused_border_color())),
276 1));
277 EXPECT_CALL(*testkit.text_renderer_mock,
278 RenderText(StrEq(GetExpectedTitle(i)),
279 RectEq(sample_row_rects.title_rect),
280 FontSpecInterface::FONTSET_INFOLIST_TITLE));
281 EXPECT_CALL(*testkit.text_renderer_mock,
282 RenderText(StrEq(GetExpectedDescription(i)),
283 RectEq(sample_row_rects.desc_rect),
284 FontSpecInterface::FONTSET_INFOLIST_DESCRIPTION));
285
286 testkit.window->DrawRow(i, ypos);
287 FinalizeTestKit(&testkit);
288 }
289 }
290 {
291 SCOPED_TRACE("Draw focus-outed area with default color");
292 for (int i = 0; i < 10; ++i) {
293 InfolistWindowTestKit testkit = SetUpInfolistWindow();
294 RendererStyle::InfolistStyle *infostyle
295 = testkit.window->style_->mutable_infolist_style();
296 infostyle->mutable_title_style()->clear_background_color();
297 infostyle->mutable_description_style()->clear_background_color();
298 const FontSpecInterface::FONT_TYPE infolist_font_type
299 = FontSpecInterface::FONTSET_INFOLIST_TITLE;
300 const FontSpecInterface::FONT_TYPE description_font_type
301 = FontSpecInterface::FONTSET_INFOLIST_DESCRIPTION;
302 const Size text_size(10, 20);
303 SetInformations(10, testkit.window->candidates_.mutable_usages());
304 const int ypos = i * 15;
305 SetInformations(10, testkit.window->candidates_.mutable_usages());
306
307 EXPECT_CALL(*testkit.text_renderer_mock,
308 GetMultiLinePixelSize(infolist_font_type,
309 GetExpectedTitle(i), _))
310 .WillRepeatedly(Return(text_size));
311 EXPECT_CALL(*testkit.text_renderer_mock,
312 GetMultiLinePixelSize(description_font_type,
313 GetExpectedDescription(i), _))
314 .WillRepeatedly(Return(text_size));
315
316 const InfolistWindow::RenderingRowRects sample_row_rects =
317 testkit.window->GetRowRects(i, ypos);
318
319 EXPECT_CALL(*testkit.draw_tool_mock,
320 FillRect(RectEq(sample_row_rects.title_back_rect),
321 RGBAEq(kWhite)));
322 EXPECT_CALL(*testkit.draw_tool_mock,
323 FillRect(RectEq(sample_row_rects.desc_back_rect),
324 RGBAEq(kWhite)));
325 EXPECT_CALL(*testkit.text_renderer_mock,
326 RenderText(StrEq(GetExpectedTitle(i)),
327 RectEq(sample_row_rects.title_rect),
328 FontSpecInterface::FONTSET_INFOLIST_TITLE));
329 EXPECT_CALL(*testkit.text_renderer_mock,
330 RenderText(StrEq(GetExpectedDescription(i)),
331 RectEq(sample_row_rects.desc_rect),
332 FontSpecInterface::FONTSET_INFOLIST_DESCRIPTION));
333
334 testkit.window->DrawRow(i, ypos);
335 FinalizeTestKit(&testkit);
336 }
337 }
338 {
339 SCOPED_TRACE("Draw focus-outed area with specified color");
340 for (int i = 0; i < 10; ++i) {
341 InfolistWindowTestKit testkit = SetUpInfolistWindow();
342 RendererStyle::InfolistStyle *infostyle
343 = testkit.window->style_->mutable_infolist_style();
344 infostyle->mutable_title_style()->mutable_background_color();
345 infostyle->mutable_description_style()->mutable_background_color();
346 const FontSpecInterface::FONT_TYPE infolist_font_type
347 = FontSpecInterface::FONTSET_INFOLIST_TITLE;
348 const FontSpecInterface::FONT_TYPE description_font_type
349 = FontSpecInterface::FONTSET_INFOLIST_DESCRIPTION;
350 const Size text_size(10, 20);
351 SetInformations(10, testkit.window->candidates_.mutable_usages());
352 const int ypos = i * 15;
353
354 SetInformations(10, testkit.window->candidates_.mutable_usages());
355
356 EXPECT_CALL(*testkit.text_renderer_mock,
357 GetMultiLinePixelSize(infolist_font_type,
358 GetExpectedTitle(i), _))
359 .WillRepeatedly(Return(text_size));
360 EXPECT_CALL(*testkit.text_renderer_mock,
361 GetMultiLinePixelSize(description_font_type,
362 GetExpectedDescription(i), _))
363 .WillRepeatedly(Return(text_size));
364
365 const InfolistWindow::RenderingRowRects sample_row_rects =
366 testkit.window->GetRowRects(i, ypos);
367
368 EXPECT_CALL(*testkit.draw_tool_mock,
369 FillRect(RectEq(sample_row_rects.title_back_rect),
370 RGBAEq(StyleColorToRGBA(
371 infostyle->title_style().background_color()))));
372 EXPECT_CALL(*testkit.draw_tool_mock,
373 FillRect(RectEq(sample_row_rects.desc_back_rect),
374 RGBAEq(StyleColorToRGBA(
375 infostyle->description_style().background_color()))));
376 EXPECT_CALL(*testkit.text_renderer_mock,
377 RenderText(StrEq(GetExpectedTitle(i)),
378 RectEq(sample_row_rects.title_rect),
379 FontSpecInterface::FONTSET_INFOLIST_TITLE));
380 EXPECT_CALL(*testkit.text_renderer_mock,
381 RenderText(StrEq(GetExpectedDescription(i)),
382 RectEq(sample_row_rects.desc_rect),
383 FontSpecInterface::FONTSET_INFOLIST_DESCRIPTION));
384
385 testkit.window->DrawRow(i, ypos);
386 FinalizeTestKit(&testkit);
387 }
388 }
389 }
390
TEST_F(InfolistWindowTest,DrawCaptionTest)391 TEST_F(InfolistWindowTest, DrawCaptionTest) {
392 {
393 SCOPED_TRACE("If there is no caption, do nothing");
394 InfolistWindowTestKit testkit = SetUpInfolistWindowWithStrictMock();
395 testkit.window->style_.reset(new RendererStyle());
396 testkit.window->DrawCaption();
397 FinalizeTestKit(&testkit);
398 }
399 {
400 InfolistWindowTestKit testkit = SetUpInfolistWindow();
401 const RendererStyle::InfolistStyle &infostyle
402 = testkit.window->style_->infolist_style();
403 const RendererStyle::TextStyle &caption_style = infostyle.caption_style();
404
405 const Rect bg_expected_rect(
406 infostyle.window_border(),
407 infostyle.window_border(),
408 infostyle.window_width() - infostyle.window_border() * 2,
409 infostyle.caption_height());
410 const RGBA bg_expected_color
411 = StyleColorToRGBA(infostyle.caption_background_color());
412 EXPECT_CALL(*testkit.draw_tool_mock,
413 FillRect(RectEq(bg_expected_rect), RGBAEq(bg_expected_color)));
414
415 const Rect caption_expected_rect(
416 bg_expected_rect.Left()
417 + infostyle.caption_padding() + caption_style.left_padding(),
418 bg_expected_rect.Top() + infostyle.caption_padding(),
419 bg_expected_rect.Width()
420 - infostyle.caption_padding() - caption_style.left_padding(),
421 infostyle.caption_height() - infostyle.caption_padding());
422 EXPECT_CALL(*testkit.text_renderer_mock,
423 RenderText(infostyle.caption_string(),
424 RectEq(caption_expected_rect),
425 FontSpecInterface::FONTSET_INFOLIST_CAPTION));
426
427 EXPECT_EQ(infostyle.caption_height(), testkit.window->DrawCaption());
428
429 FinalizeTestKit(&testkit);
430 }
431 }
432
TEST_F(InfolistWindowTest,GetRenderingRectsTest)433 TEST_F(InfolistWindowTest, GetRenderingRectsTest) {
434 // TODO(nona): rectangle argument verification.
435 {
436 SCOPED_TRACE("title style");
437 InfolistWindowTestKit testkit = SetUpInfolistWindow();
438 const RendererStyle::InfolistStyle &infostyle
439 = testkit.window->style_->infolist_style();
440 const renderer::RendererStyle::TextStyle title_style
441 = infostyle.title_style();
442 const int ypos = 123;
443 const FontSpecInterface::FONT_TYPE font_type
444 = FontSpecInterface::FONTSET_INFOLIST_TITLE;
445 const Size text_size(10, 20);
446 EXPECT_CALL(*testkit.text_renderer_mock,
447 GetMultiLinePixelSize(font_type, kSampleTitle, _))
448 .WillOnce(Return(text_size));
449
450 Rect bg_rect, text_rect;
451 testkit.window->GetRenderingRects(title_style, kSampleTitle,
452 font_type, ypos, &bg_rect, &text_rect);
453 FinalizeTestKit(&testkit);
454 }
455 {
456 SCOPED_TRACE("description style");
457 InfolistWindowTestKit testkit = SetUpInfolistWindow();
458 const RendererStyle::InfolistStyle &infostyle
459 = testkit.window->style_->infolist_style();
460 const renderer::RendererStyle::TextStyle description_style
461 = infostyle.description_style();
462 const int ypos = 234;
463 const FontSpecInterface::FONT_TYPE font_type
464 = FontSpecInterface::FONTSET_INFOLIST_DESCRIPTION;
465 const Size text_size(10, 20);
466 EXPECT_CALL(*testkit.text_renderer_mock,
467 GetMultiLinePixelSize(font_type, kSampleDescription, _))
468 .WillOnce(Return(text_size));
469
470 Rect bg_rect, text_rect;
471 testkit.window->GetRenderingRects(description_style, kSampleDescription,
472 font_type, ypos, &bg_rect, &text_rect);
473 FinalizeTestKit(&testkit);
474 }
475 }
476
TEST_F(InfolistWindowTest,ReloadFontConfigTest)477 TEST_F(InfolistWindowTest, ReloadFontConfigTest) {
478 InfolistWindowTestKit testkit = SetUpInfolistWindow();
479 const char kDummyFontDescription[] = "Foo,Bar,Baz";
480 EXPECT_CALL(*testkit.text_renderer_mock,
481 ReloadFontConfig(StrEq(kDummyFontDescription)));
482 testkit.window->ReloadFontConfig(kDummyFontDescription);
483 FinalizeTestKit(&testkit);
484 }
485
486 } // namespace gtk
487 } // namespace renderer
488 } // namespace mozc
489