| 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 |