1// Copyright 2013 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 "ui/base/ime/composition_text_util_pango.h"
6
7#include <pango/pango-attributes.h>
8
9#include <string>
10#include <utility>
11
12#include "base/basictypes.h"
13#include "base/logging.h"
14#include "testing/gtest/include/gtest/gtest.h"
15#include "ui/base/ime/composition_text.h"
16
17namespace {
18
19struct AttributeInfo {
20  int type;
21  int value;
22  int start_offset;
23  int end_offset;
24};
25
26struct Underline {
27  unsigned start_offset;
28  unsigned end_offset;
29  uint32 color;
30  bool thick;
31  uint32 background_color;
32};
33
34struct TestData {
35  const char* text;
36  const AttributeInfo attrs[10];
37  const Underline underlines[10];
38};
39
40const TestData kTestData[] = {
41    // Normal case
42    {"One Two Three",
43     {{PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 0, 3},
44      {PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_DOUBLE, 4, 7},
45      {PANGO_ATTR_BACKGROUND, 0, 4, 7},
46      {PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 8, 13},
47      {0, 0, 0, 0}},
48     {{0, 3, SK_ColorBLACK, false, SK_ColorTRANSPARENT},
49      {4, 7, SK_ColorBLACK, true, SK_ColorTRANSPARENT},
50      {8, 13, SK_ColorBLACK, false, SK_ColorTRANSPARENT},
51      {0, 0, 0, false, SK_ColorTRANSPARENT}}},
52
53    // Offset overflow.
54    {"One Two Three",
55     {{PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 0, 3},
56      {PANGO_ATTR_BACKGROUND, 0, 4, 7},
57      {PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 8, 20},
58      {0, 0, 0, 0}},
59     {{0, 3, SK_ColorBLACK, false, SK_ColorTRANSPARENT},
60      {4, 7, SK_ColorBLACK, true, SK_ColorTRANSPARENT},
61      {8, 13, SK_ColorBLACK, false, SK_ColorTRANSPARENT},
62      {0, 0, 0, false, SK_ColorTRANSPARENT}}},
63
64    // Error underline.
65    {"One Two Three",
66     {{PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 0, 3},
67      {PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_ERROR, 4, 7},
68      {PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 8, 13},
69      {0, 0, 0, 0}},
70     {{0, 3, SK_ColorBLACK, false, SK_ColorTRANSPARENT},
71      {4, 7, SK_ColorRED, false, SK_ColorTRANSPARENT},
72      {8, 13, SK_ColorBLACK, false, SK_ColorTRANSPARENT},
73      {0, 0, 0, false, SK_ColorTRANSPARENT}}},
74
75    // Default underline.
76    {"One Two Three",
77     {{0, 0, 0, 0}},
78     {{0, 13, SK_ColorBLACK, false, SK_ColorTRANSPARENT},
79      {0, 0, 0, false, SK_ColorTRANSPARENT}}},
80
81    // Unicode, including non-BMP characters: "123你好����一丁 456"
82    {"123\xE4\xBD\xA0\xE5\xA5\xBD\xF0\xA0\x80\x80\xF0\xA0\x80\x81\xE4\xB8\x80"
83     "\xE4\xB8\x81 456",
84     {{PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 0, 3},
85      {PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 3, 5},
86      {PANGO_ATTR_BACKGROUND, 0, 5, 7},
87      {PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 7, 13},
88      {0, 0, 0, 0}},
89     {{0, 3, SK_ColorBLACK, false, SK_ColorTRANSPARENT},
90      {3, 5, SK_ColorBLACK, false, SK_ColorTRANSPARENT},
91      {5, 9, SK_ColorBLACK, true, SK_ColorTRANSPARENT},
92      {9, 15, SK_ColorBLACK, false, SK_ColorTRANSPARENT},
93      {0, 0, 0, false, SK_ColorTRANSPARENT}}},
94};
95
96void CompareUnderline(const Underline& a,
97                      const ui::CompositionUnderline& b) {
98  EXPECT_EQ(a.start_offset, b.start_offset);
99  EXPECT_EQ(a.end_offset, b.end_offset);
100  EXPECT_EQ(a.color, b.color);
101  EXPECT_EQ(a.thick, b.thick);
102  EXPECT_EQ(a.background_color, b.background_color);
103}
104
105TEST(CompositionTextUtilPangoTest, ExtractCompositionText) {
106  for (size_t i = 0; i < arraysize(kTestData); ++i) {
107    const char* text = kTestData[i].text;
108    const AttributeInfo* attrs = kTestData[i].attrs;
109    SCOPED_TRACE(testing::Message() << "Testing:" << i
110                 << " text:" << text);
111
112    PangoAttrList* pango_attrs = pango_attr_list_new();
113    for (size_t a = 0; attrs[a].type; ++a) {
114      PangoAttribute* pango_attr = NULL;
115      switch (attrs[a].type) {
116        case PANGO_ATTR_UNDERLINE:
117          pango_attr = pango_attr_underline_new(
118              static_cast<PangoUnderline>(attrs[a].value));
119          break;
120        case PANGO_ATTR_BACKGROUND:
121          pango_attr = pango_attr_background_new(0, 0, 0);
122          break;
123        default:
124          NOTREACHED();
125      }
126      pango_attr->start_index =
127          g_utf8_offset_to_pointer(text, attrs[a].start_offset) - text;
128      pango_attr->end_index =
129          g_utf8_offset_to_pointer(text, attrs[a].end_offset) - text;
130      pango_attr_list_insert(pango_attrs, pango_attr);
131    }
132
133    ui::CompositionText result;
134    ui::ExtractCompositionTextFromGtkPreedit(text, pango_attrs, 0, &result);
135
136    const Underline* underlines = kTestData[i].underlines;
137    for (size_t u = 0; underlines[u].color &&
138         u < result.underlines.size(); ++u) {
139      SCOPED_TRACE(testing::Message() << "Underline:" << u);
140      CompareUnderline(underlines[u], result.underlines[u]);
141    }
142
143    pango_attr_list_unref(pango_attrs);
144  }
145}
146
147}  // namespace
148