1// Copyright (C) 2013 Google Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include <libaddressinput/address_ui.h>
16
17#include <libaddressinput/address_field.h>
18#include <libaddressinput/address_ui_component.h>
19#include <libaddressinput/localization.h>
20#include <libaddressinput/util/basictypes.h>
21
22#include <set>
23#include <string>
24#include <vector>
25
26#include <gtest/gtest.h>
27
28namespace {
29
30using i18n::addressinput::AddressField;
31using i18n::addressinput::AddressUiComponent;
32using i18n::addressinput::BuildComponents;
33using i18n::addressinput::GetRegionCodes;
34using i18n::addressinput::Localization;
35
36using i18n::addressinput::COUNTRY;
37using i18n::addressinput::ADMIN_AREA;
38using i18n::addressinput::POSTAL_CODE;
39using i18n::addressinput::STREET_ADDRESS;
40using i18n::addressinput::ORGANIZATION;
41using i18n::addressinput::RECIPIENT;
42
43static const char kUiLanguageTag[] = "en";
44
45// Returns testing::AssertionSuccess if the |components| are valid. Uses
46// |region_code| in test failure messages.
47testing::AssertionResult ComponentsAreValid(
48    const std::vector<AddressUiComponent>& components) {
49  if (components.empty()) {
50    return testing::AssertionFailure() << "no components";
51  }
52
53  for (std::vector<AddressUiComponent>::const_iterator
54       component_it = components.begin();
55       component_it != components.end(); ++component_it) {
56    static const AddressField kMinAddressField = COUNTRY;
57    static const AddressField kMaxAddressField = RECIPIENT;
58    if (component_it->field < kMinAddressField ||
59        component_it->field > kMaxAddressField) {
60      return testing::AssertionFailure() << "unexpected field "
61                                         << component_it->field;
62    }
63
64    if (component_it->name.empty()) {
65      return testing::AssertionFailure() << "empty field name for field "
66                                         << component_it->field;
67    }
68  }
69
70  return testing::AssertionSuccess();
71}
72
73// Tests for address UI functions.
74class AddressUiTest : public testing::TestWithParam<std::string> {
75 protected:
76  AddressUiTest() {}
77  Localization localization_;
78  std::string best_address_language_tag_;
79
80 private:
81  DISALLOW_COPY_AND_ASSIGN(AddressUiTest);
82};
83
84// Verifies that a region code consists of two characters, for example "TW".
85TEST_P(AddressUiTest, RegionCodeHasTwoCharacters) {
86  EXPECT_EQ(2, GetParam().size());
87}
88
89// Verifies that BuildComponents() returns valid UI components for a region
90// code.
91TEST_P(AddressUiTest, ComponentsAreValid) {
92  EXPECT_TRUE(ComponentsAreValid(BuildComponents(
93      GetParam(), localization_, kUiLanguageTag, &best_address_language_tag_)));
94}
95
96// Verifies that BuildComponents() returns at most one input field of each type.
97TEST_P(AddressUiTest, UniqueFieldTypes) {
98  std::set<AddressField> fields;
99  const std::vector<AddressUiComponent>& components =
100      BuildComponents(GetParam(), localization_, kUiLanguageTag,
101                      &best_address_language_tag_);
102  for (std::vector<AddressUiComponent>::const_iterator it = components.begin();
103       it != components.end(); ++it) {
104    EXPECT_TRUE(fields.insert(it->field).second);
105  }
106}
107
108// Test all regions codes.
109INSTANTIATE_TEST_CASE_P(
110    AllRegions, AddressUiTest,
111    testing::ValuesIn(GetRegionCodes()));
112
113// Verifies that BuildComponents() returns an empty vector for an invalid region
114// code.
115TEST_F(AddressUiTest, InvalidRegionCodeReturnsEmptyVector) {
116  EXPECT_TRUE(BuildComponents(
117      "INVALID-REGION-CODE", localization_, kUiLanguageTag,
118      &best_address_language_tag_).empty());
119}
120
121// Test data for determining the best language tag and whether the right format
122// pattern was used (fmt vs lfmt).
123struct LanguageTestCase {
124  LanguageTestCase(const std::string& region_code,
125                   const std::string& ui_language_tag,
126                   const std::string& expected_best_address_language_tag,
127                   AddressField expected_first_field)
128      : region_code(region_code),
129        ui_language_tag(ui_language_tag),
130        expected_best_address_language_tag(expected_best_address_language_tag),
131        expected_first_field(expected_first_field) {}
132
133  ~LanguageTestCase() {}
134
135  // The CLDR region code to test.
136  const std::string region_code;
137
138  // The BCP 47 language tag to test.
139  const std::string ui_language_tag;
140
141  // The expected value for the best language tag returned by BuildComponents().
142  const std::string expected_best_address_language_tag;
143
144  // The first field expected to be returned from BuildComponents(). Useful for
145  // determining whether the returned format is in Latin or default order.
146  const AddressField expected_first_field;
147};
148
149class BestAddressLanguageTagTest
150    : public testing::TestWithParam<LanguageTestCase> {
151 protected:
152  BestAddressLanguageTagTest() {}
153  Localization localization_;
154  std::string best_address_language_tag_;
155
156 private:
157  DISALLOW_COPY_AND_ASSIGN(BestAddressLanguageTagTest);
158};
159
160std::string GetterStub(int) { return std::string(); }
161
162TEST_P(BestAddressLanguageTagTest, CorrectBestAddressLanguageTag) {
163  localization_.SetGetter(&GetterStub);
164  const std::vector<AddressUiComponent>& components = BuildComponents(
165      GetParam().region_code, localization_, GetParam().ui_language_tag,
166      &best_address_language_tag_);
167  EXPECT_EQ(GetParam().expected_best_address_language_tag,
168            best_address_language_tag_);
169  ASSERT_FALSE(components.empty());
170  EXPECT_EQ(GetParam().expected_first_field, components.front().field);
171}
172
173INSTANTIATE_TEST_CASE_P(
174    LanguageTestCases, BestAddressLanguageTagTest,
175    testing::Values(
176        // Armenia supports hy and has a Latin format.
177        LanguageTestCase("AM", "", "hy", RECIPIENT),
178        LanguageTestCase("AM", "hy", "hy", RECIPIENT),
179        LanguageTestCase("AM", "en", "hy-Latn", RECIPIENT),
180
181        // P.R. China supports zh-Hans and has a Latin format.
182        LanguageTestCase("CN", "zh-hans", "zh-Hans", POSTAL_CODE),
183        LanguageTestCase("CN", "zh-hant", "zh-Hans", POSTAL_CODE),
184        LanguageTestCase("CN", "zh-hans-CN", "zh-Hans", POSTAL_CODE),
185        LanguageTestCase("CN", "zh", "zh-Hans", POSTAL_CODE),
186        LanguageTestCase("CN", "ZH_HANS", "zh-Hans", POSTAL_CODE),
187        LanguageTestCase("CN", "zh-cmn-Hans-CN", "zh-Hans", POSTAL_CODE),
188        LanguageTestCase("CN", "zh-Latn", "zh-Latn", RECIPIENT),
189        LanguageTestCase("CN", "zh-latn-CN", "zh-Latn", RECIPIENT),
190        LanguageTestCase("CN", "en", "zh-Latn", RECIPIENT),
191        LanguageTestCase("CN", "ja", "zh-Latn", RECIPIENT),
192        LanguageTestCase("CN", "ko", "zh-Latn", RECIPIENT),
193        LanguageTestCase("CN", "ZH_LATN", "zh-Latn", RECIPIENT),
194        // Libaddressinput does not have information about extended language
195        // subtags, so it uses the zh-Latn language tag for all base languages
196        // that are not zh, even if it's effectively the same language.
197        // Mandarin Chinese, Simplified script, as used in China:
198        LanguageTestCase("CN", "cmn-Hans-CN", "zh-Latn", RECIPIENT),
199
200        // Hong Kong supports zh-Hant and en. It has a Latin format.
201        LanguageTestCase("HK", "zh", "zh-Hant", ADMIN_AREA),
202        LanguageTestCase("HK", "zh-hans", "zh-Hant", ADMIN_AREA),
203        LanguageTestCase("HK", "zh-hant", "zh-Hant", ADMIN_AREA),
204        LanguageTestCase("HK", "zh-yue-HK", "zh-Hant", ADMIN_AREA),
205        LanguageTestCase("HK", "en", "en", ADMIN_AREA),
206        LanguageTestCase("HK", "zh-latn", "zh-Latn", RECIPIENT),
207        LanguageTestCase("HK", "fr", "zh-Latn", RECIPIENT),
208        LanguageTestCase("HK", "ja", "zh-Latn", RECIPIENT),
209        LanguageTestCase("HK", "ko", "zh-Latn", RECIPIENT),
210        // Libaddressinput does not have information about extended language
211        // subtags, so it uses the zh-Latn language tag for all base languages
212        // that are not zh or en, even if it's effectively the same language.
213        // Cantonese Chinese, as used in Hong Kong:
214        LanguageTestCase("HK", "yue-HK", "zh-Latn", RECIPIENT),
215
216        // Macao supports zh-Hant and pt. It has a Latin format.
217        LanguageTestCase("MO", "zh", "zh-Hant", STREET_ADDRESS),
218        LanguageTestCase("MO", "zh-Hant", "zh-Hant", STREET_ADDRESS),
219        LanguageTestCase("MO", "pt", "pt", STREET_ADDRESS),
220        LanguageTestCase("MO", "zh-Latn", "zh-Latn", RECIPIENT),
221        LanguageTestCase("MO", "en", "zh-Latn", RECIPIENT),
222
223        // Switzerland supports de, fr, and it.
224        LanguageTestCase("CH", "de", "de", ORGANIZATION),
225        LanguageTestCase("CH", "de-DE", "de", ORGANIZATION),
226        LanguageTestCase("CH", "de-Latn-DE", "de", ORGANIZATION),
227        LanguageTestCase("CH", "fr", "fr", ORGANIZATION),
228        LanguageTestCase("CH", "it", "it", ORGANIZATION),
229        LanguageTestCase("CH", "en", "de", ORGANIZATION),
230
231        // Antarctica does not have language information.
232        LanguageTestCase("AQ", "en", "en", RECIPIENT),
233        LanguageTestCase("AQ", "fr", "fr", RECIPIENT),
234        LanguageTestCase("AQ", "es", "es", RECIPIENT),
235        LanguageTestCase("AQ", "zh-Hans", "zh-Hans", RECIPIENT),
236
237        // Egypt supports ar and has a Latin format.
238        LanguageTestCase("EG", "ar", "ar", RECIPIENT),
239        LanguageTestCase("EG", "ar-Arab", "ar", RECIPIENT),
240        LanguageTestCase("EG", "ar-Latn", "ar-Latn", RECIPIENT),
241        LanguageTestCase("EG", "fr", "ar-Latn", RECIPIENT),
242        LanguageTestCase("EG", "fa", "ar-Latn", RECIPIENT),
243        // Libaddressinput does not have language-to-script mapping, so it uses
244        // the ar-Latn language tag for all base languages that are not ar, even
245        // if the script is the same.
246        LanguageTestCase("EG", "fa-Arab", "ar-Latn", RECIPIENT)));
247
248}  // namespace
249