1// Copyright (C) 2014 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/supplier.h>
16
17#include <libaddressinput/address_data.h>
18#include <libaddressinput/callback.h>
19#include <libaddressinput/null_storage.h>
20#include <libaddressinput/ondemand_supplier.h>
21#include <libaddressinput/preload_supplier.h>
22#include <libaddressinput/util/basictypes.h>
23#include <libaddressinput/util/scoped_ptr.h>
24
25#include <cstddef>
26#include <cstring>
27#include <string>
28
29#include <gtest/gtest.h>
30
31#include "lookup_key.h"
32#include "rule.h"
33#include "testdata_source.h"
34
35namespace {
36
37// For compatibility with legacy compilers, that can't handle UTF-8 string
38// literals in source code (please let them disappear from common use soon),
39// Chinese strings required in the test code are here provided as string
40// constants in hex escaped UTF-8 encoding.
41
42/* "九龍" */
43const char kKowloon[] = "\xE4\xB9\x9D\xE9\xBE\x8D";
44
45/* "新疆" */
46const char kXinJiang[] = "\xE6\x96\xB0\xE7\x96\x86";
47
48/* "喀什地区" */
49const char kKashiDiqu[] = "\xE5\x96\x80\xE4\xBB\x80\xE5\x9C\xB0\xE5\x8C\xBA";
50
51/* "喀什市" */
52const char kKashiShi[] = "\xE5\x96\x80\xE4\xBB\x80\xE5\xB8\x82";
53
54using i18n::addressinput::AddressData;
55using i18n::addressinput::BuildCallback;
56using i18n::addressinput::LookupKey;
57using i18n::addressinput::NullStorage;
58using i18n::addressinput::OndemandSupplier;
59using i18n::addressinput::PreloadSupplier;
60using i18n::addressinput::Rule;
61using i18n::addressinput::scoped_ptr;
62using i18n::addressinput::Supplier;
63using i18n::addressinput::TestdataSource;
64
65class SupplierWrapper {
66 public:
67  virtual ~SupplierWrapper() {}
68  virtual void Supply(const LookupKey& lookup_key,
69                      const Supplier::Callback& supplied) = 0;
70};
71
72class OndemandSupplierWrapper : public SupplierWrapper {
73 public:
74  static SupplierWrapper* Build() { return new OndemandSupplierWrapper; }
75
76  virtual void Supply(const LookupKey& lookup_key,
77                      const Supplier::Callback& supplied) {
78    ondemand_supplier_.Supply(lookup_key, supplied);
79  }
80
81 private:
82  OndemandSupplierWrapper()
83      : ondemand_supplier_(new TestdataSource(false), new NullStorage) {}
84
85  OndemandSupplier ondemand_supplier_;
86  DISALLOW_COPY_AND_ASSIGN(OndemandSupplierWrapper);
87};
88
89class PreloadSupplierWrapper : public SupplierWrapper {
90 public:
91  static SupplierWrapper* Build() { return new PreloadSupplierWrapper; }
92
93  virtual void Supply(const LookupKey& lookup_key,
94                      const Supplier::Callback& supplied) {
95    const std::string& region_code = lookup_key.GetRegionCode();
96    if (!region_code.empty() && !preload_supplier_.IsLoaded(region_code)) {
97      preload_supplier_.LoadRules(region_code, *loaded_);
98    }
99    preload_supplier_.Supply(lookup_key, supplied);
100  }
101
102 private:
103  PreloadSupplierWrapper()
104      : preload_supplier_(new TestdataSource(true), new NullStorage),
105        loaded_(BuildCallback(this, &PreloadSupplierWrapper::Loaded)) {}
106
107  void Loaded(bool success, const std::string&, int) { ASSERT_TRUE(success); }
108
109  PreloadSupplier preload_supplier_;
110  const scoped_ptr<const PreloadSupplier::Callback> loaded_;
111  DISALLOW_COPY_AND_ASSIGN(PreloadSupplierWrapper);
112};
113
114class SupplierTest : public testing::TestWithParam<SupplierWrapper* (*)()> {
115 protected:
116  SupplierTest()
117      : address_(),
118        rule_(),
119        called_(false),
120        lookup_key_(),
121        supplier_wrapper_((*GetParam())()),
122        supplied_(BuildCallback(this, &SupplierTest::Supplied)) {}
123
124  void Supply() {
125    lookup_key_.FromAddress(address_);
126    supplier_wrapper_->Supply(lookup_key_, *supplied_);
127  }
128
129  AddressData address_;
130  const Rule* rule_[arraysize(LookupKey::kHierarchy)];
131  bool called_;
132
133 private:
134  void Supplied(bool success,
135                const LookupKey& lookup_key,
136                const Supplier::RuleHierarchy& hierarchy) {
137    ASSERT_TRUE(success);
138    ASSERT_EQ(&lookup_key_, &lookup_key);
139    std::memcpy(rule_, hierarchy.rule, sizeof rule_);
140    called_ = true;
141  }
142
143  LookupKey lookup_key_;
144  const scoped_ptr<SupplierWrapper> supplier_wrapper_;
145  const scoped_ptr<const Supplier::Callback> supplied_;
146
147  DISALLOW_COPY_AND_ASSIGN(SupplierTest);
148};
149
150INSTANTIATE_TEST_CASE_P(OndemandSupplier,
151                        SupplierTest,
152                        testing::Values(&OndemandSupplierWrapper::Build));
153
154INSTANTIATE_TEST_CASE_P(PreloadSupplier,
155                        SupplierTest,
156                        testing::Values(&PreloadSupplierWrapper::Build));
157
158TEST_P(SupplierTest, Invalid) {
159  address_.region_code = "QZ";
160
161  ASSERT_NO_FATAL_FAILURE(Supply());
162  ASSERT_TRUE(called_);
163  EXPECT_TRUE(rule_[0] == NULL);
164  EXPECT_TRUE(rule_[1] == NULL);
165  EXPECT_TRUE(rule_[2] == NULL);
166  EXPECT_TRUE(rule_[3] == NULL);
167}
168
169TEST_P(SupplierTest, Valid) {
170  address_.region_code = "SE";
171
172  ASSERT_NO_FATAL_FAILURE(Supply());
173  ASSERT_TRUE(called_);
174  EXPECT_TRUE(rule_[0] != NULL);
175  EXPECT_TRUE(rule_[1] == NULL);
176  EXPECT_TRUE(rule_[2] == NULL);
177  EXPECT_TRUE(rule_[3] == NULL);
178  EXPECT_EQ("data/SE", rule_[0]->GetId());
179  EXPECT_FALSE(rule_[0]->GetRequired().empty());
180  EXPECT_FALSE(rule_[0]->GetFormat().empty());
181  EXPECT_TRUE(rule_[0]->GetPostalCodeMatcher() != NULL);
182}
183
184TEST_P(SupplierTest, KeyDepthEqualsMaxDepth) {
185  address_.region_code = "HK";
186  address_.administrative_area = kKowloon;
187
188  ASSERT_NO_FATAL_FAILURE(Supply());
189  ASSERT_TRUE(called_);
190  EXPECT_TRUE(rule_[0] != NULL);
191  EXPECT_TRUE(rule_[1] != NULL);
192  EXPECT_TRUE(rule_[2] == NULL);
193  EXPECT_TRUE(rule_[3] == NULL);
194}
195
196TEST_P(SupplierTest, KeyDepthLargerThanMaxDepth) {
197  address_.region_code = "HK";
198  address_.administrative_area = kKowloon;
199  address_.locality = "bbb";  // Ignored!
200
201  ASSERT_NO_FATAL_FAILURE(Supply());
202  ASSERT_TRUE(called_);
203  EXPECT_TRUE(rule_[0] != NULL);
204  EXPECT_TRUE(rule_[1] != NULL);
205  EXPECT_TRUE(rule_[2] == NULL);
206  EXPECT_TRUE(rule_[3] == NULL);
207}
208
209TEST_P(SupplierTest, KeyDepthSmallerThanMaxDepth) {
210  address_.region_code = "HK";
211
212  ASSERT_NO_FATAL_FAILURE(Supply());
213  ASSERT_TRUE(called_);
214  EXPECT_TRUE(rule_[0] != NULL);
215  EXPECT_TRUE(rule_[1] == NULL);
216  EXPECT_TRUE(rule_[2] == NULL);
217  EXPECT_TRUE(rule_[3] == NULL);
218}
219
220TEST_P(SupplierTest, KeyDepth0) {
221  address_.region_code = "CN";
222
223  ASSERT_NO_FATAL_FAILURE(Supply());
224  ASSERT_TRUE(called_);
225  EXPECT_TRUE(rule_[0] != NULL);
226  EXPECT_TRUE(rule_[1] == NULL);
227  EXPECT_TRUE(rule_[2] == NULL);
228  EXPECT_TRUE(rule_[3] == NULL);
229}
230
231TEST_P(SupplierTest, KeyDepth1) {
232  address_.region_code = "CN";
233  address_.administrative_area = kXinJiang;
234
235  ASSERT_NO_FATAL_FAILURE(Supply());
236  ASSERT_TRUE(called_);
237  EXPECT_TRUE(rule_[0] != NULL);
238  EXPECT_TRUE(rule_[1] != NULL);
239  EXPECT_TRUE(rule_[2] == NULL);
240  EXPECT_TRUE(rule_[3] == NULL);
241}
242
243TEST_P(SupplierTest, KeyDepth2) {
244  address_.region_code = "CN";
245  address_.administrative_area = kXinJiang;
246  address_.locality = kKashiDiqu;
247
248  ASSERT_NO_FATAL_FAILURE(Supply());
249  ASSERT_TRUE(called_);
250  EXPECT_TRUE(rule_[0] != NULL);
251  EXPECT_TRUE(rule_[1] != NULL);
252  EXPECT_TRUE(rule_[2] != NULL);
253  EXPECT_TRUE(rule_[3] == NULL);
254}
255
256TEST_P(SupplierTest, KeyDepth3) {
257  address_.region_code = "CN";
258  address_.administrative_area = kXinJiang;
259  address_.locality = kKashiDiqu;
260  address_.dependent_locality = kKashiShi;
261
262  ASSERT_NO_FATAL_FAILURE(Supply());
263  ASSERT_TRUE(called_);
264  EXPECT_TRUE(rule_[0] != NULL);
265  EXPECT_TRUE(rule_[1] != NULL);
266  EXPECT_TRUE(rule_[2] != NULL);
267  EXPECT_TRUE(rule_[3] != NULL);
268}
269
270TEST_P(SupplierTest, RuleCache) {
271  address_.region_code = "US";
272  address_.administrative_area = "CA";
273
274  ASSERT_NO_FATAL_FAILURE(Supply());
275  ASSERT_TRUE(called_);
276  EXPECT_TRUE(rule_[0] != NULL);
277  EXPECT_TRUE(rule_[1] != NULL);
278  EXPECT_TRUE(rule_[2] == NULL);
279  EXPECT_TRUE(rule_[3] == NULL);
280
281  // Make a copy of the currently returned pointers to the Rule objects (stored
282  // in the OndemandSupplier cache) and verify that calling Supply() again with
283  // the same LookupKey returns the same pointers again (and doesn't create any
284  // new Rule objects instead).
285
286  const Rule* rule[arraysize(LookupKey::kHierarchy)];
287  std::memcpy(rule, rule_, sizeof rule);
288
289  called_ = false;
290  ASSERT_NO_FATAL_FAILURE(Supply());
291  ASSERT_TRUE(called_);
292  EXPECT_EQ(rule[0], rule_[0]);
293  EXPECT_EQ(rule[1], rule_[1]);
294  EXPECT_EQ(rule[2], rule_[2]);
295  EXPECT_EQ(rule[3], rule_[3]);
296}
297
298}  // namespace
299