aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2014-08-19 13:00:47 +0100
committerTorne (Richard Coles) <torne@google.com>2014-08-19 13:00:47 +0100
commit495e03c1b62b0b9471886d826c706deb97335436 (patch)
tree2dca327834ceae87288326087f07a927df771938
parent731cb1d936e966db91f45d7d33cdf81a5e50afa9 (diff)
parent00190cd203186aaf923e88df1acd8ce27f208203 (diff)
downloadsrc-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
-rw-r--r--cpp/include/libaddressinput/address_validator.h6
-rw-r--r--cpp/include/libaddressinput/ondemand_supplier.h12
-rw-r--r--cpp/include/libaddressinput/preload_supplier.h24
-rw-r--r--cpp/include/libaddressinput/source.h (renamed from cpp/include/libaddressinput/downloader.h)33
-rw-r--r--cpp/include/libaddressinput/storage.h6
-rw-r--r--cpp/libaddressinput.gypi8
-rw-r--r--cpp/src/address_formatter.cc67
-rw-r--r--cpp/src/lookup_key_util.cc46
-rw-r--r--cpp/src/lookup_key_util.h59
-rw-r--r--cpp/src/ondemand_supplier.cc6
-rw-r--r--cpp/src/ondemand_supply_task.cc9
-rw-r--r--cpp/src/preload_supplier.cc81
-rw-r--r--cpp/src/region_data_builder.cc87
-rw-r--r--cpp/src/region_data_constants.cc4
-rw-r--r--cpp/src/retriever.cc35
-rw-r--r--cpp/src/retriever.h30
-rw-r--r--cpp/src/util/json.cc163
-rw-r--r--cpp/src/util/json.h22
-rw-r--r--cpp/test/address_formatter_test.cc159
-rw-r--r--cpp/test/address_input_helper_test.cc26
-rw-r--r--cpp/test/address_normalizer_test.cc8
-rw-r--r--cpp/test/address_validator_test.cc12
-rw-r--r--cpp/test/fake_downloader.h75
-rw-r--r--cpp/test/fake_storage.h6
-rw-r--r--cpp/test/fake_storage_test.cc4
-rw-r--r--cpp/test/lookup_key_util_test.cc53
-rw-r--r--cpp/test/mock_downloader.h79
-rw-r--r--cpp/test/mock_source.cc (renamed from cpp/test/mock_downloader.cc)24
-rw-r--r--cpp/test/mock_source.h74
-rw-r--r--cpp/test/ondemand_supply_task_test.cc36
-rw-r--r--cpp/test/preload_supplier_test.cc16
-rw-r--r--cpp/test/region_data_builder_test.cc7
-rw-r--r--cpp/test/retriever_test.cc34
-rw-r--r--cpp/test/rule_retriever_test.cc9
-rw-r--r--cpp/test/supplier_test.cc12
-rw-r--r--cpp/test/testdata_source.cc (renamed from cpp/test/fake_downloader.cc)147
-rw-r--r--cpp/test/testdata_source.h71
-rw-r--r--cpp/test/testdata_source_test.cc (renamed from cpp/test/fake_downloader_test.cc)111
-rw-r--r--cpp/test/util/json_test.cc30
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_,
+ &region_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