Refactor the Downloader interface into the Source interface.

Most implementations of this interface don't actually download anything,
so it's more logical to name the interface something else and define it
in terms of lookup keys ("data/CH") instead of URLs, and let those
implementations that do perform network access handle the mapping from
keys to URLs instead.

This simplifies most implementations of this interface and also the
Retriever, which now no longer needs to keep track of the base URL
(which in most cases was bogus anyway).

This change will break all current users of the libaddressinput API.

Ad infinitum et ultra!

TBR=rouslan@chromium.org
BUG=

Review URL: https://codereview.appspot.com/115680043

git-svn-id: http://libaddressinput.googlecode.com/svn/trunk@326 38ededc0-08b8-5190-f2ac-b31f878777ad
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 @@
 //    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 @@
 //      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/downloader.h b/cpp/include/libaddressinput/downloader.h
deleted file mode 100644
index 0da4e1f..0000000
--- a/cpp/include/libaddressinput/downloader.h
+++ /dev/null
@@ -1,55 +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.
-//
-// The interface to be implemented by the user of the library to enable
-// downloading validation rules from a server.
-
-#ifndef I18N_ADDRESSINPUT_DOWNLOADER_H_
-#define I18N_ADDRESSINPUT_DOWNLOADER_H_
-
-#include <libaddressinput/callback.h>
-
-#include <string>
-
-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:
-//
-//    class MyDownloader : public Downloader {
-//     public:
-//      virtual void Download(const std::string& url,
-//                            const Callback& downloaded) const {
-//        bool success = ...
-//        std::string* data = new ...
-//        downloaded(success, url, data);
-//      }
-//    };
-class Downloader {
- public:
-  typedef i18n::addressinput::Callback<const std::string&,
-                                       std::string*> Callback;
-
-  virtual ~Downloader() {}
-
-  // Downloads |url| and invokes the |downloaded| callback.
-  virtual void Download(const std::string& url,
-                        const Callback& downloaded) const = 0;
-};
-
-}  // namespace addressinput
-}  // namespace i18n
-
-#endif  // I18N_ADDRESSINPUT_DOWNLOADER_H_
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 @@
 // 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 a4025cb..5987c79 100644
--- a/cpp/include/libaddressinput/preload_supplier.h
+++ b/cpp/include/libaddressinput/preload_supplier.h
@@ -28,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,
@@ -42,8 +42,8 @@
 // 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
@@ -52,15 +52,8 @@
  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
diff --git a/cpp/include/libaddressinput/source.h b/cpp/include/libaddressinput/source.h
new file mode 100644
index 0000000..4d91b68
--- /dev/null
+++ b/cpp/include/libaddressinput/source.h
@@ -0,0 +1,56 @@
+// 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.
+//
+// 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_SOURCE_H_
+#define I18N_ADDRESSINPUT_SOURCE_H_
+
+#include <libaddressinput/callback.h>
+
+#include <string>
+
+namespace i18n {
+namespace addressinput {
+
+// Gets address metadata. The callback data must be allocated on the heap,
+// passing ownership to the callback. Sample usage:
+//
+//    class MySource : public Source {
+//     public:
+//      virtual void Get(const std::string& key,
+//                       const Callback& data_ready) const {
+//        bool success = ...
+//        std::string* data = new ...
+//        data_ready(success, key, data);
+//      }
+//    };
+class Source {
+ public:
+  typedef i18n::addressinput::Callback<const std::string&,
+                                       std::string*> Callback;
+
+  virtual ~Source() {}
+
+  // 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_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/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/preload_supplier.cc b/cpp/src/preload_supplier.cc
index 2557e33..79119bd 100644
--- a/cpp/src/preload_supplier.cc
+++ b/cpp/src/preload_supplier.cc
@@ -271,10 +271,8 @@
 
 }  // 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_(),
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 @@
   // 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 @@
       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 @@
   }
 
   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 @@
 
 }  // 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 @@
   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/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::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 @@
 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 @@
     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 @@
        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 @@
   // 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 @@
 
  private:
   OndemandValidatorWrapper()
-      : supplier_(FakeDownloader::kFakeDataUrl,
-                  new FakeDownloader,
-                  new NullStorage),
+      : supplier_(new TestdataSource(false), new NullStorage),
         validator_(&supplier_) {}
 
   OndemandSupplier supplier_;
@@ -126,9 +124,7 @@
 
  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_downloader_test.cc b/cpp/test/fake_downloader_test.cc
deleted file mode 100644
index 29a3983..0000000
--- a/cpp/test/fake_downloader_test.cc
+++ /dev/null
@@ -1,207 +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 "fake_downloader.h"
-
-#include <libaddressinput/callback.h>
-#include <libaddressinput/downloader.h>
-#include <libaddressinput/util/basictypes.h>
-#include <libaddressinput/util/scoped_ptr.h>
-
-#include <cstddef>
-#include <string>
-
-#include <gtest/gtest.h>
-
-#include "region_data_constants.h"
-
-namespace {
-
-using i18n::addressinput::BuildCallback;
-using i18n::addressinput::Downloader;
-using i18n::addressinput::FakeDownloader;
-using i18n::addressinput::RegionDataConstants;
-using i18n::addressinput::scoped_ptr;
-
-// Tests for FakeDownloader object.
-class FakeDownloaderTest : public testing::TestWithParam<std::string> {
- protected:
-  FakeDownloaderTest()
-      : downloader_(),
-        success_(false),
-        url_(),
-        data_(),
-        downloaded_(BuildCallback(this, &FakeDownloaderTest::OnDownloaded)) {}
-
-  virtual ~FakeDownloaderTest() {}
-
-  FakeDownloader downloader_;
-  bool success_;
-  std::string url_;
-  std::string data_;
-  const scoped_ptr<const Downloader::Callback> downloaded_;
-
- private:
-  void OnDownloaded(bool success, const std::string& url, std::string* data) {
-    ASSERT_FALSE(success && data == NULL);
-    success_ = success;
-    url_ = url;
-    if (data != NULL) {
-      data_ = *data;
-      delete data;
-    }
-  }
-
-  DISALLOW_COPY_AND_ASSIGN(FakeDownloaderTest);
-};
-
-// Returns testing::AssertionSuccess if |data| is valid downloaded data for
-// |key|.
-testing::AssertionResult DataIsValid(const std::string& data,
-                                     const std::string& key) {
-  if (data.empty()) {
-    return testing::AssertionFailure() << "empty data";
-  }
-
-  std::string expected_data_begin = "{\"id\":\"" + key + "\"";
-  if (data.compare(0, expected_data_begin.length(), expected_data_begin) != 0) {
-    return testing::AssertionFailure() << data << " does not begin with "
-                                       << expected_data_begin;
-  }
-
-  // Verify that the data ends on "}.
-  static const char kDataEnd[] = "\"}";
-  static const size_t kDataEndLength = sizeof kDataEnd - 1;
-  if (data.compare(data.length() - kDataEndLength,
-                   kDataEndLength,
-                   kDataEnd,
-                   kDataEndLength) != 0) {
-    return testing::AssertionFailure() << data << " does not end with "
-                                       << kDataEnd;
-  }
-
-  return testing::AssertionSuccess();
-}
-
-// Verifies that FakeDownloader downloads valid data for a region code.
-TEST_P(FakeDownloaderTest, FakeDownloaderHasValidDataForRegion) {
-  std::string key = "data/" + GetParam();
-  std::string url = std::string(FakeDownloader::kFakeDataUrl) + key;
-  downloader_.Download(url, *downloaded_);
-
-  EXPECT_TRUE(success_);
-  EXPECT_EQ(url, url_);
-  EXPECT_TRUE(DataIsValid(data_, key));
-};
-
-// Returns testing::AssertionSuccess if |data| is valid aggregated downloaded
-// data for |key|.
-testing::AssertionResult AggregateDataIsValid(const std::string& data,
-                                              const std::string& key) {
-  if (data.empty()) {
-    return testing::AssertionFailure() << "empty data";
-  }
-
-  std::string expected_data_begin = "{\"" + key;
-  if (data.compare(0, expected_data_begin.length(), expected_data_begin) != 0) {
-    return testing::AssertionFailure() << data << " does not begin with "
-                                       << expected_data_begin;
-  }
-
-  // Verify that the data ends on "}}.
-  static const char kDataEnd[] = "\"}}";
-  static const size_t kDataEndLength = sizeof kDataEnd - 1;
-  if (data.compare(data.length() - kDataEndLength,
-                   kDataEndLength,
-                   kDataEnd,
-                   kDataEndLength) != 0) {
-    return testing::AssertionFailure() << data << " does not end with "
-                                       << kDataEnd;
-  }
-
-  return testing::AssertionSuccess();
-}
-
-// Verifies that FakeDownloader downloads valid aggregated data for a region
-// code.
-TEST_P(FakeDownloaderTest, FakeDownloaderHasValidAggregatedDataForRegion) {
-  std::string key = "data/" + GetParam();
-  std::string url = std::string(FakeDownloader::kFakeAggregateDataUrl) + key;
-  downloader_.Download(url, *downloaded_);
-
-  EXPECT_TRUE(success_);
-  EXPECT_EQ(url, url_);
-  EXPECT_TRUE(AggregateDataIsValid(data_, key));
-};
-
-// Test all region codes.
-INSTANTIATE_TEST_CASE_P(
-    AllRegions, FakeDownloaderTest,
-    testing::ValuesIn(RegionDataConstants::GetRegionCodes()));
-
-// Verifies that the key "data" also contains valid data.
-TEST_F(FakeDownloaderTest, DownloadExistingData) {
-  static const std::string kKey = "data";
-  static const std::string kUrl =
-      std::string(FakeDownloader::kFakeDataUrl) + kKey;
-  downloader_.Download(kUrl, *downloaded_);
-
-  EXPECT_TRUE(success_);
-  EXPECT_EQ(kUrl, url_);
-  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_);
-
-  EXPECT_TRUE(success_);
-  EXPECT_EQ(kJunkUrl, url_);
-  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_);
-
-  EXPECT_TRUE(success_);
-  EXPECT_EQ(kJunkUrl, url_);
-  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_);
-
-  EXPECT_TRUE(success_);
-  EXPECT_EQ(kPrefixOnlyUrl, url_);
-  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/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 @@
 //    class MyClass {
 //     public:
 //      MyClass() : storage_(),
-//                  callback(BuildCallback(this, &MyClass::OnDataReady)) {}
+//                  data_ready_(BuildCallback(this, &MyClass::OnDataReady)) {}
 //
 //      ~MyClass() {}
 //
@@ -39,7 +39,7 @@
 //      }
 //
 //      void Read() {
-//        storage_.Get("key", *callback_);
+//        storage_.Get("key", *data_ready_);
 //      }
 //
 //     private:
@@ -51,7 +51,7 @@
 //      }
 //
 //      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 @@
   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.cc b/cpp/test/mock_downloader.cc
deleted file mode 100644
index 71256aa..0000000
--- a/cpp/test/mock_downloader.cc
+++ /dev/null
@@ -1,49 +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.
-
-#include "mock_downloader.h"
-
-#include <cassert>
-#include <cstddef>
-#include <map>
-#include <string>
-
-namespace i18n {
-namespace addressinput {
-
-// static
-const char MockDownloader::kMockDataUrl[] = "mock:///";
-
-namespace {
-
-// 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);
-  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);
-}
-
-}  // namespace addressinput
-}  // namespace i18n
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_source.cc b/cpp/test/mock_source.cc
new file mode 100644
index 0000000..d04598b
--- /dev/null
+++ b/cpp/test/mock_source.cc
@@ -0,0 +1,35 @@
+// 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.
+
+#include "mock_source.h"
+
+#include <cstddef>
+#include <map>
+#include <string>
+
+namespace i18n {
+namespace addressinput {
+
+MockSource::MockSource() {}
+
+MockSource::~MockSource() {}
+
+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();
+  data_ready(success, key, success ? new std::string(it->second) : NULL);
+}
+
+}  // namespace addressinput
+}  // namespace i18n
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 @@
 
 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 @@
         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 @@
 
   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, 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, 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, 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, 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, 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, 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 7181ef2..999ee21 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() {}
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 @@
 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 @@
   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 @@
   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 @@
   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 @@
 
 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::Rule;
 using i18n::addressinput::scoped_ptr;
 using i18n::addressinput::Supplier;
+using i18n::addressinput::TestdataSource;
 
 class SupplierWrapper {
  public:
@@ -82,9 +82,7 @@
 
  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 @@
 
  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
similarity index 71%
rename from cpp/test/fake_downloader.cc
rename to cpp/test/testdata_source.cc
index 1795b2b..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 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,6 +59,9 @@
     std::exit(EXIT_FAILURE);
   }
 
+  const std::string normal_prefix(1, kNormalPrefix);
+  const std::string aggregate_prefix(1, kAggregatePrefix);
+
   std::string key;
   std::string value;
 
@@ -97,10 +80,10 @@
       //     {"name": "Aargau"}
       std::getline(file, value, '\n');
 
-      // For example, map 'test:///plain/data/CH/AG' to '{"name": "Aargau"}'.
+      // For example, map '-data/CH/AG' to '{"name": "Aargau"}'.
       last_data_it = data.insert(
           last_data_it,
-          std::make_pair(GetLookupKeyUtil().GetUrlForKey(key), value));
+          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.
@@ -108,18 +91,12 @@
                       kDataKeyPrefixLength,
                       kDataKeyPrefix,
                       kDataKeyPrefixLength) == 0) {
-        // Example aggregate URL:
-        //     test:///aggregate/data/CH
-        const std::string& aggregate_url =
-            GetAggregateLookupKeyUtil().GetUrlForKey(
-                key.substr(0, kAggregateDataKeyLength));
-
         // 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,
-                        kFakeAggregateDataUrlLength,
+                        sizeof kAggregatePrefix,
                         kAggregateDataKeyLength) == 0) {
           // Append more data to the aggregate string, for example:
           //     , "data/CH/AG": {"name": "Aargau"}
@@ -142,11 +119,16 @@
             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"}
           aggregate_data_it = data.insert(
               aggregate_data_it,
-              std::make_pair(aggregate_url, "{\"" + key + "\": " + value));
+              std::make_pair(aggregate_key, "{\"" + key + "\": " + value));
         }
       }
     }
@@ -163,29 +145,29 @@
 
 }  // 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/testdata_source_test.cc b/cpp/test/testdata_source_test.cc
new file mode 100644
index 0000000..095b503
--- /dev/null
+++ b/cpp/test/testdata_source_test.cc
@@ -0,0 +1,192 @@
+// 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 "testdata_source.h"
+
+#include <libaddressinput/callback.h>
+#include <libaddressinput/source.h>
+#include <libaddressinput/util/basictypes.h>
+#include <libaddressinput/util/scoped_ptr.h>
+
+#include <cstddef>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "region_data_constants.h"
+
+namespace {
+
+using i18n::addressinput::BuildCallback;
+using i18n::addressinput::RegionDataConstants;
+using i18n::addressinput::scoped_ptr;
+using i18n::addressinput::Source;
+using i18n::addressinput::TestdataSource;
+
+// Tests for TestdataSource object.
+class TestdataSourceTest : public testing::TestWithParam<std::string> {
+ protected:
+  TestdataSourceTest()
+      : source_(false),
+        aggregate_source_(true),
+        success_(false),
+        key_(),
+        data_(),
+        data_ready_(BuildCallback(this, &TestdataSourceTest::OnDataReady)) {}
+
+  virtual ~TestdataSourceTest() {}
+
+  TestdataSource source_;
+  TestdataSource aggregate_source_;
+  bool success_;
+  std::string key_;
+  std::string data_;
+  const scoped_ptr<const Source::Callback> data_ready_;
+
+ private:
+  void OnDataReady(bool success, const std::string& key, std::string* data) {
+    ASSERT_FALSE(success && data == NULL);
+    success_ = success;
+    key_ = key;
+    if (data != NULL) {
+      data_ = *data;
+      delete data;
+    }
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(TestdataSourceTest);
+};
+
+// Returns testing::AssertionSuccess if |data| is valid callback data for
+// |key|.
+testing::AssertionResult DataIsValid(const std::string& data,
+                                     const std::string& key) {
+  if (data.empty()) {
+    return testing::AssertionFailure() << "empty data";
+  }
+
+  std::string expected_data_begin = "{\"id\":\"" + key + "\"";
+  if (data.compare(0, expected_data_begin.length(), expected_data_begin) != 0) {
+    return testing::AssertionFailure() << data << " does not begin with "
+                                       << expected_data_begin;
+  }
+
+  // Verify that the data ends on "}.
+  static const char kDataEnd[] = "\"}";
+  static const size_t kDataEndLength = sizeof kDataEnd - 1;
+  if (data.compare(data.length() - kDataEndLength,
+                   kDataEndLength,
+                   kDataEnd,
+                   kDataEndLength) != 0) {
+    return testing::AssertionFailure() << data << " does not end with "
+                                       << kDataEnd;
+  }
+
+  return testing::AssertionSuccess();
+}
+
+// Verifies that TestdataSource gets valid data for a region code.
+TEST_P(TestdataSourceTest, TestdataSourceHasValidDataForRegion) {
+  std::string key = "data/" + GetParam();
+  source_.Get(key, *data_ready_);
+
+  EXPECT_TRUE(success_);
+  EXPECT_EQ(key, key_);
+  EXPECT_TRUE(DataIsValid(data_, key));
+};
+
+// Returns testing::AssertionSuccess if |data| is valid aggregated callback
+// data for |key|.
+testing::AssertionResult AggregateDataIsValid(const std::string& data,
+                                              const std::string& key) {
+  if (data.empty()) {
+    return testing::AssertionFailure() << "empty data";
+  }
+
+  std::string expected_data_begin = "{\"" + key;
+  if (data.compare(0, expected_data_begin.length(), expected_data_begin) != 0) {
+    return testing::AssertionFailure() << data << " does not begin with "
+                                       << expected_data_begin;
+  }
+
+  // Verify that the data ends on "}}.
+  static const char kDataEnd[] = "\"}}";
+  static const size_t kDataEndLength = sizeof kDataEnd - 1;
+  if (data.compare(data.length() - kDataEndLength,
+                   kDataEndLength,
+                   kDataEnd,
+                   kDataEndLength) != 0) {
+    return testing::AssertionFailure() << data << " does not end with "
+                                       << kDataEnd;
+  }
+
+  return testing::AssertionSuccess();
+}
+
+// Verifies that TestdataSource gets valid aggregated data for a region code.
+TEST_P(TestdataSourceTest, TestdataSourceHasValidAggregatedDataForRegion) {
+  std::string key = "data/" + GetParam();
+  aggregate_source_.Get(key, *data_ready_);
+
+  EXPECT_TRUE(success_);
+  EXPECT_EQ(key, key_);
+  EXPECT_TRUE(AggregateDataIsValid(data_, key));
+};
+
+// Test all region codes.
+INSTANTIATE_TEST_CASE_P(
+    AllRegions, TestdataSourceTest,
+    testing::ValuesIn(RegionDataConstants::GetRegionCodes()));
+
+// Verifies that the key "data" also contains valid data.
+TEST_F(TestdataSourceTest, GetExistingData) {
+  static const std::string kKey = "data";
+  source_.Get(kKey, *data_ready_);
+
+  EXPECT_TRUE(success_);
+  EXPECT_EQ(kKey, key_);
+  EXPECT_TRUE(DataIsValid(data_, kKey));
+}
+
+// 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(kJunkKey, key_);
+  EXPECT_EQ("{}", data_);
+}
+
+// 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(kJunkKey, key_);
+  EXPECT_EQ("{}", data_);
+}
+
+// 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(kEmptyKey, key_);
+  EXPECT_EQ("{}", data_);
+}
+
+}  // namespace