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