Home | History | Annotate | Download | only in test
      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
    336