Index: components/ntp_tiles/popular_sites_unittest.cc |
diff --git a/components/ntp_tiles/popular_sites_unittest.cc b/components/ntp_tiles/popular_sites_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..76071cb36e82cf8f2041b5200c4b4ec505d7a725 |
--- /dev/null |
+++ b/components/ntp_tiles/popular_sites_unittest.cc |
@@ -0,0 +1,224 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "components/ntp_tiles/popular_sites.h" |
+ |
+#include <string> |
+#include <utility> |
+#include <vector> |
+ |
+#include "base/bind.h" |
+#include "base/files/scoped_temp_dir.h" |
+#include "base/json/json_parser.h" |
+#include "base/json/json_writer.h" |
+#include "base/run_loop.h" |
+#include "base/strings/stringprintf.h" |
+#include "base/test/sequenced_worker_pool_owner.h" |
+#include "base/threading/thread_task_runner_handle.h" |
+#include "base/values.h" |
+#include "components/ntp_tiles/pref_names.h" |
+#include "components/pref_registry/pref_registry_syncable.h" |
+#include "components/pref_registry/testing_pref_service_syncable.h" |
+#include "net/http/http_status_code.h" |
+#include "net/url_request/test_url_fetcher_factory.h" |
+#include "net/url_request/url_request_status.h" |
+#include "net/url_request/url_request_test_util.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+using testing::Eq; |
+using testing::StrEq; |
+ |
+namespace ntp_tiles { |
+namespace { |
+ |
+const char kTitle[] = "title"; |
+const char kUrl[] = "url"; |
+const char kLargeIconUrl[] = "large_icon_url"; |
+const char kFaviconUrl[] = "favicon_url"; |
+ |
+using TestPopularSite = std::vector<std::pair<std::string, std::string>>; |
+using TestPopularSiteVector = std::vector<TestPopularSite>; |
+ |
+::testing::Matcher<const base::string16&> Str16Eq(const std::string& s) { |
+ return ::testing::Eq(base::UTF8ToUTF16(s)); |
+} |
+ |
+::testing::Matcher<const GURL&> URLEq(const std::string& s) { |
+ return ::testing::Eq(GURL(s)); |
+} |
+ |
+// Copied from iOS code. Perhaps should be in a shared location. |
+class JsonUnsafeParser { |
+ public: |
+ using SuccessCallback = base::Callback<void(std::unique_ptr<base::Value>)>; |
+ using ErrorCallback = base::Callback<void(const std::string&)>; |
+ |
+ // As with SafeJsonParser, runs either success_callback or error_callback on |
+ // the calling thread, but not before the call returns. |
+ static void Parse(const std::string& unsafe_json, |
+ const SuccessCallback& success_callback, |
+ const ErrorCallback& error_callback) { |
+ base::ThreadTaskRunnerHandle::Get()->PostTask( |
+ FROM_HERE, |
+ base::Bind( |
+ [](const std::string& unsafe_json, |
+ const SuccessCallback& success_callback, |
+ const ErrorCallback& error_callback) { |
+ std::string error_msg; |
+ int error_line, error_column; |
+ std::unique_ptr<base::Value> value = |
+ base::JSONReader::ReadAndReturnError( |
+ unsafe_json, base::JSON_ALLOW_TRAILING_COMMAS, nullptr, |
+ &error_msg, &error_line, &error_column); |
+ if (value) { |
+ success_callback.Run(std::move(value)); |
+ } else { |
+ error_callback.Run(base::StringPrintf( |
+ "%s (%d:%d)", error_msg.c_str(), error_line, error_column)); |
+ } |
+ }, |
+ unsafe_json, success_callback, error_callback)); |
+ } |
+ |
+ JsonUnsafeParser() = delete; |
+}; |
+ |
+class PopularSitesTest : public ::testing::Test { |
+ protected: |
+ PopularSitesTest() |
+ : worker_pool_owner_(2, "PopularSitesTest."), |
+ url_fetcher_factory_(nullptr) { |
+ PopularSites::RegisterProfilePrefs(prefs_.registry()); |
+ CHECK(cache_dir_.CreateUniqueTempDir()); |
+ } |
+ |
+ void SetCountryAndVersion(const std::string& country, |
+ const std::string& version) { |
+ prefs_.SetString(prefs::kPopularSitesOverrideCountry, country); |
+ prefs_.SetString(prefs::kPopularSitesOverrideVersion, version); |
+ } |
+ |
+ void RespondWithJSON(const std::string& url, |
+ const TestPopularSiteVector& sites) { |
+ base::ListValue sites_value; |
+ for (const TestPopularSite& site : sites) { |
+ auto site_value = base::MakeUnique<base::DictionaryValue>(); |
+ for (const std::pair<std::string, std::string>& kv : site) { |
+ site_value->SetString(kv.first, kv.second); |
+ } |
+ sites_value.Append(std::move(site_value)); |
+ } |
+ std::string sites_string; |
+ base::JSONWriter::Write(sites_value, &sites_string); |
+ url_fetcher_factory_.SetFakeResponse(GURL(url), sites_string, net::HTTP_OK, |
+ net::URLRequestStatus::SUCCESS); |
+ } |
+ |
+ void RespondWith404(const std::string& url) { |
+ url_fetcher_factory_.SetFakeResponse(GURL(url), "404", net::HTTP_NOT_FOUND, |
+ net::URLRequestStatus::SUCCESS); |
+ } |
+ |
+ bool FetchPopularSites(bool force_download, |
+ std::vector<PopularSites::Site>* sites) { |
+ scoped_refptr<net::TestURLRequestContextGetter> url_request_context( |
+ new net::TestURLRequestContextGetter( |
+ base::ThreadTaskRunnerHandle::Get())); |
+ PopularSites popular_sites(worker_pool_owner_.pool().get(), &prefs_, |
+ /*template_url_service=*/nullptr, |
+ /*variations_service=*/nullptr, |
+ url_request_context.get(), cache_dir_.GetPath(), |
+ base::Bind(JsonUnsafeParser::Parse)); |
+ |
+ base::RunLoop loop; |
+ bool save_success = false; |
+ popular_sites.StartFetch( |
+ force_download, |
+ base::Bind( |
+ [](bool* save_success, base::RunLoop* loop, bool success) { |
+ *save_success = success; |
+ loop->Quit(); |
+ }, |
+ &save_success, &loop)); |
+ loop.Run(); |
+ *sites = popular_sites.sites(); |
+ return save_success; |
+ } |
+ |
+ base::MessageLoopForUI ui_loop_; |
+ base::SequencedWorkerPoolOwner worker_pool_owner_; |
+ base::ScopedTempDir cache_dir_; |
+ user_prefs::TestingPrefServiceSyncable prefs_; |
+ net::FakeURLFetcherFactory url_fetcher_factory_; |
+}; |
+ |
+TEST_F(PopularSitesTest, Basic) { |
+ SetCountryAndVersion("ZZ", "9"); |
+ RespondWithJSON( |
+ "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_9.json", |
+ {{ |
+ {kTitle, "Wikipedia, fhta Ph'nglui mglw'nafh"}, |
+ {kUrl, "https://zz.m.wikipedia.org/"}, |
+ {kLargeIconUrl, "https://zz.m.wikipedia.org/wikipedia.png"}, |
+ }}); |
+ |
+ std::vector<PopularSites::Site> sites; |
+ EXPECT_TRUE(FetchPopularSites(/*force_download=*/false, &sites)); |
+ |
+ ASSERT_THAT(sites.size(), Eq(1u)); |
+ EXPECT_THAT(sites[0].title, Str16Eq("Wikipedia, fhta Ph'nglui mglw'nafh")); |
+ EXPECT_THAT(sites[0].url, URLEq("https://zz.m.wikipedia.org/")); |
+ EXPECT_THAT(sites[0].large_icon_url, |
+ URLEq("https://zz.m.wikipedia.org/wikipedia.png")); |
+ EXPECT_THAT(sites[0].favicon_url, URLEq("")); |
+} |
+ |
+TEST_F(PopularSitesTest, Fallback) { |
+ SetCountryAndVersion("ZZ", "9"); |
+ RespondWith404( |
+ "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_9.json"); |
+ RespondWithJSON( |
+ "https://www.gstatic.com/chrome/ntp/suggested_sites_DEFAULT_5.json", |
+ {{ |
+ {kTitle, "YouTube"}, |
+ {kUrl, "https://m.youtube.com/"}, |
+ {kLargeIconUrl, "https://s.ytimg.com/apple-touch-icon.png"}, |
+ }, |
+ { |
+ {kTitle, "The Chromium Project"}, |
+ {kUrl, "https://www.chromium.org/"}, |
+ {kFaviconUrl, "https://www.chromium.org/favicon.ico"}, |
+ }}); |
+ |
+ std::vector<PopularSites::Site> sites; |
+ EXPECT_TRUE(FetchPopularSites(/*force_download=*/false, &sites)); |
+ |
+ ASSERT_THAT(sites.size(), Eq(2u)); |
+ EXPECT_THAT(sites[0].title, Str16Eq("YouTube")); |
+ EXPECT_THAT(sites[0].url, URLEq("https://m.youtube.com/")); |
+ EXPECT_THAT(sites[0].large_icon_url, |
+ URLEq("https://s.ytimg.com/apple-touch-icon.png")); |
+ EXPECT_THAT(sites[0].favicon_url, URLEq("")); |
+ EXPECT_THAT(sites[1].title, Str16Eq("The Chromium Project")); |
+ EXPECT_THAT(sites[1].url, URLEq("https://www.chromium.org/")); |
+ EXPECT_THAT(sites[1].large_icon_url, URLEq("")); |
+ EXPECT_THAT(sites[1].favicon_url, |
+ URLEq("https://www.chromium.org/favicon.ico")); |
+} |
+ |
+TEST_F(PopularSitesTest, Failure) { |
+ SetCountryAndVersion("ZZ", "9"); |
+ RespondWith404( |
+ "https://www.gstatic.com/chrome/ntp/suggested_sites_ZZ_9.json"); |
+ RespondWith404( |
+ "https://www.gstatic.com/chrome/ntp/suggested_sites_DEFAULT_5.json"); |
+ |
+ std::vector<PopularSites::Site> sites; |
+ EXPECT_FALSE(FetchPopularSites(/*force_download=*/false, &sites)); |
+ ASSERT_THAT(sites.size(), Eq(0u)); |
+} |
+ |
+} // namespace |
+} // namespace ntp_tiles |