diff options
author | Torne (Richard Coles) <torne@google.com> | 2014-08-19 13:00:47 +0100 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2014-08-19 13:00:47 +0100 |
commit | 495e03c1b62b0b9471886d826c706deb97335436 (patch) | |
tree | 2dca327834ceae87288326087f07a927df771938 | |
parent | 731cb1d936e966db91f45d7d33cdf81a5e50afa9 (diff) | |
parent | 00190cd203186aaf923e88df1acd8ce27f208203 (diff) | |
download | src-495e03c1b62b0b9471886d826c706deb97335436.tar.gz |
Merge from Chromium at DEPS revision 290040android-wear-5.1.1_r1android-wear-5.1.0_r1
This commit was generated by merge_to_master.py.
Change-Id: I88f4d9af967904aba0b4c5c2c70920610c0ce790
39 files changed, 822 insertions, 869 deletions
diff --git a/cpp/include/libaddressinput/address_validator.h b/cpp/include/libaddressinput/address_validator.h index d3a09f3..cdb1edf 100644 --- a/cpp/include/libaddressinput/address_validator.h +++ b/cpp/include/libaddressinput/address_validator.h @@ -38,7 +38,8 @@ typedef std::multimap<AddressField, AddressProblem> FieldProblemMap; // class MyClass { // public: // MyClass() -// : validator_(kMyServerUrl, new MyDownloader, new MyStorage), +// : supplier_(new MySupplier), +// validator_(new AddressValidator(supplier_.get())), // validated_(BuildCallback(this, &MyClass::Validated)) {} // // virtual ~MyClass() {} @@ -61,7 +62,8 @@ typedef std::multimap<AddressField, AddressProblem> FieldProblemMap; // AddressData address_; // FieldProblemMap filter_; // FieldProblemMap problems_; -// const AddressValidator validator_; +// const scoped_ptr<Supplier> supplier_; +// const scoped_ptr<AddressValidator> validator_; // const scoped_ptr<const AddressValidator::Callback> validated_; // }; class AddressValidator { diff --git a/cpp/include/libaddressinput/ondemand_supplier.h b/cpp/include/libaddressinput/ondemand_supplier.h index 0fe33f8..6212d34 100644 --- a/cpp/include/libaddressinput/ondemand_supplier.h +++ b/cpp/include/libaddressinput/ondemand_supplier.h @@ -26,10 +26,10 @@ namespace i18n { namespace addressinput { -class Downloader; class LookupKey; class Retriever; class Rule; +class Source; class Storage; // An implementation of the Supplier interface that owns a Retriever object, @@ -46,14 +46,8 @@ class Storage; // in total less than 2 MB of JSON data.) class OndemandSupplier : public Supplier { public: - // Takes ownership of |downloader| and |storage|. The |validation_data_url| - // should be a URL to an address data server that |downloader| can access. - // - // (See the documentation for the Downloader implementation used for - // information about what URLs are useable with that Downloader.) - OndemandSupplier(const std::string& validation_data_url, - const Downloader* downloader, - Storage* storage); + // Takes ownership of |source| and |storage|. + OndemandSupplier(const Source* source, Storage* storage); virtual ~OndemandSupplier(); // Loads the metadata needed for |lookup_key|, then calls |supplied|. diff --git a/cpp/include/libaddressinput/preload_supplier.h b/cpp/include/libaddressinput/preload_supplier.h index 740b4d0..5987c79 100644 --- a/cpp/include/libaddressinput/preload_supplier.h +++ b/cpp/include/libaddressinput/preload_supplier.h @@ -20,6 +20,7 @@ #include <libaddressinput/util/basictypes.h> #include <libaddressinput/util/scoped_ptr.h> +#include <map> #include <set> #include <string> #include <vector> @@ -27,11 +28,11 @@ namespace i18n { namespace addressinput { -class Downloader; class IndexMap; class LookupKey; class Retriever; class Rule; +class Source; class Storage; // An implementation of the Supplier interface that owns a Retriever object, @@ -41,8 +42,8 @@ class Storage; // or in progress of being loaded. // // When using a PreloadSupplier, it becomes possible to do synchronous address -// validation using an asynchronous Downloader, and to have full control over -// when network access is being done. +// validation using an asynchronous Source, and to have full control over when +// network access is being done. // // The maximum size of this cache is naturally limited to the amount of data // available from the data server. (Currently this is less than 12,000 items of @@ -51,15 +52,8 @@ class PreloadSupplier : public Supplier { public: typedef i18n::addressinput::Callback<const std::string&, int> Callback; - // Takes ownership of |downloader| and |storage|. The |validation_data_url| - // should be a URL to a service that returns address metadata aggregated per - // region, and which |downloader| can access. - // - // (See the documentation for the Downloader implementation used for - // information about what URLs are useable with that Downloader.) - PreloadSupplier(const std::string& validation_data_url, - const Downloader* downloader, - Storage* storage); + // Takes ownership of |source| and |storage|. + PreloadSupplier(const Source* source, Storage* storage); virtual ~PreloadSupplier(); // Collects the metadata needed for |lookup_key| from the cache, then calls @@ -80,6 +74,11 @@ class PreloadSupplier : public Supplier { // Calls |loaded| when the loading has finished. void LoadRules(const std::string& region_code, const Callback& loaded); + // Returns a mapping of lookup keys to rules. Should be called only when + // IsLoaded() returns true for the |region_code|. + const std::map<std::string, const Rule*>& GetRulesForRegion( + const std::string& region_code) const; + bool IsLoaded(const std::string& region_code) const; bool IsPending(const std::string& region_code) const; @@ -93,6 +92,7 @@ class PreloadSupplier : public Supplier { std::set<std::string> pending_; const scoped_ptr<IndexMap> rule_index_; std::vector<const Rule*> rule_storage_; + std::map<std::string, std::map<std::string, const Rule*> > region_rules_; DISALLOW_COPY_AND_ASSIGN(PreloadSupplier); }; diff --git a/cpp/include/libaddressinput/downloader.h b/cpp/include/libaddressinput/source.h index 0da4e1f..4d91b68 100644 --- a/cpp/include/libaddressinput/downloader.h +++ b/cpp/include/libaddressinput/source.h @@ -12,11 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// The interface to be implemented by the user of the library to enable -// downloading validation rules from a server. +// The interface to be implemented by the user of the library to access address +// metadata, typically by downloading this from the address metadata server or +// by linking the metadata into the binary. -#ifndef I18N_ADDRESSINPUT_DOWNLOADER_H_ -#define I18N_ADDRESSINPUT_DOWNLOADER_H_ +#ifndef I18N_ADDRESSINPUT_SOURCE_H_ +#define I18N_ADDRESSINPUT_SOURCE_H_ #include <libaddressinput/callback.h> @@ -25,31 +26,31 @@ namespace i18n { namespace addressinput { -// Downloads validation rules from the server. The downloaded data must be -// allocated on the heap, passing ownership to the callback. Sample usage: +// Gets address metadata. The callback data must be allocated on the heap, +// passing ownership to the callback. Sample usage: // -// class MyDownloader : public Downloader { +// class MySource : public Source { // public: -// virtual void Download(const std::string& url, -// const Callback& downloaded) const { +// virtual void Get(const std::string& key, +// const Callback& data_ready) const { // bool success = ... // std::string* data = new ... -// downloaded(success, url, data); +// data_ready(success, key, data); // } // }; -class Downloader { +class Source { public: typedef i18n::addressinput::Callback<const std::string&, std::string*> Callback; - virtual ~Downloader() {} + virtual ~Source() {} - // Downloads |url| and invokes the |downloaded| callback. - virtual void Download(const std::string& url, - const Callback& downloaded) const = 0; + // Gets metadata for |key| and invokes the |data_ready| callback. + virtual void Get(const std::string& key, + const Callback& data_ready) const = 0; }; } // namespace addressinput } // namespace i18n -#endif // I18N_ADDRESSINPUT_DOWNLOADER_H_ +#endif // I18N_ADDRESSINPUT_SOURCE_H_ diff --git a/cpp/include/libaddressinput/storage.h b/cpp/include/libaddressinput/storage.h index 38fb887..94ad13b 100644 --- a/cpp/include/libaddressinput/storage.h +++ b/cpp/include/libaddressinput/storage.h @@ -13,7 +13,7 @@ // limitations under the License. // // The interface to be implemented by the user of the library to enable storing -// the downloaded validation rules (e.g. on disk). +// address metadata (e.g. on disk). #ifndef I18N_ADDRESSINPUT_STORAGE_H_ #define I18N_ADDRESSINPUT_STORAGE_H_ @@ -25,8 +25,8 @@ namespace i18n { namespace addressinput { -// Stores downloaded validation rules. The data must be allocated on the heap, -// passing ownership to the called function. Sample usage: +// Stores address metadata. The data must be allocated on the heap, passing +// ownership to the called function. Sample usage: // // class MyStorage : public Storage { // public: diff --git a/cpp/libaddressinput.gypi b/cpp/libaddressinput.gypi index 03ecaf9..e36ee95 100644 --- a/cpp/libaddressinput.gypi +++ b/cpp/libaddressinput.gypi @@ -28,7 +28,6 @@ 'src/language.cc', 'src/localization.cc', 'src/lookup_key.cc', - 'src/lookup_key_util.cc', 'src/null_storage.cc', 'src/ondemand_supplier.cc', 'src/ondemand_supply_task.cc', @@ -61,16 +60,13 @@ 'test/address_problem_test.cc', 'test/address_ui_test.cc', 'test/address_validator_test.cc', - 'test/fake_downloader.cc', - 'test/fake_downloader_test.cc', 'test/fake_storage.cc', 'test/fake_storage_test.cc', 'test/format_element_test.cc', 'test/language_test.cc', 'test/localization_test.cc', 'test/lookup_key_test.cc', - 'test/lookup_key_util_test.cc', - 'test/mock_downloader.cc', + 'test/mock_source.cc', 'test/null_storage_test.cc', 'test/ondemand_supply_task_test.cc', 'test/post_box_matchers_test.cc', @@ -82,6 +78,8 @@ 'test/rule_retriever_test.cc', 'test/rule_test.cc', 'test/supplier_test.cc', + 'test/testdata_source.cc', + 'test/testdata_source_test.cc', 'test/util/json_test.cc', 'test/util/md5_unittest.cc', 'test/util/scoped_ptr_unittest.cc', diff --git a/cpp/src/address_formatter.cc b/cpp/src/address_formatter.cc index b866b8d..b54cbac 100644 --- a/cpp/src/address_formatter.cc +++ b/cpp/src/address_formatter.cc @@ -100,10 +100,10 @@ std::string GetLineSeparatorForLanguage(const std::string& language_tag) { arraysize(kLanguagesThatUseAnArabicComma)) { return kArabicCommaSeparator; } - // Either the language is a latin-script language, or no language was + // Either the language is a Latin-script language, or no language was // specified. In the latter case we still return ", " as the most common // separator in use. In countries that don't use this, e.g. Thailand, - // addresses are often written in latin script where this would still be + // addresses are often written in Latin script where this would still be // appropriate, so this is a reasonable default in the absence of information. return kCommaSeparator; } @@ -139,38 +139,73 @@ void GetFormattedNationalAddress( Language language(address_data.language_code); - // If latinized rules are available and the |language_code| of this address is - // explicitly tagged as being Latin, then use the latinized formatting rules. + // If Latin-script rules are available and the |language_code| of this address + // is explicitly tagged as being Latin, then use the Latin-script formatting + // rules. const std::vector<FormatElement>& format = language.has_latin_script && !rule.GetLatinFormat().empty() ? rule.GetLatinFormat() : rule.GetFormat(); + // Address format without the unnecessary elements (based on which address + // fields are empty). We assume all literal strings that are not at the start + // or end of a line are separators, and therefore only relevant if the + // surrounding fields are filled in. This works with the data we have + // currently. + std::vector<FormatElement> pruned_format; + for (std::vector<FormatElement>::const_iterator + element_it = format.begin(); + element_it != format.end(); + ++element_it) { + // Always keep the newlines. + if (element_it->IsNewline() || + // Always keep the non-empty address fields. + (element_it->IsField() && + !address_data.IsFieldEmpty(element_it->GetField())) || + // Only keep literals that satisfy these 2 conditions: + (!element_it->IsField() && + // (1) Not preceding an empty field. + (element_it + 1 == format.end() || + !(element_it + 1)->IsField() || + !address_data.IsFieldEmpty((element_it + 1)->GetField())) && + // (2) Not following a removed field. + (element_it == format.begin() || + !(element_it - 1)->IsField() || + (!pruned_format.empty() && pruned_format.back().IsField())))) { + pruned_format.push_back(*element_it); + } + } + std::string line; - for (size_t i = 0; i < format.size(); ++i) { - FormatElement element = format[i]; - if (element.IsNewline()) { + for (std::vector<FormatElement>::const_iterator + element_it = pruned_format.begin(); + element_it != pruned_format.end(); + ++element_it) { + if (element_it->IsNewline()) { if (!line.empty()) { lines->push_back(line); line.clear(); } - } else if (element.IsField()) { - AddressField field = element.GetField(); + } else if (element_it->IsField()) { + AddressField field = element_it->GetField(); if (field == STREET_ADDRESS) { // The field "street address" represents the street address lines of an // address, so there can be multiple values. - if (!line.empty()) { - lines->push_back(line); - line.clear(); + if (!address_data.IsFieldEmpty(field)) { + line.append(address_data.address_line.front()); + if (address_data.address_line.size() > 1U) { + lines->push_back(line); + line.clear(); + lines->insert(lines->end(), + address_data.address_line.begin() + 1, + address_data.address_line.end()); + } } - lines->insert(lines->end(), - address_data.address_line.begin(), - address_data.address_line.end()); } else { line.append(address_data.GetFieldValue(field)); } } else { - line.append(element.GetLiteral()); + line.append(element_it->GetLiteral()); } } if (!line.empty()) { diff --git a/cpp/src/lookup_key_util.cc b/cpp/src/lookup_key_util.cc deleted file mode 100644 index e63e9a5..0000000 --- a/cpp/src/lookup_key_util.cc +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (C) 2013 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "lookup_key_util.h" - -#include <cassert> -#include <string> - -namespace i18n { -namespace addressinput { - -LookupKeyUtil::LookupKeyUtil(const std::string& validation_data_url) - : validation_data_url_(validation_data_url) { - assert(validation_data_url_.length() > 0); - assert(validation_data_url_[validation_data_url_.length() - 1] == '/'); -} - -LookupKeyUtil::~LookupKeyUtil() {} - -std::string LookupKeyUtil::GetUrlForKey(const std::string& key) const { - return validation_data_url_ + key; -} - -std::string LookupKeyUtil::GetKeyForUrl(const std::string& url) const { - return IsValidationDataUrl(url) ? url.substr(validation_data_url_.length()) - : std::string(); -} - -bool LookupKeyUtil::IsValidationDataUrl(const std::string& url) const { - return - url.compare(0, validation_data_url_.length(), validation_data_url_) == 0; -} - -} // namespace addressinput -} // namespace i18n diff --git a/cpp/src/lookup_key_util.h b/cpp/src/lookup_key_util.h deleted file mode 100644 index 4996c1c..0000000 --- a/cpp/src/lookup_key_util.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (C) 2013 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Functions for working with lookup keys. The lookup keys are strings that -// identify serialized validation rules. - -#ifndef I18N_ADDRESSINPUT_LOOKUP_KEY_UTIL_H_ -#define I18N_ADDRESSINPUT_LOOKUP_KEY_UTIL_H_ - -#include <string> - -namespace i18n { -namespace addressinput { - -// Utility functions for lookup keys. Sample usage: -// LookupKeyUtil lookup_keys("https://i18napis.appspot.com/ssl-address/"); -// Download(lookup_keys.GetUrlForKey("data/US")); -class LookupKeyUtil { - public: - // Builds a lookup key utility for the |validation_data_url| parameter. The - // parameter must end with a '/'. - explicit LookupKeyUtil(const std::string& validation_data_url); - ~LookupKeyUtil(); - - // Returns the URL where the |key| can be retrieved. For example, returns - // "https://i18napis.appspot.com/ssl-address/data/US" for input "data/US". - // Assumes that the input string is a valid URL segment. - std::string GetUrlForKey(const std::string& key) const; - - // Returns the key for the |url|. For example, returns "data/US" for - // "https://i18napis.appspot.com/ssl-address/data/US". If the |url| does not - // start with |validation_data_url| that was passed to the constructor, then - // returns an empty string. (This can happen if the user of the library - // returns a bad URL in their Downloader implementation.) - std::string GetKeyForUrl(const std::string& url) const; - - // Returns true if the |url| starts with |validation_data_url| that was passed - // to the constructor. - bool IsValidationDataUrl(const std::string& url) const; - - private: - const std::string validation_data_url_; -}; - -} // namespace addressinput -} // namespace i18n - -#endif // I18N_ADDRESSINPUT_LOOKUP_KEY_UTIL_H_ diff --git a/cpp/src/ondemand_supplier.cc b/cpp/src/ondemand_supplier.cc index dddeae5..fa3f88e 100644 --- a/cpp/src/ondemand_supplier.cc +++ b/cpp/src/ondemand_supplier.cc @@ -28,10 +28,8 @@ namespace i18n { namespace addressinput { -OndemandSupplier::OndemandSupplier(const std::string& validation_data_url, - const Downloader* downloader, - Storage* storage) - : retriever_(new Retriever(validation_data_url, downloader, storage)) { +OndemandSupplier::OndemandSupplier(const Source* source, Storage* storage) + : retriever_(new Retriever(source, storage)) { } OndemandSupplier::~OndemandSupplier() { diff --git a/cpp/src/ondemand_supply_task.cc b/cpp/src/ondemand_supply_task.cc index 64568b3..f46d475 100644 --- a/cpp/src/ondemand_supply_task.cc +++ b/cpp/src/ondemand_supply_task.cc @@ -81,21 +81,20 @@ void OndemandSupplyTask::Retrieve(const Retriever& retriever) { void OndemandSupplyTask::Load(bool success, const std::string& key, const std::string& data) { + size_t depth = std::count(key.begin(), key.end(), '/') - 1; + assert(depth < arraysize(LookupKey::kHierarchy)); + // Sanity check: This key should be present in the set of pending requests. size_t status = pending_.erase(key); assert(status == 1); // There will always be one item erased from the set. (void)status; // Prevent unused variable if assert() is optimized away. - size_t depth = std::count(key.begin(), key.end(), '/') - 1; - assert(depth < arraysize(LookupKey::kHierarchy)); - AddressField field = LookupKey::kHierarchy[depth]; - if (success) { // The address metadata server will return the empty JSON "{}" when it // successfully performed a lookup, but didn't find any data for that key. if (data != "{}") { Rule* rule = new Rule; - if (field == COUNTRY) { + if (LookupKey::kHierarchy[depth] == COUNTRY) { // All rules on the COUNTRY level inherit from the default rule. rule->CopyFrom(Rule::GetDefault()); } diff --git a/cpp/src/preload_supplier.cc b/cpp/src/preload_supplier.cc index 3a9cdc3..79119bd 100644 --- a/cpp/src/preload_supplier.cc +++ b/cpp/src/preload_supplier.cc @@ -72,16 +72,19 @@ class Helper { const Retriever& retriever, std::set<std::string>* pending, IndexMap* rule_index, - std::vector<const Rule*>* rule_storage) + std::vector<const Rule*>* rule_storage, + std::map<std::string, const Rule*>* region_rules) : region_code_(region_code), loaded_(loaded), pending_(pending), rule_index_(rule_index), rule_storage_(rule_storage), + region_rules_(region_rules), retrieved_(BuildCallback(this, &Helper::OnRetrieved)) { assert(pending_ != NULL); assert(rule_index_ != NULL); assert(rule_storage_ != NULL); + assert(region_rules_ != NULL); assert(retrieved_ != NULL); pending_->insert(key); retriever.Retrieve(key, *retrieved_); @@ -103,6 +106,14 @@ class Helper { std::string id; std::vector<const Rule*> sub_rules; + IndexMap::iterator last_index_it = rule_index_->end(); + IndexMap::iterator last_latin_it = rule_index_->end(); + std::map<std::string, const Rule*>::iterator last_region_it = + region_rules_->end(); + + IndexMap::const_iterator hints[arraysize(LookupKey::kHierarchy) - 1]; + std::fill(hints, hints + arraysize(hints), rule_index_->end()); + if (!success) { goto callback; } @@ -112,19 +123,17 @@ class Helper { goto callback; } - for (std::vector<std::string>::const_iterator - it = json.GetKeys().begin(); it != json.GetKeys().end(); ++it) { - if (!json.HasDictionaryValueForKey(*it)) { - success = false; - goto callback; - } - const Json& value = json.GetDictionaryValueForKey(*it); - - if (!value.GetStringValueForKey("id", &id)) { + for (std::vector<const Json*>::const_iterator + it = json.GetSubDictionaries().begin(); + it != json.GetSubDictionaries().end(); + ++it) { + const Json* value = *it; + assert(value != NULL); + if (!value->GetStringValueForKey("id", &id)) { success = false; goto callback; } - assert(*it == id); // Sanity check. + assert(!id.empty()); size_t depth = std::count(id.begin(), id.end(), '/') - 1; assert(depth < arraysize(LookupKey::kHierarchy)); @@ -135,7 +144,7 @@ class Helper { // All rules on the COUNTRY level inherit from the default rule. rule->CopyFrom(Rule::GetDefault()); } - rule->ParseJsonRule(value); + rule->ParseJsonRule(*value); assert(id == rule->GetId()); // Sanity check. rule_storage_->push_back(rule); @@ -143,11 +152,15 @@ class Helper { sub_rules.push_back(rule); } - // Add the ID of this Rule object to the rule index. - std::pair<IndexMap::iterator, bool> result = - rule_index_->insert(std::make_pair(id, rule)); - assert(result.second); - (void)result; // Prevent unused variable if assert() is optimized away. + // Add the ID of this Rule object to the rule index with natural string + // comparison for keys. + last_index_it = + rule_index_->insert(last_index_it, std::make_pair(id, rule)); + + // Add the ID of this Rule object to the region-specific rule index with + // exact string comparison for keys. + last_region_it = + region_rules_->insert(last_region_it, std::make_pair(id, rule)); ++rule_count; } @@ -179,9 +192,12 @@ class Helper { } parent_id.resize(pos); - IndexMap::const_iterator jt = rule_index_->find(parent_id); - assert(jt != rule_index_->end()); - hierarchy.push(jt->second); + IndexMap::const_iterator* const hint = &hints[hierarchy.size() - 1]; + if (*hint == rule_index_->end() || (*hint)->first != parent_id) { + *hint = rule_index_->find(parent_id); + } + assert(*hint != rule_index_->end()); + hierarchy.push((*hint)->second); } std::string human_id((*it)->GetId().substr(0, sizeof "data/ZZ" - 1)); @@ -217,13 +233,15 @@ class Helper { } } - rule_index_->insert(std::make_pair(human_id, *it)); + last_index_it = + rule_index_->insert(last_index_it, std::make_pair(human_id, *it)); // Add the Latin script ID, if a Latin script name could be found for // every part of the ID. if (std::count(human_id.begin(), human_id.end(), '/') == std::count(latin_id.begin(), latin_id.end(), '/')) { - rule_index_->insert(std::make_pair(latin_id, *it)); + last_latin_it = + rule_index_->insert(last_latin_it, std::make_pair(latin_id, *it)); } } @@ -237,6 +255,7 @@ class Helper { std::set<std::string>* const pending_; IndexMap* const rule_index_; std::vector<const Rule*>* const rule_storage_; + std::map<std::string, const Rule*>* const region_rules_; const scoped_ptr<const Retriever::Callback> retrieved_; DISALLOW_COPY_AND_ASSIGN(Helper); @@ -252,13 +271,12 @@ std::string KeyFromRegionCode(const std::string& region_code) { } // namespace -PreloadSupplier::PreloadSupplier(const std::string& validation_data_url, - const Downloader* downloader, - Storage* storage) - : retriever_(new Retriever(validation_data_url, downloader, storage)), +PreloadSupplier::PreloadSupplier(const Source* source, Storage* storage) + : retriever_(new Retriever(source, storage)), pending_(), rule_index_(new IndexMap), - rule_storage_() {} + rule_storage_(), + region_rules_() {} PreloadSupplier::~PreloadSupplier() { for (std::vector<const Rule*>::const_iterator @@ -303,7 +321,14 @@ void PreloadSupplier::LoadRules(const std::string& region_code, *retriever_, &pending_, rule_index_.get(), - &rule_storage_); + &rule_storage_, + ®ion_rules_[region_code]); +} + +const std::map<std::string, const Rule*>& PreloadSupplier::GetRulesForRegion( + const std::string& region_code) const { + assert(IsLoaded(region_code)); + return region_rules_.find(region_code)->second; } bool PreloadSupplier::IsLoaded(const std::string& region_code) const { diff --git a/cpp/src/region_data_builder.cc b/cpp/src/region_data_builder.cc index cf49c71..b600d09 100644 --- a/cpp/src/region_data_builder.cc +++ b/cpp/src/region_data_builder.cc @@ -17,6 +17,7 @@ #include <libaddressinput/address_data.h> #include <libaddressinput/preload_supplier.h> #include <libaddressinput/region_data.h> +#include <libaddressinput/util/basictypes.h> #include <cassert> #include <cstddef> @@ -34,59 +35,91 @@ namespace addressinput { namespace { -// Does not take ownership of |supplier| or |parent_region|, neither of which is -// allowed to be NULL. -void BuildRegionTreeRecursively(PreloadSupplier* supplier, - const LookupKey& parent_key, - RegionData* parent_region, - const std::vector<std::string>& keys, - bool prefer_latin_name) { - assert(supplier != NULL); +// The maximum depth of lookup keys. +static const size_t kLookupKeysMaxDepth = arraysize(LookupKey::kHierarchy) - 1; + +// Does not take ownership of |parent_region|, which is not allowed to be NULL. +void BuildRegionTreeRecursively( + const std::map<std::string, const Rule*>& rules, + std::map<std::string, const Rule*>::const_iterator hint, + const LookupKey& parent_key, + RegionData* parent_region, + const std::vector<std::string>& keys, + bool prefer_latin_name, + size_t region_max_depth) { assert(parent_region != NULL); LookupKey lookup_key; for (std::vector<std::string>::const_iterator key_it = keys.begin(); key_it != keys.end(); ++key_it) { lookup_key.FromLookupKey(parent_key, *key_it); - const Rule* rule = supplier->GetRule(lookup_key); - if (rule == NULL) { - return; + const std::string& lookup_key_string = + lookup_key.ToKeyString(kLookupKeysMaxDepth); + + ++hint; + if (hint == rules.end() || hint->first != lookup_key_string) { + hint = rules.find(lookup_key_string); + if (hint == rules.end()) { + return; + } } + + const Rule* rule = hint->second; + assert(rule != NULL); + const std::string& local_name = rule->GetName().empty() ? *key_it : rule->GetName(); const std::string& name = prefer_latin_name && !rule->GetLatinName().empty() ? rule->GetLatinName() : local_name; RegionData* region = parent_region->AddSubRegion(*key_it, name); - if (!rule->GetSubKeys().empty()) { - BuildRegionTreeRecursively( - supplier, lookup_key, region, rule->GetSubKeys(), prefer_latin_name); + + if (!rule->GetSubKeys().empty() && + region_max_depth > parent_key.GetDepth()) { + BuildRegionTreeRecursively(rules, + hint, + lookup_key, + region, + rule->GetSubKeys(), + prefer_latin_name, + region_max_depth); } } } -// Does not take ownership of |supplier|, which cannot be NULL. The caller owns -// the result. -RegionData* BuildRegion(PreloadSupplier* supplier, +// The caller owns the result. +RegionData* BuildRegion(const std::map<std::string, const Rule*>& rules, const std::string& region_code, const Language& language) { - assert(supplier != NULL); - AddressData address; address.region_code = region_code; LookupKey lookup_key; lookup_key.FromAddress(address); - const Rule* const rule = supplier->GetRule(lookup_key); + std::map<std::string, const Rule*>::const_iterator hint = + rules.find(lookup_key.ToKeyString(kLookupKeysMaxDepth)); + assert(hint != rules.end()); + + const Rule* rule = hint->second; assert(rule != NULL); RegionData* region = new RegionData(region_code); - BuildRegionTreeRecursively(supplier, - lookup_key, - region, - rule->GetSubKeys(), - language.has_latin_script); + + // If there're sub-keys for field X, but field X is not used in this region + // code, then these sub-keys are skipped over. For example, CH has sub-keys + // for field ADMIN_AREA, but CH does not use ADMIN_AREA field. + size_t region_max_depth = + RegionDataConstants::GetMaxLookupKeyDepth(region_code); + if (region_max_depth > 0) { + BuildRegionTreeRecursively(rules, + hint, + lookup_key, + region, + rule->GetSubKeys(), + language.has_latin_script, + region_max_depth); + } return region; } @@ -139,9 +172,11 @@ const RegionData& RegionDataBuilder::Build( LanguageRegionMap::const_iterator language_it = region_it->second->find(best_language.tag); if (language_it == region_it->second->end()) { + const std::map<std::string, const Rule*>& rules = + supplier_->GetRulesForRegion(region_code); language_it = region_it->second->insert(std::make_pair(best_language.tag, - BuildRegion(supplier_, + BuildRegion(rules, region_code, best_language))) .first; diff --git a/cpp/src/region_data_constants.cc b/cpp/src/region_data_constants.cc index 3557536..e21d4b8 100644 --- a/cpp/src/region_data_constants.cc +++ b/cpp/src/region_data_constants.cc @@ -1243,7 +1243,9 @@ std::map<std::string, std::string> InitRegionData() { "\"languages\":\"sw~en\"" "}")); region_data.insert(std::make_pair("UA", "{" - "\"fmt\":\"%Z %C%n%A%n%O%n%N\"," + "\"fmt\":\"%N%n%O%n%A%n%C%n%S%n%Z\"," + "\"require\":\"ACZ\"," + "\"state_name_type\":\"oblast\"," "\"zipex\":\"15432,01055,01001\"," "\"posturl\":\"http://services.ukrposhta.com/postindex_new/\"," "\"languages\":\"uk~ru\"" diff --git a/cpp/src/retriever.cc b/cpp/src/retriever.cc index bdb8145..151b74d 100644 --- a/cpp/src/retriever.cc +++ b/cpp/src/retriever.cc @@ -15,7 +15,7 @@ #include "retriever.h" #include <libaddressinput/callback.h> -#include <libaddressinput/downloader.h> +#include <libaddressinput/source.h> #include <libaddressinput/storage.h> #include <libaddressinput/util/basictypes.h> #include <libaddressinput/util/scoped_ptr.h> @@ -24,7 +24,6 @@ #include <cstddef> #include <string> -#include "lookup_key_util.h" #include "validating_storage.h" namespace i18n { @@ -37,14 +36,12 @@ class Helper { // Does not take ownership of its parameters. Helper(const std::string& key, const Retriever::Callback& retrieved, - const LookupKeyUtil& lookup_key_util, - const Downloader& downloader, + const Source& source, ValidatingStorage* storage) : retrieved_(retrieved), - lookup_key_util_(lookup_key_util), - downloader_(downloader), + source_(source), storage_(storage), - downloaded_(BuildCallback(this, &Helper::OnDownloaded)), + fresh_data_ready_(BuildCallback(this, &Helper::OnFreshDataReady)), validated_data_ready_( BuildCallback(this, &Helper::OnValidatedDataReady)), stale_data_() { @@ -68,13 +65,14 @@ class Helper { if (data != NULL && !data->empty()) { stale_data_ = *data; } - downloader_.Download(lookup_key_util_.GetUrlForKey(key), *downloaded_); + source_.Get(key, *fresh_data_ready_); } delete data; } - void OnDownloaded(bool success, const std::string& url, std::string* data) { - const std::string& key = lookup_key_util_.GetKeyForUrl(url); + void OnFreshDataReady(bool success, + const std::string& key, + std::string* data) { if (success) { assert(data != NULL); retrieved_(true, key, *data); @@ -92,10 +90,9 @@ class Helper { } const Retriever::Callback& retrieved_; - const LookupKeyUtil& lookup_key_util_; - const Downloader& downloader_; + const Source& source_; ValidatingStorage* storage_; - const scoped_ptr<const Downloader::Callback> downloaded_; + const scoped_ptr<const Source::Callback> fresh_data_ready_; const scoped_ptr<const Storage::Callback> validated_data_ready_; std::string stale_data_; @@ -104,21 +101,17 @@ class Helper { } // namespace -Retriever::Retriever(const std::string& validation_data_url, - const Downloader* downloader, - Storage* storage) - : lookup_key_util_(validation_data_url), - downloader_(downloader), - storage_(new ValidatingStorage(storage)) { +Retriever::Retriever(const Source* source, Storage* storage) + : source_(source), storage_(new ValidatingStorage(storage)) { + assert(source_ != NULL); assert(storage_ != NULL); - assert(downloader_ != NULL); } Retriever::~Retriever() {} void Retriever::Retrieve(const std::string& key, const Callback& retrieved) const { - new Helper(key, retrieved, lookup_key_util_, *downloader_, storage_.get()); + new Helper(key, retrieved, *source_, storage_.get()); } } // namespace addressinput diff --git a/cpp/src/retriever.h b/cpp/src/retriever.h index 9634935..4bf27a5 100644 --- a/cpp/src/retriever.h +++ b/cpp/src/retriever.h @@ -23,20 +23,17 @@ #include <string> -#include "lookup_key_util.h" - namespace i18n { namespace addressinput { -class Downloader; +class Source; class Storage; class ValidatingStorage; // Retrieves data. Sample usage: +// Source* source = ...; // Storage* storage = ...; -// Downloader* downloader = ...; -// Retriever retriever("https://i18napis.appspot.com/ssl-address/", -// downloader, storage); +// Retriever retriever(source, storage); // const scoped_ptr<const Retriever::Callback> retrieved( // BuildCallback(this, &MyClass::OnDataRetrieved)); // retriever.Retrieve("data/CA/AB--fr", *retrieved); @@ -45,24 +42,21 @@ class Retriever { typedef i18n::addressinput::Callback<const std::string&, const std::string&> Callback; - // Takes ownership of |downloader| and |storage|. - Retriever(const std::string& validation_data_url, - const Downloader* downloader, - Storage* storage); + // Takes ownership of |source| and |storage|. + Retriever(const Source* source, Storage* storage); ~Retriever(); // Retrieves the data for |key| and invokes the |retrieved| callback. Checks - // for the data in storage first. If storage does not have the data for |key|, - // then downloads the data and places it in storage. If the data in storage is - // corrupted, then it's discarded and redownloaded. If the data is stale, then - // it's redownloaded. If the download fails, then stale data will be returned - // this one time. The next call to Retrieve() will attempt to download fresh - // data again. + // for the data in |storage_| first. If storage does not have the data for + // |key|, then gets the data from |source_| and places it in storage. If the + // data in storage is corrupted, then it's discarded and requested anew. If + // the data is stale, then it's requested anew. If the request fails, then + // stale data will be returned this one time. Any subsequent call to + // Retrieve() will attempt to get fresh data again. void Retrieve(const std::string& key, const Callback& retrieved) const; private: - const LookupKeyUtil lookup_key_util_; - scoped_ptr<const Downloader> downloader_; + scoped_ptr<const Source> source_; scoped_ptr<ValidatingStorage> storage_; DISALLOW_COPY_AND_ASSIGN(Retriever); diff --git a/cpp/src/util/json.cc b/cpp/src/util/json.cc index 4f26069..730479c 100644 --- a/cpp/src/util/json.cc +++ b/cpp/src/util/json.cc @@ -19,9 +19,7 @@ #include <cassert> #include <cstddef> -#include <map> #include <string> -#include <utility> #include <vector> #include <rapidjson/document.h> @@ -36,155 +34,100 @@ using rapidjson::Value; class Json::JsonImpl { public: - // Takes ownership of |document|. - explicit JsonImpl(const Document* document) - : document_(document), value_(document), dictionaries_() { - assert(value_ != NULL); - assert(value_->IsObject()); - BuildKeyList(); - } - - // Does not take ownership of |value|. - explicit JsonImpl(const Value* value) - : document_(), value_(value), dictionaries_() { - assert(value_ != NULL); - assert(value_->IsObject()); - BuildKeyList(); + explicit JsonImpl(const std::string& json) + : document_(new Document), + value_(document_.get()), + dictionaries_(), + valid_(false) { + document_->Parse<kParseValidateEncodingFlag>(json.c_str()); + valid_ = !document_->HasParseError() && document_->IsObject(); } ~JsonImpl() { - for (std::map<std::string, const Json*>::const_iterator - it = dictionaries_.begin(); + for (std::vector<const Json*>::const_iterator it = dictionaries_.begin(); it != dictionaries_.end(); ++it) { - delete it->second; + delete *it; } } - // The caller does not own the result. - const Value::Member* FindMember(const std::string& key) { - return value_->FindMember(key.c_str()); - } + bool valid() const { return valid_; } - // The caller does not own the result. The result can be NULL if there's no - // dictionary for |key|. - const Json* FindDictionary(const std::string& key) const { - std::map<std::string, const Json*>::const_iterator it = - dictionaries_.find(key); - return it != dictionaries_.end() ? it->second : NULL; + const std::vector<const Json*>& GetSubDictionaries() { + if (dictionaries_.empty()) { + for (Value::ConstMemberIterator member = value_->MemberBegin(); + member != value_->MemberEnd(); ++member) { + if (member->value.IsObject()) { + dictionaries_.push_back(new Json(new JsonImpl(&member->value))); + } + } + } + return dictionaries_; } - // Takes ownership of |dictionary|. Should be called only once per |key| and - // per |dictionary|. - void AddDictionary(const std::string& key, const Json* dictionary) { - bool inserted = - dictionaries_.insert(std::make_pair(key, dictionary)).second; - // Cannot do work inside of assert(), because the compiler can optimize it - // away. - assert(inserted); - // Avoid unused variable warning when assert() is optimized away. - (void)inserted; - } + bool GetStringValueForKey(const std::string& key, std::string* value) const { + assert(value != NULL); - const std::vector<std::string>& GetKeys() const { return keys_; } + Value::ConstMemberIterator member = value_->FindMember(key.c_str()); + if (member == NULL || !member->value.IsString()) { + return false; + } + + value->assign(member->value.GetString(), + member->value.GetStringLength()); + return true; + } private: - void BuildKeyList() { - assert(keys_.empty()); - for (Value::ConstMemberIterator it = value_->MemberBegin(); - it != value_->MemberEnd(); ++it) { - keys_.push_back(it->name.GetString()); - } + // Does not take ownership of |value|. + explicit JsonImpl(const Value* value) + : document_(), + value_(value), + dictionaries_(), + valid_(true) { + assert(value_ != NULL); + assert(value_->IsObject()); } // An owned JSON document. Can be NULL if the JSON document is not owned. - // - // When a JsonImpl object is constructed using a Document object, then - // JsonImpl is supposed to take ownership of that object, making sure to - // delete it in its own destructor. But when a JsonImpl object is constructed - // using a Value object, then that object is owned by a Member object which is - // owned by a Document object, and should therefore not be deleted by - // JsonImpl. - const scoped_ptr<const Document> document_; + const scoped_ptr<Document> document_; // A JSON document that is not owned. Cannot be NULL. Can point to document_. const Value* const value_; - // Owned JSON objects. - std::map<std::string, const Json*> dictionaries_; + // Owned JSON objects of sub-dictionaries. + std::vector<const Json*> dictionaries_; - std::vector<std::string> keys_; + // True if the JSON object was parsed successfully. + bool valid_; DISALLOW_COPY_AND_ASSIGN(JsonImpl); }; -Json::Json() {} +Json::Json() : impl_() {} Json::~Json() {} bool Json::ParseObject(const std::string& json) { assert(impl_ == NULL); - scoped_ptr<Document> document(new Document); - document->Parse<kParseValidateEncodingFlag>(json.c_str()); - bool valid = !document->HasParseError() && document->IsObject(); - if (valid) { - impl_.reset(new JsonImpl(document.release())); + impl_.reset(new JsonImpl(json)); + if (!impl_->valid()) { + impl_.reset(); } - return valid; + return impl_ != NULL; } -const std::vector<std::string>& Json::GetKeys() const { +const std::vector<const Json*>& Json::GetSubDictionaries() const { assert(impl_ != NULL); - return impl_->GetKeys(); + return impl_->GetSubDictionaries(); } bool Json::GetStringValueForKey(const std::string& key, std::string* value) const { assert(impl_ != NULL); - assert(value != NULL); - - // Member is owned by impl_. - const Value::Member* member = impl_->FindMember(key.c_str()); - if (member == NULL || !member->value.IsString()) { - return false; - } - - value->assign(member->value.GetString(), member->value.GetStringLength()); - return true; -} - -bool Json::HasDictionaryValueForKey(const std::string& key) const { - assert(impl_ != NULL); - - // The value returned by FindDictionary() is owned by impl_. - if (impl_->FindDictionary(key) != NULL) { - return true; - } - - // Member is owned by impl_. - const Value::Member* member = impl_->FindMember(key); - return member != NULL && member->value.IsObject(); + return impl_->GetStringValueForKey(key, value); } -const Json& Json::GetDictionaryValueForKey(const std::string& key) const { - assert(impl_ != NULL); - - // Existing_dictionary is owned by impl_. - const Json* existing_dictionary = impl_->FindDictionary(key); - if (existing_dictionary != NULL) { - return *existing_dictionary; - } - - // Member is owned by impl_. - const Value::Member* member = impl_->FindMember(key); - assert(member != NULL); - assert(member->value.IsObject()); - - // Dictionary is owned by impl_. - Json* dictionary = new Json; - dictionary->impl_.reset(new JsonImpl(&member->value)); - impl_->AddDictionary(key, dictionary); - return *dictionary; -} +Json::Json(JsonImpl* impl) : impl_(impl) {} } // namespace addressinput } // namespace i18n diff --git a/cpp/src/util/json.h b/cpp/src/util/json.h index 90565e6..1aac803 100644 --- a/cpp/src/util/json.h +++ b/cpp/src/util/json.h @@ -39,9 +39,10 @@ class Json { // object. bool ParseObject(const std::string& json); - // Returns the list of keys in the parsed JSON. The JSON object must be parsed - // successfully in ParseObject() before invoking this method. - const std::vector<std::string>& GetKeys() const; + // Returns the list of sub dictionaries. The JSON object must be parsed + // successfully in ParseObject() before invoking this method. The caller does + // not own the result. + const std::vector<const Json*>& GetSubDictionaries() const; // Returns true if the parsed JSON contains a string value for |key|. Sets // |value| to the string value of the |key|. The JSON object must be parsed @@ -49,18 +50,13 @@ class Json { // parameter should not be NULL. bool GetStringValueForKey(const std::string& key, std::string* value) const; - // Returns true if the parsed JSON contains a dictionary value for |key|. The - // JSON object must be parsed successfully in ParseObject() before invoking - // this method. - bool HasDictionaryValueForKey(const std::string& key) const; - - // Returns the dictionary value for the |key|. The |key| must be present and - // its value must be of the dictionary type, i.e., HasDictionaryValueForKey() - // must return true before invoking this method. - const Json& GetDictionaryValueForKey(const std::string& key) const; - private: class JsonImpl; + friend class JsonImpl; + + // Constructor to be called by JsonImpl. + explicit Json(JsonImpl* impl); + scoped_ptr<JsonImpl> impl_; DISALLOW_COPY_AND_ASSIGN(Json); diff --git a/cpp/test/address_formatter_test.cc b/cpp/test/address_formatter_test.cc index d20a344..c656ec8 100644 --- a/cpp/test/address_formatter_test.cc +++ b/cpp/test/address_formatter_test.cc @@ -205,4 +205,163 @@ TEST(AddressFormatterTest, GetFormattedNationalAddressMultilingualCountry) { EXPECT_EQ(expected, lines); } +TEST(AddressFormatterTest, GetFormattedNationalAddress_InlineStreetAddress) { + AddressData address; + address.region_code = "CI"; + address.address_line.push_back("32 Boulevard Carde"); + address.locality = "Abidjan"; + address.sorting_code = "64"; + + std::vector<std::string> expected; + expected.push_back("64 32 Boulevard Carde Abidjan 64"); + + std::vector<std::string> lines; + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); +} + +TEST(AddressFormatterTest, + GetFormattedNationalAddressMissingFields_LiteralsAroundField) { + AddressData address; + address.region_code = "CH"; + std::vector<std::string> expected; + std::vector<std::string> lines; + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.locality = "Zurich"; + expected.push_back("Zurich"); + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.postal_code = "8001"; + expected.back().assign("CH-8001 Zurich"); + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.locality.clear(); + expected.back().assign("CH-8001"); + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); +} + +TEST(AddressFormatterTest, + GetFormattedNationalAddressMissingFields_LiteralsBetweenFields) { + AddressData address; + address.region_code = "US"; + std::vector<std::string> expected; + std::vector<std::string> lines; + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.administrative_area = "CA"; + expected.push_back("CA"); + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.locality = "Los Angeles"; + expected.back().assign("Los Angeles, CA"); + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.postal_code = "90291"; + expected.back().assign("Los Angeles, CA 90291"); + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.administrative_area.clear(); + expected.back().assign("Los Angeles 90291"); + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.locality.clear(); + address.administrative_area = "CA"; + expected.back().assign("CA 90291"); + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); +} + +TEST(AddressFormatterTest, + GetFormattedNationalAddressMissingFields_LiteralOnSeparateLine) { + AddressData address; + address.region_code = "AX"; + std::vector<std::string> expected; + expected.push_back("\xC3\x85LAND"); /* Ã…LAND */ + std::vector<std::string> lines; + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.locality = "City"; + expected.insert(expected.begin(), "City"); + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.postal_code = "123"; + expected.front().assign("AX-123 City"); + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); +} + +TEST(AddressFormatterTest, + GetFormattedNationalAddressMissingFields_LiteralBeforeField) { + AddressData address; + address.region_code = "JP"; + address.language_code = "ja"; + std::vector<std::string> expected; + std::vector<std::string> lines; + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.postal_code = "123"; + expected.push_back("\xE3\x80\x92" "123"); /* 〒123 */ + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.administrative_area = "Prefecture"; + expected.push_back("Prefecture"); + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.postal_code.clear(); + expected.erase(expected.begin()); + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); +} + +TEST(AddressFormatterTest, + GetFormattedNationalAddressMissingFields_DuplicateField) { + AddressData address; + address.region_code = "CI"; + std::vector<std::string> expected; + std::vector<std::string> lines; + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.sorting_code = "123"; + expected.push_back("123 123"); + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.address_line.push_back("456 Main St"); + expected.back().assign("123 456 Main St 123"); + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.locality = "Yamoussoukro"; + expected.back().assign("123 456 Main St Yamoussoukro 123"); + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.sorting_code.erase(); + expected.back().assign("456 Main St Yamoussoukro"); + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); + + address.address_line.clear(); + expected.back().assign("Yamoussoukro"); + GetFormattedNationalAddress(address, &lines); + EXPECT_EQ(expected, lines); +} + + } // namespace diff --git a/cpp/test/address_input_helper_test.cc b/cpp/test/address_input_helper_test.cc index 5580ec7..cfae3f3 100644 --- a/cpp/test/address_input_helper_test.cc +++ b/cpp/test/address_input_helper_test.cc @@ -26,8 +26,8 @@ #include <gtest/gtest.h> -#include "fake_downloader.h" -#include "mock_downloader.h" +#include "mock_source.h" +#include "testdata_source.h" namespace { @@ -35,19 +35,17 @@ using i18n::addressinput::AddressData; using i18n::addressinput::AddressInputHelper; using i18n::addressinput::BuildCallback; using i18n::addressinput::Callback; -using i18n::addressinput::FakeDownloader; -using i18n::addressinput::MockDownloader; +using i18n::addressinput::MockSource; using i18n::addressinput::NullStorage; using i18n::addressinput::PreloadSupplier; using i18n::addressinput::scoped_ptr; +using i18n::addressinput::TestdataSource; class AddressInputHelperTest : public testing::Test { protected: AddressInputHelperTest() // Our PreloadSupplier loads all data for a country at a time. - : supplier_(FakeDownloader::kFakeAggregateDataUrl, - new FakeDownloader, - new NullStorage), + : supplier_(new TestdataSource(true), new NullStorage), address_input_helper_(&supplier_), loaded_(BuildCallback(this, &AddressInputHelperTest::Loaded)) {} @@ -245,11 +243,9 @@ TEST_F(AddressInputHelperTest, AddressWithInvalidOrMissingRegionCode) { class AddressInputHelperMockDataTest : public testing::Test { protected: AddressInputHelperMockDataTest() - : downloader_(new MockDownloader), + : source_(new MockSource), // Our PreloadSupplier loads all data for a country at a time. - supplier_(MockDownloader::kMockDataUrl, - downloader_, - new NullStorage), + supplier_(source_, new NullStorage), address_input_helper_(&supplier_), loaded_(BuildCallback(this, &AddressInputHelperMockDataTest::Loaded)) {} @@ -265,10 +261,10 @@ class AddressInputHelperMockDataTest : public testing::Test { address_input_helper_.FillAddress(address); } - MockDownloader* const downloader_; + MockSource* const source_; private: - // Our mock downloader we assume will always succeed. + // Our mock source we assume will always succeed. void Loaded(bool success, const std::string&, int) { ASSERT_TRUE(success); } PreloadSupplier supplier_; @@ -281,7 +277,7 @@ TEST_F(AddressInputHelperMockDataTest, PostalCodeSharedAcrossDifferentHierarchies) { // Note that this data is in the format of data that would be returned from // the aggregate server. - downloader_->data_.insert(std::make_pair( + source_->data_.insert(std::make_pair( // We use KR since we need a country where we format all fields down to // dependent locality, or the hierarchy won't be loaded. "data/KR", @@ -315,7 +311,7 @@ TEST_F(AddressInputHelperMockDataTest, // Create data where one state matches the ZIP code, but the other doesn't: // within the state which does, multiple cities and sub-cities match. The only // thing we can be certain of is therefore the state. - downloader_->data_.insert(std::make_pair( + source_->data_.insert(std::make_pair( // We use KR since we need a country where we format all fields down to // dependent locality, or the hierarchy won't be loaded. "data/KR", diff --git a/cpp/test/address_normalizer_test.cc b/cpp/test/address_normalizer_test.cc index fc4404b..7a01d38 100644 --- a/cpp/test/address_normalizer_test.cc +++ b/cpp/test/address_normalizer_test.cc @@ -25,24 +25,22 @@ #include <gtest/gtest.h> -#include "fake_downloader.h" +#include "testdata_source.h" namespace { using i18n::addressinput::AddressData; using i18n::addressinput::AddressNormalizer; using i18n::addressinput::BuildCallback; -using i18n::addressinput::FakeDownloader; using i18n::addressinput::NullStorage; using i18n::addressinput::PreloadSupplier; using i18n::addressinput::scoped_ptr; +using i18n::addressinput::TestdataSource; class AddressNormalizerTest : public testing::Test { protected: AddressNormalizerTest() - : supplier_(FakeDownloader::kFakeAggregateDataUrl, - new FakeDownloader, - new NullStorage), + : supplier_(new TestdataSource(true), new NullStorage), loaded_(BuildCallback(this, &AddressNormalizerTest::OnLoaded)), normalizer_(&supplier_) {} diff --git a/cpp/test/address_validator_test.cc b/cpp/test/address_validator_test.cc index b90eef9..0b4b025 100644 --- a/cpp/test/address_validator_test.cc +++ b/cpp/test/address_validator_test.cc @@ -29,19 +29,19 @@ #include <gtest/gtest.h> -#include "fake_downloader.h" +#include "testdata_source.h" namespace { using i18n::addressinput::AddressData; using i18n::addressinput::AddressValidator; using i18n::addressinput::BuildCallback; -using i18n::addressinput::FakeDownloader; using i18n::addressinput::FieldProblemMap; using i18n::addressinput::NullStorage; using i18n::addressinput::OndemandSupplier; using i18n::addressinput::PreloadSupplier; using i18n::addressinput::scoped_ptr; +using i18n::addressinput::TestdataSource; using i18n::addressinput::COUNTRY; using i18n::addressinput::ADMIN_AREA; @@ -89,9 +89,7 @@ class OndemandValidatorWrapper : public ValidatorWrapper { private: OndemandValidatorWrapper() - : supplier_(FakeDownloader::kFakeDataUrl, - new FakeDownloader, - new NullStorage), + : supplier_(new TestdataSource(false), new NullStorage), validator_(&supplier_) {} OndemandSupplier supplier_; @@ -126,9 +124,7 @@ class PreloadValidatorWrapper : public ValidatorWrapper { private: PreloadValidatorWrapper() - : supplier_(FakeDownloader::kFakeAggregateDataUrl, - new FakeDownloader, - new NullStorage), + : supplier_(new TestdataSource(true), new NullStorage), validator_(&supplier_), loaded_(BuildCallback(this, &PreloadValidatorWrapper::Loaded)) {} diff --git a/cpp/test/fake_downloader.h b/cpp/test/fake_downloader.h deleted file mode 100644 index 565263b..0000000 --- a/cpp/test/fake_downloader.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (C) 2013 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// A fake downloader object to use in tests. Reads data from a file instead of -// downloading it from a server. - -#ifndef I18N_ADDRESSINPUT_TEST_FAKE_DOWNLOADER_H_ -#define I18N_ADDRESSINPUT_TEST_FAKE_DOWNLOADER_H_ - -#include <libaddressinput/downloader.h> - -#include <string> - -namespace i18n { -namespace addressinput { - -// "Downloads" serialized validation rules from a test data file. Sample usage: -// class MyClass { -// public: -// MyClass() : downloader_(), -// callback_(BuildCallback(this, &MyClass::OnDownloaded)) {} -// -// ~MyClass() {} -// -// void GetData(const std::string& key) { -// downloader_.Download(std::string(FakeDownloader::kFakeDataUrl) + key, -// *callback_); -// } -// -// private: -// void OnDownloaded(bool success, -// const std::string& url, -// std::string* data) { -// ... -// delete data; -// } -// -// FakeDownloader downloader_; -// const scoped_ptr<const Downloader::Callback> callback_; -// -// DISALLOW_COPY_AND_ASSIGN(MyClass); -// }; -class FakeDownloader : public Downloader { - public: - // The fake data URL to be used in tests for retrieving one key at a time. - static const char kFakeDataUrl[]; - - // The fake data URL to be used in tests for retrieving aggregate data, which - // is a JSON dictionary that maps from keys to dictionaries of what you would - // normally get from kFakeDataUrl. - static const char kFakeAggregateDataUrl[]; - - FakeDownloader(); - virtual ~FakeDownloader(); - - // Downloader implementation. - virtual void Download(const std::string& url, - const Callback& downloaded) const; -}; - -} // namespace addressinput -} // namespace i18n - -#endif // I18N_ADDRESSINPUT_TEST_FAKE_DOWNLOADER_H_ diff --git a/cpp/test/fake_storage.h b/cpp/test/fake_storage.h index 9188b3b..55273c6 100644 --- a/cpp/test/fake_storage.h +++ b/cpp/test/fake_storage.h @@ -30,7 +30,7 @@ namespace addressinput { // class MyClass { // public: // MyClass() : storage_(), -// callback(BuildCallback(this, &MyClass::OnDataReady)) {} +// data_ready_(BuildCallback(this, &MyClass::OnDataReady)) {} // // ~MyClass() {} // @@ -39,7 +39,7 @@ namespace addressinput { // } // // void Read() { -// storage_.Get("key", *callback_); +// storage_.Get("key", *data_ready_); // } // // private: @@ -51,7 +51,7 @@ namespace addressinput { // } // // FakeStorage storage_; -// const scoped_ptr<const Storage::Callback> callback_; +// const scoped_ptr<const Storage::Callback> data_ready_; // // DISALLOW_COPY_AND_ASSIGN(MyClass); // }; diff --git a/cpp/test/fake_storage_test.cc b/cpp/test/fake_storage_test.cc index 94fed9e..275c9fc 100644 --- a/cpp/test/fake_storage_test.cc +++ b/cpp/test/fake_storage_test.cc @@ -50,9 +50,7 @@ class FakeStorageTest : public testing::Test { const scoped_ptr<const Storage::Callback> data_ready_; private: - void OnDataReady(bool success, - const std::string& key, - std::string* data) { + void OnDataReady(bool success, const std::string& key, std::string* data) { ASSERT_FALSE(success && data == NULL); success_ = success; key_ = key; diff --git a/cpp/test/lookup_key_util_test.cc b/cpp/test/lookup_key_util_test.cc deleted file mode 100644 index a210bd5..0000000 --- a/cpp/test/lookup_key_util_test.cc +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (C) 2013 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "lookup_key_util.h" - -#include <gtest/gtest.h> - -namespace { - -using i18n::addressinput::LookupKeyUtil; - -TEST(LookupKeyUtilTest, GetUrlForKey) { - const LookupKeyUtil util("test:///"); - EXPECT_EQ("test:///", util.GetUrlForKey("")); - EXPECT_EQ("test:///data", util.GetUrlForKey("data")); - EXPECT_EQ("test:///data/US", util.GetUrlForKey("data/US")); - EXPECT_EQ("test:///data/CA--fr", util.GetUrlForKey("data/CA--fr")); -} - -TEST(LookupKeyUtilTest, GetKeyForUrl) { - const LookupKeyUtil util("test:///"); - EXPECT_EQ("", util.GetKeyForUrl("test://")); - EXPECT_EQ("", util.GetKeyForUrl("http://www.google.com/")); - EXPECT_EQ("", util.GetKeyForUrl("")); - EXPECT_EQ("", util.GetKeyForUrl("test:///")); - EXPECT_EQ("data", util.GetKeyForUrl("test:///data")); - EXPECT_EQ("data/US", util.GetKeyForUrl("test:///data/US")); - EXPECT_EQ("data/CA--fr", util.GetKeyForUrl("test:///data/CA--fr")); -} - -TEST(LookupKeyUtilTest, IsValidationDataUrl) { - const LookupKeyUtil util("test:///"); - EXPECT_FALSE(util.IsValidationDataUrl("test://")); - EXPECT_FALSE(util.IsValidationDataUrl("http://www.google.com/")); - EXPECT_FALSE(util.IsValidationDataUrl("")); - EXPECT_TRUE(util.IsValidationDataUrl("test:///")); - EXPECT_TRUE(util.IsValidationDataUrl("test:///data")); - EXPECT_TRUE(util.IsValidationDataUrl("test:///data/US")); - EXPECT_TRUE(util.IsValidationDataUrl("test:///data/CA--fr")); -} - -} // namespace diff --git a/cpp/test/mock_downloader.h b/cpp/test/mock_downloader.h deleted file mode 100644 index d12b38d..0000000 --- a/cpp/test/mock_downloader.h +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (C) 2014 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// A mock downloader object to use in tests. - -#ifndef I18N_ADDRESSINPUT_TEST_MOCK_DOWNLOADER_H_ -#define I18N_ADDRESSINPUT_TEST_MOCK_DOWNLOADER_H_ - -#include <libaddressinput/downloader.h> -#include <libaddressinput/util/basictypes.h> - -#include <map> -#include <string> - -namespace i18n { -namespace addressinput { - -// "Downloads" serialized validation rules from a key-value map. Sample usage: -// class MyClass { -// public: -// MyClass() : downloader_(), -// callback_(BuildCallback(this, &MyClass::OnDownloaded)) { -// downloader_.data_.insert( -// std::make_pair("data/XA", "{\"id\":\"data/XA\"}")); -// } -// -// ~MyClass() {} -// -// void GetData(const std::string& key) { -// downloader_.Download(std::string(MockDownloader::kMockDataUrl) + key, -// *callback_); -// } -// -// private: -// void OnDownloaded(bool success, -// const std::string& url, -// std::string* data) { -// ... -// delete data; -// } -// -// MockDownloader downloader_; -// const scoped_ptr<const Downloader::Callback> callback_; -// -// DISALLOW_COPY_AND_ASSIGN(MyClass); -// }; -class MockDownloader : public Downloader { - public: - // The mock data URL to be used in tests. - static const char kMockDataUrl[]; - - MockDownloader(); - virtual ~MockDownloader(); - - // Downloader implementation. - virtual void Download(const std::string& url, - const Callback& downloaded) const; - - std::map<std::string, std::string> data_; - - private: - DISALLOW_COPY_AND_ASSIGN(MockDownloader); -}; - -} // namespace addressinput -} // namespace i18n - -#endif // I18N_ADDRESSINPUT_TEST_MOCK_DOWNLOADER_H_ diff --git a/cpp/test/mock_downloader.cc b/cpp/test/mock_source.cc index 71256aa..d04598b 100644 --- a/cpp/test/mock_downloader.cc +++ b/cpp/test/mock_source.cc @@ -12,9 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "mock_downloader.h" +#include "mock_source.h" -#include <cassert> #include <cstddef> #include <map> #include <string> @@ -22,27 +21,14 @@ namespace i18n { namespace addressinput { -// static -const char MockDownloader::kMockDataUrl[] = "mock:///"; +MockSource::MockSource() {} -namespace { +MockSource::~MockSource() {} -// The number of characters in the mock data URL prefix. -const size_t kMockDataUrlLength = sizeof MockDownloader::kMockDataUrl - 1; - -} // namespace - -MockDownloader::MockDownloader() {} - -MockDownloader::~MockDownloader() {} - -void MockDownloader::Download(const std::string& url, - const Callback& downloaded) const { - assert(url.compare(0, kMockDataUrlLength, kMockDataUrl) == 0); - std::string key(url, kMockDataUrlLength); +void MockSource::Get(const std::string& key, const Callback& data_ready) const { std::map<std::string, std::string>::const_iterator it = data_.find(key); bool success = it != data_.end(); - downloaded(success, url, success ? new std::string(it->second) : NULL); + data_ready(success, key, success ? new std::string(it->second) : NULL); } } // namespace addressinput diff --git a/cpp/test/mock_source.h b/cpp/test/mock_source.h new file mode 100644 index 0000000..27f75c4 --- /dev/null +++ b/cpp/test/mock_source.h @@ -0,0 +1,74 @@ +// Copyright (C) 2014 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// A mock implementation of the Source interface to be used in tests. + +#ifndef I18N_ADDRESSINPUT_TEST_MOCK_SOURCE_H_ +#define I18N_ADDRESSINPUT_TEST_MOCK_SOURCE_H_ + +#include <libaddressinput/source.h> +#include <libaddressinput/util/basictypes.h> + +#include <map> +#include <string> + +namespace i18n { +namespace addressinput { + +// Gets address metadata from a key-value map. Sample usage: +// class MyClass { +// public: +// MyClass() : source_(), +// data_ready_(BuildCallback(this, &MyClass::OnDataReady)) { +// source_.data_.insert( +// std::make_pair("data/XA", "{\"id\":\"data/XA\"}")); +// } +// +// ~MyClass() {} +// +// void GetData(const std::string& key) { +// source_.Get(key, *data_ready_); +// } +// +// private: +// void OnDataReady(bool success, +// const std::string& key, +// std::string* data) { +// ... +// delete data; +// } +// +// MockSource source_; +// const scoped_ptr<const Source::Callback> data_ready_; +// +// DISALLOW_COPY_AND_ASSIGN(MyClass); +// }; +class MockSource : public Source { + public: + MockSource(); + virtual ~MockSource(); + + // Source implementation. + virtual void Get(const std::string& key, const Callback& data_ready) const; + + std::map<std::string, std::string> data_; + + private: + DISALLOW_COPY_AND_ASSIGN(MockSource); +}; + +} // namespace addressinput +} // namespace i18n + +#endif // I18N_ADDRESSINPUT_TEST_MOCK_SOURCE_H_ diff --git a/cpp/test/ondemand_supply_task_test.cc b/cpp/test/ondemand_supply_task_test.cc index 64f55d0..e15e997 100644 --- a/cpp/test/ondemand_supply_task_test.cc +++ b/cpp/test/ondemand_supply_task_test.cc @@ -29,7 +29,7 @@ #include <gtest/gtest.h> #include "lookup_key.h" -#include "mock_downloader.h" +#include "mock_source.h" #include "retriever.h" #include "rule.h" @@ -37,7 +37,7 @@ namespace { using i18n::addressinput::BuildCallback; using i18n::addressinput::LookupKey; -using i18n::addressinput::MockDownloader; +using i18n::addressinput::MockSource; using i18n::addressinput::NullStorage; using i18n::addressinput::OndemandSupplyTask; using i18n::addressinput::Retriever; @@ -52,11 +52,9 @@ class OndemandSupplyTaskTest : public testing::Test { lookup_key_(), rule_(), called_(false), - downloader_(new MockDownloader), + source_(new MockSource), rule_cache_(), - retriever_( - new Retriever( - MockDownloader::kMockDataUrl, downloader_, new NullStorage)), + retriever_(new Retriever(source_, new NullStorage)), supplied_(BuildCallback(this, &OndemandSupplyTaskTest::Supplied)), task_(new OndemandSupplyTask(lookup_key_, &rule_cache_, *supplied_)) {} @@ -71,11 +69,11 @@ class OndemandSupplyTaskTest : public testing::Test { void Retrieve() { task_->Retrieve(*retriever_); } - bool success_; // Expected status from MockDownloader. + bool success_; // Expected status from MockSource. LookupKey lookup_key_; // Stub. const Rule* rule_[arraysize(LookupKey::kHierarchy)]; bool called_; - MockDownloader* const downloader_; + MockSource* const source_; private: void Supplied(bool success, @@ -115,7 +113,7 @@ TEST_F(OndemandSupplyTaskTest, Invalid) { } TEST_F(OndemandSupplyTaskTest, Valid) { - downloader_->data_.insert(std::make_pair("data/XA", "{\"id\":\"data/XA\"}")); + source_->data_.insert(std::make_pair("data/XA", "{\"id\":\"data/XA\"}")); Queue("data/XA"); @@ -135,13 +133,13 @@ TEST_F(OndemandSupplyTaskTest, Valid) { } TEST_F(OndemandSupplyTaskTest, ValidHierarchy) { - downloader_->data_.insert( + source_->data_.insert( std::make_pair("data/XA", "{\"id\":\"data/XA\"}")); - downloader_->data_.insert( + source_->data_.insert( std::make_pair("data/XA/aa", "{\"id\":\"data/XA/aa\"}")); - downloader_->data_.insert( + source_->data_.insert( std::make_pair("data/XA/aa/bb", "{\"id\":\"data/XA/aa/bb\"}")); - downloader_->data_.insert( + source_->data_.insert( std::make_pair("data/XA/aa/bb/cc", "{\"id\":\"data/XA/aa/bb/cc\"}")); Queue("data/XA"); @@ -175,7 +173,7 @@ TEST_F(OndemandSupplyTaskTest, ValidHierarchy) { } TEST_F(OndemandSupplyTaskTest, InvalidJson1) { - downloader_->data_.insert(std::make_pair("data/XA", ":")); + source_->data_.insert(std::make_pair("data/XA", ":")); success_ = false; @@ -186,8 +184,8 @@ TEST_F(OndemandSupplyTaskTest, InvalidJson1) { } TEST_F(OndemandSupplyTaskTest, InvalidJson2) { - downloader_->data_.insert(std::make_pair("data/XA", "{\"id\":\"data/XA\"}")); - downloader_->data_.insert(std::make_pair("data/XA/aa", ":")); + source_->data_.insert(std::make_pair("data/XA", "{\"id\":\"data/XA\"}")); + source_->data_.insert(std::make_pair("data/XA/aa", ":")); success_ = false; @@ -199,8 +197,8 @@ TEST_F(OndemandSupplyTaskTest, InvalidJson2) { } TEST_F(OndemandSupplyTaskTest, EmptyJsonJustMeansServerKnowsNothingAboutKey) { - downloader_->data_.insert(std::make_pair("data/XA", "{\"id\":\"data/XA\"}")); - downloader_->data_.insert(std::make_pair("data/XA/aa", "{}")); + source_->data_.insert(std::make_pair("data/XA", "{\"id\":\"data/XA\"}")); + source_->data_.insert(std::make_pair("data/XA/aa", "{}")); Queue("data/XA"); Queue("data/XA/aa"); @@ -216,7 +214,7 @@ TEST_F(OndemandSupplyTaskTest, EmptyJsonJustMeansServerKnowsNothingAboutKey) { } TEST_F(OndemandSupplyTaskTest, IfCountryFailsAllFails) { - downloader_->data_.insert( + source_->data_.insert( std::make_pair("data/XA/aa", "{\"id\":\"data/XA/aa\"}")); success_ = false; diff --git a/cpp/test/preload_supplier_test.cc b/cpp/test/preload_supplier_test.cc index f8e9e23..bf04a85 100644 --- a/cpp/test/preload_supplier_test.cc +++ b/cpp/test/preload_supplier_test.cc @@ -25,27 +25,25 @@ #include <gtest/gtest.h> -#include "fake_downloader.h" #include "lookup_key.h" #include "rule.h" +#include "testdata_source.h" namespace { using i18n::addressinput::AddressData; using i18n::addressinput::BuildCallback; -using i18n::addressinput::FakeDownloader; using i18n::addressinput::LookupKey; using i18n::addressinput::NullStorage; using i18n::addressinput::PreloadSupplier; using i18n::addressinput::Rule; using i18n::addressinput::scoped_ptr; +using i18n::addressinput::TestdataSource; class PreloadSupplierTest : public testing::Test { protected: PreloadSupplierTest() - : supplier_(FakeDownloader::kFakeAggregateDataUrl, - new FakeDownloader, - new NullStorage), + : supplier_(new TestdataSource(true), new NullStorage), loaded_callback_(BuildCallback(this, &PreloadSupplierTest::OnLoaded)) {} virtual ~PreloadSupplierTest() {} @@ -121,4 +119,12 @@ TEST_F(PreloadSupplierTest, GetTooPreciseRule) { EXPECT_TRUE(rule == NULL); } +TEST_F(PreloadSupplierTest, GetRulesForRegion) { + supplier_.LoadRules("CN", *loaded_callback_); + const std::map<std::string, const Rule*>& rules = + supplier_.GetRulesForRegion("CN"); + EXPECT_TRUE(rules.find("data/CN") != rules.end()); + EXPECT_LT(1U, rules.size()); +} + } // namespace diff --git a/cpp/test/region_data_builder_test.cc b/cpp/test/region_data_builder_test.cc index b8e0ddd..a60189f 100644 --- a/cpp/test/region_data_builder_test.cc +++ b/cpp/test/region_data_builder_test.cc @@ -25,23 +25,22 @@ #include <gtest/gtest.h> -#include "fake_downloader.h" +#include "testdata_source.h" namespace { using i18n::addressinput::BuildCallback; -using i18n::addressinput::FakeDownloader; using i18n::addressinput::NullStorage; using i18n::addressinput::PreloadSupplier; using i18n::addressinput::RegionData; using i18n::addressinput::RegionDataBuilder; using i18n::addressinput::scoped_ptr; +using i18n::addressinput::TestdataSource; class RegionDataBuilderTest : public testing::Test { protected: RegionDataBuilderTest() - : supplier_(FakeDownloader::kFakeAggregateDataUrl, - new FakeDownloader, + : supplier_(new TestdataSource(true), new NullStorage), builder_(&supplier_), loaded_callback_(BuildCallback(this, &RegionDataBuilderTest::OnLoaded)), diff --git a/cpp/test/retriever_test.cc b/cpp/test/retriever_test.cc index 978e9f0..5b884b0 100644 --- a/cpp/test/retriever_test.cc +++ b/cpp/test/retriever_test.cc @@ -25,8 +25,8 @@ #include <gtest/gtest.h> -#include "fake_downloader.h" -#include "mock_downloader.h" +#include "mock_source.h" +#include "testdata_source.h" #define CHECKSUM "dd63dafcbd4d5b28badfcaf86fb6fcdb" #define DATA "{'foo': 'bar'}" @@ -35,16 +35,16 @@ namespace { using i18n::addressinput::BuildCallback; -using i18n::addressinput::FakeDownloader; -using i18n::addressinput::MockDownloader; +using i18n::addressinput::MockSource; using i18n::addressinput::NullStorage; using i18n::addressinput::Retriever; using i18n::addressinput::scoped_ptr; using i18n::addressinput::Storage; +using i18n::addressinput::TestdataSource; const char kKey[] = "data/CA/AB--fr"; -// Empty data that the downloader can return. +// Empty data that the source can return. const char kEmptyData[] = "{}"; // The value of the data that the stale storage returns. @@ -59,9 +59,7 @@ const char kStaleWrappedData[] = "timestamp=" OLD_TIMESTAMP "\n" class RetrieverTest : public testing::Test { protected: RetrieverTest() - : retriever_(FakeDownloader::kFakeDataUrl, - new FakeDownloader, - new NullStorage), + : retriever_(new TestdataSource(false), new NullStorage), success_(false), key_(), data_(), @@ -116,11 +114,9 @@ TEST_F(RetrieverTest, MissingKeyReturnsEmptyData) { EXPECT_EQ(kEmptyData, data_); } -TEST_F(RetrieverTest, FaultyDownloader) { - // An empty MockDownloader will fail for any request. - Retriever bad_retriever(MockDownloader::kMockDataUrl, - new MockDownloader, - new NullStorage); +TEST_F(RetrieverTest, FaultySource) { + // An empty MockSource will fail for any request. + Retriever bad_retriever(new MockSource, new NullStorage); bad_retriever.Retrieve(kKey, *data_ready_); @@ -152,12 +148,11 @@ class StaleStorage : public Storage { DISALLOW_COPY_AND_ASSIGN(StaleStorage); }; -TEST_F(RetrieverTest, UseStaleDataWhenDownloaderFails) { +TEST_F(RetrieverTest, UseStaleDataWhenSourceFails) { // Owned by |resilient_retriver|. StaleStorage* stale_storage = new StaleStorage; - // An empty MockDownloader will fail for any request. - Retriever resilient_retriever( - MockDownloader::kMockDataUrl, new MockDownloader, stale_storage); + // An empty MockSource will fail for any request. + Retriever resilient_retriever(new MockSource, stale_storage); resilient_retriever.Retrieve(kKey, *data_ready_); @@ -167,11 +162,10 @@ TEST_F(RetrieverTest, UseStaleDataWhenDownloaderFails) { EXPECT_FALSE(stale_storage->data_updated_); } -TEST_F(RetrieverTest, DoNotUseStaleDataWhenDownloaderSucceeds) { +TEST_F(RetrieverTest, DoNotUseStaleDataWhenSourceSucceeds) { // Owned by |resilient_retriver|. StaleStorage* stale_storage = new StaleStorage; - Retriever resilient_retriever( - FakeDownloader::kFakeDataUrl, new FakeDownloader, stale_storage); + Retriever resilient_retriever(new TestdataSource(false), stale_storage); resilient_retriever.Retrieve(kKey, *data_ready_); diff --git a/cpp/test/rule_retriever_test.cc b/cpp/test/rule_retriever_test.cc index 7442bb4..656b23b 100644 --- a/cpp/test/rule_retriever_test.cc +++ b/cpp/test/rule_retriever_test.cc @@ -23,27 +23,26 @@ #include <gtest/gtest.h> -#include "fake_downloader.h" #include "retriever.h" #include "rule.h" +#include "testdata_source.h" namespace { using i18n::addressinput::BuildCallback; -using i18n::addressinput::FakeDownloader; using i18n::addressinput::NullStorage; using i18n::addressinput::Retriever; using i18n::addressinput::Rule; using i18n::addressinput::RuleRetriever; using i18n::addressinput::scoped_ptr; +using i18n::addressinput::TestdataSource; // Tests for RuleRetriever object. class RuleRetrieverTest : public testing::Test { protected: RuleRetrieverTest() - : rule_retriever_(new Retriever(FakeDownloader::kFakeDataUrl, - new FakeDownloader, - new NullStorage)), + : rule_retriever_( + new Retriever(new TestdataSource(false), new NullStorage)), success_(false), key_(), rule_(), diff --git a/cpp/test/supplier_test.cc b/cpp/test/supplier_test.cc index f1e2704..bb783db 100644 --- a/cpp/test/supplier_test.cc +++ b/cpp/test/supplier_test.cc @@ -28,9 +28,9 @@ #include <gtest/gtest.h> -#include "fake_downloader.h" #include "lookup_key.h" #include "rule.h" +#include "testdata_source.h" namespace { @@ -53,7 +53,6 @@ const char kKashiShi[] = "\xE5\x96\x80\xE4\xBB\x80\xE5\xB8\x82"; using i18n::addressinput::AddressData; using i18n::addressinput::BuildCallback; -using i18n::addressinput::FakeDownloader; using i18n::addressinput::LookupKey; using i18n::addressinput::NullStorage; using i18n::addressinput::OndemandSupplier; @@ -61,6 +60,7 @@ using i18n::addressinput::PreloadSupplier; using i18n::addressinput::Rule; using i18n::addressinput::scoped_ptr; using i18n::addressinput::Supplier; +using i18n::addressinput::TestdataSource; class SupplierWrapper { public: @@ -82,9 +82,7 @@ class OndemandSupplierWrapper : public SupplierWrapper { private: OndemandSupplierWrapper() - : ondemand_supplier_(FakeDownloader::kFakeDataUrl, - new FakeDownloader, - new NullStorage) {} + : ondemand_supplier_(new TestdataSource(false), new NullStorage) {} OndemandSupplier ondemand_supplier_; DISALLOW_COPY_AND_ASSIGN(OndemandSupplierWrapper); @@ -107,9 +105,7 @@ class PreloadSupplierWrapper : public SupplierWrapper { private: PreloadSupplierWrapper() - : preload_supplier_(FakeDownloader::kFakeAggregateDataUrl, - new FakeDownloader, - new NullStorage), + : preload_supplier_(new TestdataSource(true), new NullStorage), loaded_(BuildCallback(this, &PreloadSupplierWrapper::Loaded)) {} void Loaded(bool success, const std::string&, int) { ASSERT_TRUE(success); } diff --git a/cpp/test/fake_downloader.cc b/cpp/test/testdata_source.cc index c642eac..d333cb0 100644 --- a/cpp/test/fake_downloader.cc +++ b/cpp/test/testdata_source.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "fake_downloader.h" +#include "testdata_source.h" #include <cassert> #include <cstddef> @@ -23,29 +23,20 @@ #include <string> #include <utility> -#include "lookup_key_util.h" - namespace i18n { namespace addressinput { -// static -const char FakeDownloader::kFakeDataUrl[] = "test:///plain/"; - -// static -const char FakeDownloader::kFakeAggregateDataUrl[] = "test:///aggregate/"; - namespace { +// For historical reasons, normal and aggregated data is here stored in the +// same data structure, differentiated by giving each key a prefix. It does +// seem like a good idea to refactor this. +const char kNormalPrefix = '-'; +const char kAggregatePrefix = '+'; + // The name of the test data file. const char kDataFileName[] = TEST_DATA_DIR "/countryinfo.txt"; -// The number of characters in the fake data URL prefix. -const size_t kFakeDataUrlLength = sizeof FakeDownloader::kFakeDataUrl - 1; - -// The number of characters in the fake aggregate data URL prefix. -const size_t kFakeAggregateDataUrlLength = - sizeof FakeDownloader::kFakeAggregateDataUrl - 1; - // Each data key begins with this string. Example of a data key: // data/CH/AG const char kDataKeyPrefix[] = "data/"; @@ -60,17 +51,6 @@ const size_t kCldrRegionCodeLength = 2; const size_t kAggregateDataKeyLength = kDataKeyPrefixLength + kCldrRegionCodeLength; -const LookupKeyUtil& GetLookupKeyUtil() { - static const LookupKeyUtil kLookupKeyUtil(FakeDownloader::kFakeDataUrl); - return kLookupKeyUtil; -} - -const LookupKeyUtil& GetAggregateLookupKeyUtil() { - static const LookupKeyUtil kLookupKeyUtil( - FakeDownloader::kFakeAggregateDataUrl); - return kLookupKeyUtil; -} - std::map<std::string, std::string> InitData() { std::map<std::string, std::string> data; std::ifstream file(kDataFileName); @@ -79,24 +59,31 @@ std::map<std::string, std::string> InitData() { std::exit(EXIT_FAILURE); } - std::string line; + const std::string normal_prefix(1, kNormalPrefix); + const std::string aggregate_prefix(1, kAggregatePrefix); + + std::string key; + std::string value; + + std::map<std::string, std::string>::iterator last_data_it = data.end(); + std::map<std::string, std::string>::iterator aggregate_data_it = data.end(); + while (file.good()) { // Example line from countryinfo.txt: // data/CH/AG={"name": "Aargau"} - std::getline(file, line); - - std::string::size_type divider = line.find('='); - if (divider != std::string::npos) { - // Example key: - // data/CH/AG - const std::string& key = line.substr(0, divider); + // Example key: + // data/CH/AG + std::getline(file, key, '='); + if (!key.empty()) { // Example value: // {"name": "Aargau"} - const std::string& value = line.substr(divider + 1); + std::getline(file, value, '\n'); - // For example, map 'test:///plain/data/CH/AG' to '{"name": "Aargau"}'. - data.insert(std::make_pair(GetLookupKeyUtil().GetUrlForKey(key), value)); + // For example, map '-data/CH/AG' to '{"name": "Aargau"}'. + last_data_it = data.insert( + last_data_it, + std::make_pair(normal_prefix + key, value)); // Aggregate keys that begin with 'data/'. We don't aggregate keys that // begin with 'example/' because example data is not used anywhere. @@ -104,48 +91,50 @@ std::map<std::string, std::string> InitData() { kDataKeyPrefixLength, kDataKeyPrefix, kDataKeyPrefixLength) == 0) { - // Example aggregate URL: - // test:///aggregate/data/CH - const std::string& aggregate_url = - GetAggregateLookupKeyUtil().GetUrlForKey( - key.substr(0, kAggregateDataKeyLength)); - - std::map<std::string, std::string>::iterator aggregate_data_it = - data.find(aggregate_url); - if (aggregate_data_it != data.end()) { + // If aggregate_data_it and key have the same prefix, e.g. "data/ZZ". + if (aggregate_data_it != data.end() && + key.compare(0, + kAggregateDataKeyLength, + aggregate_data_it->first, + sizeof kAggregatePrefix, + kAggregateDataKeyLength) == 0) { // Append more data to the aggregate string, for example: // , "data/CH/AG": {"name": "Aargau"} aggregate_data_it->second.append(", \"" + key + "\": " + value); } else { + // The countryinfo.txt file must be sorted so that subkey data + // follows directly after its parent key data. + assert(key.size() == kAggregateDataKeyLength); + + // Make the aggregate data strings valid. For example, this incomplete + // JSON data: + // {"data/CH/AG": {"name": "Aargau"}, + // "data/CH": {"name": "SWITZERLAND"} + // + // becomes valid JSON data like so: + // + // {"data/CH/AG": {"name": "Aargau"}, + // "data/CH": {"name": "SWITZERLAND"}} + if (aggregate_data_it != data.end()) { + aggregate_data_it->second.push_back('}'); + } + + // Example aggregate prefixed key: + // +data/CH + const std::string& aggregate_key = + aggregate_prefix + key.substr(0, kAggregateDataKeyLength); + // Begin a new aggregate string, for example: // {"data/CH/AG": {"name": "Aargau"} - data.insert( - std::make_pair(aggregate_url, "{\"" + key + "\": " + value)); + aggregate_data_it = data.insert( + aggregate_data_it, + std::make_pair(aggregate_key, "{\"" + key + "\": " + value)); } } } } - file.close(); - - // Make the aggregate data strings valid. For example, this incomplete JSON - // data: - // {"data/CH/AG": {"name": "Aargau"}, - // "data/CH": {"name": "SWITZERLAND"} - // - // becomes valid JSON data like so: - // - // {"data/CH/AG": {"name": "Aargau"}, - // "data/CH": {"name": "SWITZERLAND"}} - for (std::map<std::string, std::string>::iterator data_it = data.begin(); - data_it != data.end(); ++data_it) { - if (data_it->first.compare(0, - kFakeAggregateDataUrlLength, - FakeDownloader::kFakeAggregateDataUrl, - kFakeAggregateDataUrlLength) == 0) { - data_it->second.append("}"); - } - } + file.close(); return data; } @@ -156,29 +145,29 @@ const std::map<std::string, std::string>& GetData() { } // namespace -FakeDownloader::FakeDownloader() {} +TestdataSource::TestdataSource(bool aggregate) : aggregate_(aggregate) {} -FakeDownloader::~FakeDownloader() {} +TestdataSource::~TestdataSource() {} -void FakeDownloader::Download(const std::string& url, - const Callback& downloaded) const { +void TestdataSource::Get(const std::string& key, + const Callback& data_ready) const { + std::string prefixed_key(1, aggregate_ ? kAggregatePrefix : kNormalPrefix); + prefixed_key += key; std::map<std::string, std::string>::const_iterator data_it = - GetData().find(url); + GetData().find(prefixed_key); bool success = data_it != GetData().end(); std::string* data = NULL; if (success) { data = new std::string(data_it->second); - } else if (GetLookupKeyUtil().IsValidationDataUrl(url) || - GetAggregateLookupKeyUtil().IsValidationDataUrl(url)) { + } else { // URLs that start with "https://i18napis.appspot.com/ssl-address/" or // "https://i18napis.appspot.com/ssl-aggregate-address/" prefix, but do not // have associated data will always return "{}" with status code 200. - // FakeDownloader imitates this behavior for URLs that start with - // "test://address/" and "test://aggregate-address/" prefixes. + // TestdataSource imitates this behavior. success = true; data = new std::string("{}"); } - downloaded(success, url, data); + data_ready(success, key, data); } } // namespace addressinput diff --git a/cpp/test/testdata_source.h b/cpp/test/testdata_source.h new file mode 100644 index 0000000..626483e --- /dev/null +++ b/cpp/test/testdata_source.h @@ -0,0 +1,71 @@ +// Copyright (C) 2013 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// An implementation of the Source interface, that reads address metadata from a +// text file, to be used in tests. + +#ifndef I18N_ADDRESSINPUT_TEST_TESTDATA_SOURCE_H_ +#define I18N_ADDRESSINPUT_TEST_TESTDATA_SOURCE_H_ + +#include <libaddressinput/source.h> +#include <libaddressinput/util/basictypes.h> + +#include <string> + +namespace i18n { +namespace addressinput { + +// Gets address metadata from a text file. Sample usage: +// class MyClass { +// public: +// MyClass() : source_(), +// data_ready_(BuildCallback(this, &MyClass::OnDataReady)) {} +// +// ~MyClass() {} +// +// void GetData(const std::string& key) { +// source_.Get(key, *data_ready_); +// } +// +// private: +// void OnDataReady(bool success, +// const std::string& key, +// std::string* data) { +// ... +// delete data; +// } +// +// TestdataSource source_; +// const scoped_ptr<const Source::Callback> data_ready_; +// +// DISALLOW_COPY_AND_ASSIGN(MyClass); +// }; +class TestdataSource : public Source { + public: + // Will return aggregate data if |aggregate| is set to true. + explicit TestdataSource(bool aggregate); + virtual ~TestdataSource(); + + // Source implementation. + virtual void Get(const std::string& key, const Callback& data_ready) const; + + private: + const bool aggregate_; + DISALLOW_COPY_AND_ASSIGN(TestdataSource); +}; + +} // namespace addressinput +} // namespace i18n + +#endif // I18N_ADDRESSINPUT_TEST_TESTDATA_SOURCE_H_ diff --git a/cpp/test/fake_downloader_test.cc b/cpp/test/testdata_source_test.cc index 29a3983..095b503 100644 --- a/cpp/test/fake_downloader_test.cc +++ b/cpp/test/testdata_source_test.cc @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "fake_downloader.h" +#include "testdata_source.h" #include <libaddressinput/callback.h> -#include <libaddressinput/downloader.h> +#include <libaddressinput/source.h> #include <libaddressinput/util/basictypes.h> #include <libaddressinput/util/scoped_ptr.h> @@ -29,44 +29,46 @@ namespace { using i18n::addressinput::BuildCallback; -using i18n::addressinput::Downloader; -using i18n::addressinput::FakeDownloader; using i18n::addressinput::RegionDataConstants; using i18n::addressinput::scoped_ptr; +using i18n::addressinput::Source; +using i18n::addressinput::TestdataSource; -// Tests for FakeDownloader object. -class FakeDownloaderTest : public testing::TestWithParam<std::string> { +// Tests for TestdataSource object. +class TestdataSourceTest : public testing::TestWithParam<std::string> { protected: - FakeDownloaderTest() - : downloader_(), + TestdataSourceTest() + : source_(false), + aggregate_source_(true), success_(false), - url_(), + key_(), data_(), - downloaded_(BuildCallback(this, &FakeDownloaderTest::OnDownloaded)) {} + data_ready_(BuildCallback(this, &TestdataSourceTest::OnDataReady)) {} - virtual ~FakeDownloaderTest() {} + virtual ~TestdataSourceTest() {} - FakeDownloader downloader_; + TestdataSource source_; + TestdataSource aggregate_source_; bool success_; - std::string url_; + std::string key_; std::string data_; - const scoped_ptr<const Downloader::Callback> downloaded_; + const scoped_ptr<const Source::Callback> data_ready_; private: - void OnDownloaded(bool success, const std::string& url, std::string* data) { + void OnDataReady(bool success, const std::string& key, std::string* data) { ASSERT_FALSE(success && data == NULL); success_ = success; - url_ = url; + key_ = key; if (data != NULL) { data_ = *data; delete data; } } - DISALLOW_COPY_AND_ASSIGN(FakeDownloaderTest); + DISALLOW_COPY_AND_ASSIGN(TestdataSourceTest); }; -// Returns testing::AssertionSuccess if |data| is valid downloaded data for +// Returns testing::AssertionSuccess if |data| is valid callback data for // |key|. testing::AssertionResult DataIsValid(const std::string& data, const std::string& key) { @@ -94,18 +96,17 @@ testing::AssertionResult DataIsValid(const std::string& data, return testing::AssertionSuccess(); } -// Verifies that FakeDownloader downloads valid data for a region code. -TEST_P(FakeDownloaderTest, FakeDownloaderHasValidDataForRegion) { +// Verifies that TestdataSource gets valid data for a region code. +TEST_P(TestdataSourceTest, TestdataSourceHasValidDataForRegion) { std::string key = "data/" + GetParam(); - std::string url = std::string(FakeDownloader::kFakeDataUrl) + key; - downloader_.Download(url, *downloaded_); + source_.Get(key, *data_ready_); EXPECT_TRUE(success_); - EXPECT_EQ(url, url_); + EXPECT_EQ(key, key_); EXPECT_TRUE(DataIsValid(data_, key)); }; -// Returns testing::AssertionSuccess if |data| is valid aggregated downloaded +// Returns testing::AssertionSuccess if |data| is valid aggregated callback // data for |key|. testing::AssertionResult AggregateDataIsValid(const std::string& data, const std::string& key) { @@ -133,75 +134,59 @@ testing::AssertionResult AggregateDataIsValid(const std::string& data, return testing::AssertionSuccess(); } -// Verifies that FakeDownloader downloads valid aggregated data for a region -// code. -TEST_P(FakeDownloaderTest, FakeDownloaderHasValidAggregatedDataForRegion) { +// Verifies that TestdataSource gets valid aggregated data for a region code. +TEST_P(TestdataSourceTest, TestdataSourceHasValidAggregatedDataForRegion) { std::string key = "data/" + GetParam(); - std::string url = std::string(FakeDownloader::kFakeAggregateDataUrl) + key; - downloader_.Download(url, *downloaded_); + aggregate_source_.Get(key, *data_ready_); EXPECT_TRUE(success_); - EXPECT_EQ(url, url_); + EXPECT_EQ(key, key_); EXPECT_TRUE(AggregateDataIsValid(data_, key)); }; // Test all region codes. INSTANTIATE_TEST_CASE_P( - AllRegions, FakeDownloaderTest, + AllRegions, TestdataSourceTest, testing::ValuesIn(RegionDataConstants::GetRegionCodes())); // Verifies that the key "data" also contains valid data. -TEST_F(FakeDownloaderTest, DownloadExistingData) { +TEST_F(TestdataSourceTest, GetExistingData) { static const std::string kKey = "data"; - static const std::string kUrl = - std::string(FakeDownloader::kFakeDataUrl) + kKey; - downloader_.Download(kUrl, *downloaded_); + source_.Get(kKey, *data_ready_); EXPECT_TRUE(success_); - EXPECT_EQ(kUrl, url_); + EXPECT_EQ(kKey, key_); EXPECT_TRUE(DataIsValid(data_, kKey)); } -// Verifies that downloading a missing key will return "{}". -TEST_F(FakeDownloaderTest, DownloadMissingKeyReturnsEmptyDictionary) { - static const std::string kJunkUrl = - std::string(FakeDownloader::kFakeDataUrl) + "junk"; - downloader_.Download(kJunkUrl, *downloaded_); +// Verifies that requesting a missing key will return "{}". +TEST_F(TestdataSourceTest, GetMissingKeyReturnsEmptyDictionary) { + static const std::string kJunkKey = "junk"; + source_.Get(kJunkKey, *data_ready_); EXPECT_TRUE(success_); - EXPECT_EQ(kJunkUrl, url_); + EXPECT_EQ(kJunkKey, key_); EXPECT_EQ("{}", data_); } -// Verifies that aggregate downloading of a missing key will also return "{}". -TEST_F(FakeDownloaderTest, AggregateDownloadMissingKeyReturnsEmptyDictionary) { - static const std::string kJunkUrl = - std::string(FakeDownloader::kFakeAggregateDataUrl) + "junk"; - downloader_.Download(kJunkUrl, *downloaded_); +// Verifies that aggregate requesting of a missing key will also return "{}". +TEST_F(TestdataSourceTest, AggregateGetMissingKeyReturnsEmptyDictionary) { + static const std::string kJunkKey = "junk"; + aggregate_source_.Get(kJunkKey, *data_ready_); EXPECT_TRUE(success_); - EXPECT_EQ(kJunkUrl, url_); + EXPECT_EQ(kJunkKey, key_); EXPECT_EQ("{}", data_); } -// Verifies that downloading an empty key will return "{}". -TEST_F(FakeDownloaderTest, DownloadEmptyKeyReturnsEmptyDictionary) { - static const std::string kPrefixOnlyUrl = FakeDownloader::kFakeDataUrl; - downloader_.Download(kPrefixOnlyUrl, *downloaded_); +// Verifies that requesting an empty key will return "{}". +TEST_F(TestdataSourceTest, GetEmptyKeyReturnsEmptyDictionary) { + static const std::string kEmptyKey; + source_.Get(kEmptyKey, *data_ready_); EXPECT_TRUE(success_); - EXPECT_EQ(kPrefixOnlyUrl, url_); + EXPECT_EQ(kEmptyKey, key_); EXPECT_EQ("{}", data_); } -// Verifies that downloading a real URL fails. -TEST_F(FakeDownloaderTest, DownloadRealUrlFals) { - static const std::string kRealUrl = "http://www.google.com/"; - downloader_.Download(kRealUrl, *downloaded_); - - EXPECT_FALSE(success_); - EXPECT_EQ(kRealUrl, url_); - EXPECT_TRUE(data_.empty()); -} - } // namespace diff --git a/cpp/test/util/json_test.cc b/cpp/test/util/json_test.cc index ff23ea6..594c341 100644 --- a/cpp/test/util/json_test.cc +++ b/cpp/test/util/json_test.cc @@ -120,29 +120,35 @@ TEST(JsonTest, NumberIsNotValid) { TEST(JsonTest, NoDictionaryFound) { Json json; ASSERT_TRUE(json.ParseObject("{\"key\":\"value\"}")); - EXPECT_FALSE(json.HasDictionaryValueForKey("key")); + EXPECT_TRUE(json.GetSubDictionaries().empty()); } TEST(JsonTest, DictionaryFound) { Json json; ASSERT_TRUE(json.ParseObject("{\"key\":{\"inner_key\":\"value\"}}")); - ASSERT_TRUE(json.HasDictionaryValueForKey("key")); - const Json& sub_json = json.GetDictionaryValueForKey("key"); + const std::vector<const Json*>& sub_dicts = json.GetSubDictionaries(); + ASSERT_EQ(1U, sub_dicts.size()); + std::string value; - EXPECT_TRUE(sub_json.GetStringValueForKey("inner_key", &value)); + EXPECT_TRUE(sub_dicts.front()->GetStringValueForKey("inner_key", &value)); EXPECT_EQ("value", value); } -TEST(JsonTest, DictionariesHaveKeys) { +TEST(JsonTest, DictionariesHaveSubDictionaries) { Json json; - ASSERT_TRUE(json.ParseObject("{\"key\":{\"inner_key\":\"value\"}}")); - std::vector<std::string> expected_keys(1, "key"); - EXPECT_EQ(expected_keys, json.GetKeys()); + ASSERT_TRUE(json.ParseObject( + "{\"key\":{\"inner_key\":{\"inner_inner_key\":\"value\"}}}")); + const std::vector<const Json*>& sub_dicts = json.GetSubDictionaries(); + ASSERT_EQ(1U, sub_dicts.size()); + + const std::vector<const Json*>& sub_sub_dicts = + sub_dicts.front()->GetSubDictionaries(); + ASSERT_EQ(1U, sub_sub_dicts.size()); - ASSERT_TRUE(json.HasDictionaryValueForKey("key")); - const Json& sub_json = json.GetDictionaryValueForKey("key"); - std::vector<std::string> expected_sub_keys(1, "inner_key"); - EXPECT_EQ(expected_sub_keys, sub_json.GetKeys()); + std::string value; + EXPECT_TRUE( + sub_sub_dicts.front()->GetStringValueForKey("inner_inner_key", &value)); + EXPECT_EQ("value", value); } } // namespace |