OLD | NEW |
| (Empty) |
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 "retriever.h" | |
16 | |
17 #include <libaddressinput/callback.h> | |
18 #include <libaddressinput/downloader.h> | |
19 #include <libaddressinput/storage.h> | |
20 #include <libaddressinput/util/scoped_ptr.h> | |
21 | |
22 #include <cstddef> | |
23 #include <ctime> | |
24 #include <string> | |
25 | |
26 #include <gtest/gtest.h> | |
27 | |
28 #include "fake_downloader.h" | |
29 #include "fake_storage.h" | |
30 #include "region_data_constants.h" | |
31 #include "util/string_util.h" | |
32 | |
33 namespace i18n { | |
34 namespace addressinput { | |
35 | |
36 namespace { | |
37 | |
38 const char kKey[] = "data/CA"; | |
39 | |
40 // Empty data that the downloader can return. | |
41 const char kEmptyData[] = "{}"; | |
42 | |
43 // The MD5 checksum for kEmptyData. Retriever uses MD5 to validate data | |
44 // integrity. | |
45 const char kEmptyDataChecksum[] = "99914b932bd37a50b983c5e7c90ae93b"; | |
46 | |
47 scoped_ptr<std::string> Wrap(const std::string& data, | |
48 const std::string& checksum, | |
49 const std::string& timestamp) { | |
50 return make_scoped_ptr(new std::string( | |
51 data + "\n" + "checksum=" + checksum + "\n" + "timestamp=" + timestamp)); | |
52 } | |
53 | |
54 } // namespace | |
55 | |
56 // Tests for Retriever object. | |
57 class RetrieverTest : public testing::Test { | |
58 protected: | |
59 RetrieverTest() | |
60 : storage_(NULL), | |
61 retriever_(), | |
62 success_(false), | |
63 key_(), | |
64 data_(), | |
65 reject_empty_data_(false) { | |
66 ResetRetriever(FakeDownloader::kFakeDataUrl); | |
67 } | |
68 | |
69 virtual ~RetrieverTest() {} | |
70 | |
71 scoped_ptr<Retriever::Callback> BuildCallback() { | |
72 return ::i18n::addressinput::BuildCallback( | |
73 this, &RetrieverTest::OnDataReady); | |
74 } | |
75 | |
76 void ResetRetriever(const std::string& url) { | |
77 storage_ = new FakeStorage; | |
78 retriever_.reset( | |
79 new Retriever(url, | |
80 scoped_ptr<Downloader>(new FakeDownloader), | |
81 scoped_ptr<Storage>(storage_))); | |
82 } | |
83 | |
84 std::string GetUrlForKey(const std::string& key) { | |
85 return retriever_->GetUrlForKey(key); | |
86 } | |
87 | |
88 std::string GetKeyForUrl(const std::string& url) { | |
89 return retriever_->GetKeyForUrl(url); | |
90 } | |
91 | |
92 FakeStorage* storage_; // Owned by |retriever_|. | |
93 scoped_ptr<Retriever> retriever_; | |
94 bool success_; | |
95 std::string key_; | |
96 std::string data_; | |
97 bool reject_empty_data_; | |
98 | |
99 private: | |
100 bool OnDataReady(bool success, | |
101 const std::string& key, | |
102 const std::string& data) { | |
103 success_ = success; | |
104 key_ = key; | |
105 data_ = data; | |
106 return !reject_empty_data_ || data_ != kEmptyData; | |
107 } | |
108 }; | |
109 | |
110 TEST_F(RetrieverTest, RegionHasData) { | |
111 const std::vector<std::string>& region_codes = | |
112 RegionDataConstants::GetRegionCodes(); | |
113 for (size_t i = 0; i < region_codes.size(); ++i) { | |
114 std::string key = "data/" + region_codes[i]; | |
115 SCOPED_TRACE("For key: " + key); | |
116 | |
117 retriever_->Retrieve(key, BuildCallback()); | |
118 EXPECT_TRUE(success_); | |
119 EXPECT_EQ(key, key_); | |
120 EXPECT_FALSE(data_.empty()); | |
121 EXPECT_NE(kEmptyData, data_); | |
122 } | |
123 } | |
124 | |
125 TEST_F(RetrieverTest, RetrieveData) { | |
126 retriever_->Retrieve(kKey, BuildCallback()); | |
127 | |
128 EXPECT_TRUE(success_); | |
129 EXPECT_EQ(kKey, key_); | |
130 EXPECT_FALSE(data_.empty()); | |
131 EXPECT_NE(kEmptyData, data_); | |
132 } | |
133 | |
134 TEST_F(RetrieverTest, ReadDataFromStorage) { | |
135 retriever_->Retrieve(kKey, BuildCallback()); | |
136 retriever_->Retrieve(kKey, BuildCallback()); | |
137 | |
138 EXPECT_TRUE(success_); | |
139 EXPECT_EQ(kKey, key_); | |
140 EXPECT_FALSE(data_.empty()); | |
141 EXPECT_NE(kEmptyData, data_); | |
142 } | |
143 | |
144 TEST_F(RetrieverTest, MissingKeyReturnsEmptyData) { | |
145 static const char kMissingKey[] = "junk"; | |
146 | |
147 retriever_->Retrieve(kMissingKey, BuildCallback()); | |
148 | |
149 EXPECT_TRUE(success_); | |
150 EXPECT_EQ(kMissingKey, key_); | |
151 EXPECT_EQ(kEmptyData, data_); | |
152 } | |
153 | |
154 // The downloader that always fails. | |
155 class FaultyDownloader : public Downloader { | |
156 public: | |
157 FaultyDownloader() {} | |
158 virtual ~FaultyDownloader() {} | |
159 | |
160 // Downloader implementation. | |
161 virtual void Download(const std::string& url, | |
162 scoped_ptr<Callback> downloaded) { | |
163 (*downloaded)(false, url, make_scoped_ptr(new std::string("garbage"))); | |
164 } | |
165 }; | |
166 | |
167 TEST_F(RetrieverTest, FaultyDownloader) { | |
168 Retriever bad_retriever(FakeDownloader::kFakeDataUrl, | |
169 scoped_ptr<Downloader>(new FaultyDownloader), | |
170 scoped_ptr<Storage>(new FakeStorage)); | |
171 bad_retriever.Retrieve(kKey, BuildCallback()); | |
172 | |
173 EXPECT_FALSE(success_); | |
174 EXPECT_EQ(kKey, key_); | |
175 EXPECT_TRUE(data_.empty()); | |
176 } | |
177 | |
178 TEST_F(RetrieverTest, FaultyDownloaderFallback) { | |
179 Retriever bad_retriever(FakeDownloader::kFakeDataUrl, | |
180 scoped_ptr<Downloader>(new FaultyDownloader), | |
181 scoped_ptr<Storage>(new FakeStorage)); | |
182 const char kFallbackDataKey[] = "data/US"; | |
183 bad_retriever.Retrieve(kFallbackDataKey, BuildCallback()); | |
184 | |
185 EXPECT_TRUE(success_); | |
186 EXPECT_EQ(kFallbackDataKey, key_); | |
187 EXPECT_FALSE(data_.empty()); | |
188 EXPECT_NE(kEmptyData, data_); | |
189 } | |
190 | |
191 TEST_F(RetrieverTest, NoChecksumAndTimestampWillRedownload) { | |
192 storage_->Put(kKey, make_scoped_ptr(new std::string(kEmptyData))); | |
193 retriever_->Retrieve(kKey, BuildCallback()); | |
194 EXPECT_TRUE(success_); | |
195 EXPECT_EQ(kKey, key_); | |
196 EXPECT_FALSE(data_.empty()); | |
197 EXPECT_NE(kEmptyData, data_); | |
198 } | |
199 | |
200 TEST_F(RetrieverTest, ChecksumAndTimestampWillNotRedownload) { | |
201 storage_->Put(kKey, | |
202 Wrap(kEmptyData, kEmptyDataChecksum, TimeToString(time(NULL)))); | |
203 retriever_->Retrieve(kKey, BuildCallback()); | |
204 EXPECT_TRUE(success_); | |
205 EXPECT_EQ(kKey, key_); | |
206 EXPECT_EQ(kEmptyData, data_); | |
207 } | |
208 | |
209 TEST_F(RetrieverTest, OldTimestampWillRedownload) { | |
210 storage_->Put(kKey, Wrap(kEmptyData, kEmptyDataChecksum, "0")); | |
211 retriever_->Retrieve(kKey, BuildCallback()); | |
212 EXPECT_TRUE(success_); | |
213 EXPECT_EQ(kKey, key_); | |
214 EXPECT_FALSE(data_.empty()); | |
215 EXPECT_NE(kEmptyData, data_); | |
216 } | |
217 | |
218 TEST_F(RetrieverTest, JunkDataRedownloads) { | |
219 ResetRetriever(std::string(FakeDownloader::kFakeDataUrl)); | |
220 storage_->Put(kKey, | |
221 Wrap(kEmptyData, kEmptyDataChecksum, TimeToString(time(NULL)))); | |
222 reject_empty_data_ = true; | |
223 retriever_->Retrieve(kKey, BuildCallback()); | |
224 EXPECT_TRUE(success_); | |
225 EXPECT_EQ(kKey, key_); | |
226 EXPECT_FALSE(data_.empty()); | |
227 EXPECT_NE(kEmptyData, data_); | |
228 | |
229 // After verifying it's correct, it's saved in storage. | |
230 EXPECT_EQ(data_, storage_->SynchronousGet(kKey).substr(0, data_.size())); | |
231 } | |
232 | |
233 TEST_F(RetrieverTest, JunkDataIsntStored) { | |
234 // Data the retriever accepts is stored in |storage_|. | |
235 ResetRetriever("test:///"); | |
236 const std::string not_a_key("foobar"); | |
237 retriever_->Retrieve(not_a_key, BuildCallback()); | |
238 EXPECT_TRUE(success_); | |
239 EXPECT_EQ(not_a_key, key_); | |
240 EXPECT_FALSE(data_.empty()); | |
241 EXPECT_EQ(kEmptyData, data_); | |
242 EXPECT_EQ( | |
243 kEmptyData, | |
244 storage_->SynchronousGet(not_a_key).substr(0, sizeof kEmptyData - 1)); | |
245 | |
246 // Try again, but this time reject the data. | |
247 reject_empty_data_ = true; | |
248 ResetRetriever("test:///"); | |
249 EXPECT_EQ("", storage_->SynchronousGet(not_a_key)); | |
250 retriever_->Retrieve(not_a_key, BuildCallback()); | |
251 | |
252 // Falls back to the fallback, which doesn't have data for Canada. | |
253 EXPECT_FALSE(success_); | |
254 EXPECT_EQ(not_a_key, key_); | |
255 EXPECT_TRUE(data_.empty()); | |
256 | |
257 // Since the retriever is rejecting empty data, it shouldn't be stored. | |
258 EXPECT_EQ("", storage_->SynchronousGet(not_a_key)); | |
259 } | |
260 | |
261 TEST_F(RetrieverTest, OldTimestampOkIfDownloadFails) { | |
262 storage_ = new FakeStorage; | |
263 Retriever bad_retriever(FakeDownloader::kFakeDataUrl, | |
264 scoped_ptr<Downloader>(new FaultyDownloader), | |
265 scoped_ptr<Storage>(storage_)); | |
266 storage_->Put(kKey, Wrap(kEmptyData, kEmptyDataChecksum, "0")); | |
267 bad_retriever.Retrieve(kKey, BuildCallback()); | |
268 EXPECT_TRUE(success_); | |
269 EXPECT_EQ(kKey, key_); | |
270 EXPECT_EQ(kEmptyData, data_); | |
271 } | |
272 | |
273 TEST_F(RetrieverTest, WrongChecksumWillRedownload) { | |
274 static const char kNonEmptyData[] = "{\"non-empty\": \"data\"}"; | |
275 storage_->Put( | |
276 kKey, | |
277 Wrap(kNonEmptyData, kEmptyDataChecksum, TimeToString(time(NULL)))); | |
278 retriever_->Retrieve(kKey, BuildCallback()); | |
279 EXPECT_TRUE(success_); | |
280 EXPECT_EQ(kKey, key_); | |
281 EXPECT_FALSE(data_.empty()); | |
282 EXPECT_NE(kNonEmptyData, data_); | |
283 } | |
284 | |
285 // The downloader that doesn't get back to you. | |
286 class HangingDownloader : public Downloader { | |
287 public: | |
288 HangingDownloader() {} | |
289 virtual ~HangingDownloader() {} | |
290 | |
291 // Downloader implementation. | |
292 virtual void Download(const std::string& url, | |
293 scoped_ptr<Callback> downloaded) {} | |
294 }; | |
295 | |
296 TEST_F(RetrieverTest, RequestsDontStack) { | |
297 Retriever slow_retriever(FakeDownloader::kFakeDataUrl, | |
298 scoped_ptr<Downloader>(new HangingDownloader), | |
299 scoped_ptr<Storage>(new FakeStorage)); | |
300 | |
301 slow_retriever.Retrieve(kKey, BuildCallback()); | |
302 EXPECT_FALSE(success_); | |
303 EXPECT_TRUE(key_.empty()); | |
304 | |
305 EXPECT_NO_FATAL_FAILURE(slow_retriever.Retrieve(kKey, BuildCallback())); | |
306 } | |
307 | |
308 TEST_F(RetrieverTest, GetUrlForKey) { | |
309 ResetRetriever("test:///"); | |
310 EXPECT_EQ("test:///", GetUrlForKey("")); | |
311 EXPECT_EQ("test:///data", GetUrlForKey("data")); | |
312 EXPECT_EQ("test:///data/US", GetUrlForKey("data/US")); | |
313 EXPECT_EQ("test:///data/CA--fr", GetUrlForKey("data/CA--fr")); | |
314 } | |
315 | |
316 TEST_F(RetrieverTest, GetKeyForUrl) { | |
317 ResetRetriever("test:///"); | |
318 EXPECT_EQ("", GetKeyForUrl("test://")); | |
319 EXPECT_EQ("", GetKeyForUrl("http://www.google.com/")); | |
320 EXPECT_EQ("", GetKeyForUrl("")); | |
321 EXPECT_EQ("", GetKeyForUrl("test:///")); | |
322 EXPECT_EQ("data", GetKeyForUrl("test:///data")); | |
323 EXPECT_EQ("data/US", GetKeyForUrl("test:///data/US")); | |
324 EXPECT_EQ("data/CA--fr", GetKeyForUrl("test:///data/CA--fr")); | |
325 } | |
326 | |
327 TEST_F(RetrieverTest, NullCallbackNoCrash) { | |
328 ASSERT_NO_FATAL_FAILURE( | |
329 retriever_->Retrieve(kKey, scoped_ptr<Retriever::Callback>())); | |
330 ASSERT_NO_FATAL_FAILURE( | |
331 retriever_->Retrieve(kKey, scoped_ptr<Retriever::Callback>())); | |
332 } | |
333 | |
334 } // namespace addressinput | |
335 } // namespace i18n | |
OLD | NEW |