Chromium Code Reviews| Index: components/ntp_snippets/remote/ntp_snippets_fetcher_unittest.cc |
| diff --git a/components/ntp_snippets/remote/ntp_snippets_fetcher_unittest.cc b/components/ntp_snippets/remote/ntp_snippets_fetcher_unittest.cc |
| index c02ffa4753d5056f26eddc47663323831c844361..6824f9a2679644106a55e8815ae4af73e16d89bc 100644 |
| --- a/components/ntp_snippets/remote/ntp_snippets_fetcher_unittest.cc |
| +++ b/components/ntp_snippets/remote/ntp_snippets_fetcher_unittest.cc |
| @@ -4,6 +4,7 @@ |
| #include "components/ntp_snippets/remote/ntp_snippets_fetcher.h" |
| +#include <list> |
| #include <map> |
| #include <utility> |
| @@ -16,6 +17,7 @@ |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "components/ntp_snippets/category_factory.h" |
| +#include "components/ntp_snippets/features.h" |
| #include "components/ntp_snippets/ntp_snippets_constants.h" |
| #include "components/ntp_snippets/remote/ntp_snippet.h" |
| #include "components/ntp_snippets/user_classifier.h" |
| @@ -158,6 +160,81 @@ class MockSnippetsAvailableCallback { |
| void(NTPSnippetsFetcher::OptionalFetchedCategories* fetched_categories)); |
| }; |
| +// DelegateCallingTestURLFetcherFactory can be used to temporarily inject fake |
| +// url fetcher instances into a scope. |
|
Marc Treib
2016/12/09 12:59:24
s/fake url fetcher/TestURLFetcher/ ?
Since both Te
fhorschig
2016/12/12 09:54:03
Done.
|
| +// Client code can access the last created fetcher to verify expected |
| +// properties. When the factory gets destroyed, all available delegates of still |
| +// valid fetchers will be called. |
| +// This ensures once-bound callbacks (like SnippetsAvailableCallback) will be |
| +// called at some point and are not leaked. |
| +class DelegateCallingTestURLFetcherFactory |
|
tschumann
2016/12/09 12:53:38
hmm... I wonder why the previous (much simpler ver
fhorschig
2016/12/12 09:54:03
Yes, you mentioned that. I replaced that multiple
tschumann
2016/12/12 10:00:42
to be more precise, what's the new bit of function
fhorschig
2016/12/12 11:05:10
It's true that we deal with a lot of implementatio
|
| + : public net::TestURLFetcherFactory, |
| + public net::TestURLFetcherDelegateForTests { |
| + public: |
| + DelegateCallingTestURLFetcherFactory() { |
| + SetDelegateForTests(this); |
| + set_remove_fetcher_on_delete(true); |
| + } |
| + |
| + ~DelegateCallingTestURLFetcherFactory() override { |
| + while (!fetcher_id_queue_.empty()) { |
| + DropAndCallDelegate(fetcher_id_queue_.front()); |
| + } |
| + } |
| + |
| + std::unique_ptr<net::URLFetcher> CreateURLFetcher( |
| + int id, |
| + const GURL& url, |
| + net::URLFetcher::RequestType request_type, |
| + net::URLFetcherDelegate* d) override { |
| + if (GetFetcherByID(id)) { |
| + LOG(ERROR) << "The ID " << id << " was already assigned to a fetcher." |
|
Marc Treib
2016/12/09 12:59:24
Is this actually an error? If so, should we just f
fhorschig
2016/12/12 09:54:03
It's a warning now.
I thought about just failing
|
| + << "Its delegate will thereforde be called right now."; |
| + DropAndCallDelegate(id); |
| + } |
| + fetcher_id_queue_.push_back(id); |
| + return TestURLFetcherFactory::CreateURLFetcher(id, url, request_type, d); |
| + } |
| + |
| + // Returns the raw pointer of the last created URL fetcher. |
| + // If it was destroyed or no fetcher was created, it will return a nulltpr. |
| + net::TestURLFetcher* GetLastCreatedFetcher() { |
| + if (fetcher_id_queue_.empty()) { |
| + return nullptr; |
| + } |
| + return GetFetcherByID(fetcher_id_queue_.front()); |
|
tschumann
2016/12/09 12:53:38
shouldn't this be back?
fhorschig
2016/12/12 09:54:03
What came in first should be handled first (--> qu
|
| + } |
| + |
| + private: |
| + std::list<int> fetcher_id_queue_; // Use a list as searching is essential. |
|
tschumann
2016/12/09 12:53:38
drop the type in the variable name (it's outdated
Marc Treib
2016/12/09 12:59:24
You mean, this can't be std::queue because that do
fhorschig
2016/12/12 09:54:03
@tschumann: Done.
@treib: std::deque it is. And do
|
| + |
| + // The fetcher can either be destroyed because the delegate was called during |
| + // execution or because we called it on destruction. |
| + void DropAndCallDelegate(int fetcher_id) { |
| + auto found_id_iter = std::find(fetcher_id_queue_.begin(), |
| + fetcher_id_queue_.end(), fetcher_id); |
| + if (found_id_iter == fetcher_id_queue_.end()) { |
| + return; |
| + } |
| + fetcher_id_queue_.erase(found_id_iter); |
| + net::TestURLFetcher* fetcher = GetFetcherByID(fetcher_id); |
| + if (!fetcher) { |
| + return; |
| + } |
| + if (!fetcher->delegate()) { |
|
Marc Treib
2016/12/09 12:59:24
nit: Could merge the two ifs:
if (!fetcher || !fet
fhorschig
2016/12/12 09:54:03
Okay.
|
| + return; |
| + } |
| + fetcher->delegate()->OnURLFetchComplete(fetcher); |
| + } |
| + |
| + // net::TestURLFetcherDelegateForTests overrides: |
| + void OnRequestStart(int fetcher_id) override {} |
| + void OnChunkUpload(int fetcher_id) override {} |
| + void OnRequestEnd(int fetcher_id) override { |
| + DropAndCallDelegate(fetcher_id); |
| + } |
| +}; |
| + |
| // Factory for FakeURLFetcher objects that always generate errors. |
| class FailingFakeURLFetcherFactory : public net::URLFetcherFactory { |
| public: |
| @@ -209,6 +286,11 @@ class NTPSnippetsFetcherTest : public testing::Test { |
| bool IsSendingTopLanguagesEnabled() const override { return true; } |
| }; |
| + struct ExpectationForVariationParam { |
| + std::string param_value; |
| + int expected_value; |
| + }; |
| + |
| NTPSnippetsFetcherTest() |
| : NTPSnippetsFetcherTest(GURL(kTestChromeReaderUrl), |
| std::map<std::string, std::string>()) {} |
| @@ -290,6 +372,15 @@ class NTPSnippetsFetcherTest : public testing::Test { |
| ntp_snippets::kStudyName, params); |
| } |
| + void SetVariationParametersForFeatures( |
|
Marc Treib
2016/12/09 12:59:24
Hm, now SetFetchingPersonalizationVariation and Se
fhorschig
2016/12/12 09:54:03
The basic difference is that one is tied to a feat
|
| + const std::map<std::string, std::string>& params, |
| + const std::set<std::string>& features) { |
| + params_manager_.reset(); |
| + params_manager_ = |
| + base::MakeUnique<variations::testing::VariationParamsManager>( |
| + ntp_snippets::kStudyName, params, features); |
| + } |
| + |
| void SetFakeResponse(const std::string& response_data, |
| net::HttpStatusCode response_code, |
| net::URLRequestStatus::Status status) { |
| @@ -907,13 +998,15 @@ TEST_F(NTPSnippetsFetcherTest, ShouldFetchSuccessfullyEmptyList) { |
| } |
| TEST_F(NTPSnippetsFetcherTest, ShouldRestrictToHosts) { |
| - net::TestURLFetcherFactory test_url_fetcher_factory; |
| + DelegateCallingTestURLFetcherFactory fetcher_factory; |
| NTPSnippetsFetcher::Params params = test_params(); |
| params.hosts = {"www.somehost1.com", "www.somehost2.com"}; |
| params.count_to_fetch = 17; |
| + |
| snippets_fetcher().FetchSnippets( |
| params, ToSnippetsAvailableCallback(&mock_callback())); |
| - net::TestURLFetcher* fetcher = test_url_fetcher_factory.GetFetcherByID(0); |
| + |
| + net::TestURLFetcher* fetcher = fetcher_factory.GetLastCreatedFetcher(); |
| ASSERT_THAT(fetcher, NotNull()); |
| std::unique_ptr<base::Value> value = |
| base::JSONReader::Read(fetcher->upload_data()); |
| @@ -936,16 +1029,43 @@ TEST_F(NTPSnippetsFetcherTest, ShouldRestrictToHosts) { |
| ASSERT_TRUE(content_selectors->GetDictionary(1, &content_selector)); |
| EXPECT_TRUE(content_selector->GetString("value", &content_selector_value)); |
| EXPECT_THAT(content_selector_value, Eq("www.somehost2.com")); |
| - // Call the delegate callback manually as the TestURLFetcher deletes any |
| - // call to the delegate that usually happens on |Start|. |
| - // Without the call to the delegate, it leaks the request that owns itself. |
| - ASSERT_THAT(fetcher->delegate(), NotNull()); |
| - EXPECT_CALL(mock_callback(), Run(/*snippets=*/Not(HasValue()))).Times(1); |
| - // An 4XX response needs the least configuration to successfully invoke the |
| - // callback properly as the results are not important in this test. |
| - fetcher->set_response_code(net::HTTP_NOT_FOUND); |
| - fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::FAILED, -2)); |
| - fetcher->delegate()->OnURLFetchComplete(fetcher); |
| +} |
| + |
| +TEST_F(NTPSnippetsFetcherTest, RetryOnInteractiveRequests) { |
| + DelegateCallingTestURLFetcherFactory fetcher_factory; |
| + NTPSnippetsFetcher::Params params = test_params(); |
| + params.interactive_request = true; |
| + |
| + snippets_fetcher().FetchSnippets( |
| + params, ToSnippetsAvailableCallback(&mock_callback())); |
| + |
| + net::TestURLFetcher* fetcher = fetcher_factory.GetLastCreatedFetcher(); |
| + ASSERT_THAT(fetcher, NotNull()); |
| + EXPECT_THAT(fetcher->GetMaxRetriesOn5xx(), Eq(2)); |
| +} |
| + |
| +TEST_F(NTPSnippetsFetcherTest, RetriesConfigurableOnNonInteractiveRequests) { |
| + NTPSnippetsFetcher::Params params = test_params(); |
| + params.interactive_request = false; |
| + const std::vector<ExpectationForVariationParam> retry_config_expectation = { |
| + {"", 0}, // Keep 2 retries if no variation is set. |
| + {"0", 0}, |
| + {"-1", 0}, // Allow no negative retry count. |
| + {"3", 3}}; |
| + |
| + for (const auto& retry_config : retry_config_expectation) { |
| + DelegateCallingTestURLFetcherFactory fetcher_factory; |
| + SetVariationParametersForFeatures( |
| + {{"background_5xx_retries_count", retry_config.param_value}}, |
| + {ntp_snippets::kArticleSuggestionsFeature.name}); |
| + |
| + snippets_fetcher().FetchSnippets( |
| + params, ToSnippetsAvailableCallback(&mock_callback())); |
| + |
| + net::TestURLFetcher* fetcher = fetcher_factory.GetLastCreatedFetcher(); |
| + ASSERT_THAT(fetcher, NotNull()); |
| + EXPECT_THAT(fetcher->GetMaxRetriesOn5xx(), Eq(retry_config.expected_value)); |
| + } |
| } |
| TEST_F(NTPSnippetsFetcherTest, ShouldReportUrlStatusError) { |