Chromium Code Reviews| Index: chrome/browser/safe_browsing/incident_reporting/off_domain_inclusion_detector_unittest.cc |
| diff --git a/chrome/browser/safe_browsing/incident_reporting/off_domain_inclusion_detector_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/off_domain_inclusion_detector_unittest.cc |
| index 4860a455d611c97fbe729e022981f34870d918e9..5f6afe4d6514e2cb973f91a015547918473033d2 100644 |
| --- a/chrome/browser/safe_browsing/incident_reporting/off_domain_inclusion_detector_unittest.cc |
| +++ b/chrome/browser/safe_browsing/incident_reporting/off_domain_inclusion_detector_unittest.cc |
| @@ -3,16 +3,37 @@ |
| // found in the LICENSE file. |
| #include "base/bind.h" |
| +#include "base/files/file_util.h" |
| +#include "base/logging.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/run_loop.h" |
| +#include "base/strings/string_number_conversions.h" |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "base/time/time.h" |
| +#include "chrome/browser/history/chrome_history_client.h" |
| +#include "chrome/browser/history/chrome_history_client_factory.h" |
| +#include "chrome/browser/history/history_service.h" |
| +#include "chrome/browser/history/history_service_factory.h" |
| +#include "chrome/browser/history/web_history_service_factory.h" |
| +#include "chrome/browser/prefs/browser_prefs.h" |
| +#include "chrome/browser/profiles/profile.h" |
| +#include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/safe_browsing/database_manager.h" |
| #include "chrome/browser/safe_browsing/incident_reporting/off_domain_inclusion_detector.h" |
| #include "chrome/browser/safe_browsing/safe_browsing_service.h" |
| +#include "chrome/test/base/testing_browser_process.h" |
| +#include "chrome/test/base/testing_pref_service_syncable.h" |
| +#include "chrome/test/base/testing_profile.h" |
| +#include "chrome/test/base/testing_profile_manager.h" |
| +#include "components/history/content/browser/history_database_helper.h" |
| +#include "components/history/core/browser/history_constants.h" |
| +#include "components/history/core/browser/history_database_params.h" |
| +#include "components/history/core/browser/history_types.h" |
| +#include "components/keyed_service/core/service_access_type.h" |
| #include "content/public/browser/resource_request_info.h" |
| #include "content/public/common/resource_type.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| -#include "ipc/ipc_message.h" |
| #include "net/base/request_priority.h" |
| #include "net/url_request/url_request.h" |
| #include "net/url_request/url_request_test_util.h" |
| @@ -21,10 +42,44 @@ |
| #include "url/gurl.h" |
| using testing::_; |
| +using testing::Eq; |
| using testing::NiceMock; |
| using testing::Return; |
| using testing::Values; |
| +namespace { |
| + |
| +const int kFakeRenderProcessId = 123; |
| + |
| +// A BrowserContextKeyedServiceFactory::TestingFactoryFunction that creates a |
| +// HistoryService for a TestingProfile. |
| +KeyedService* BuildHistoryService(content::BrowserContext* context) { |
| + TestingProfile* profile = static_cast<TestingProfile*>(context); |
| + |
| + // Delete the file before creating the service. |
| + base::FilePath history_path( |
| + profile->GetPath().Append(history::kHistoryFilename)); |
| + if (!base::DeleteFile(history_path, false) || |
| + base::PathExists(history_path)) { |
| + ADD_FAILURE() << "failed to delete history db file " |
| + << history_path.value(); |
| + return NULL; |
| + } |
| + |
| + HistoryService* history_service = new HistoryService( |
| + ChromeHistoryClientFactory::GetForProfile(profile), profile); |
| + if (history_service->Init( |
| + history::HistoryDatabaseParamsForPath(profile->GetPath()))) { |
| + return history_service; |
| + } |
| + |
| + ADD_FAILURE() << "failed to initialize history service"; |
| + delete history_service; |
| + return NULL; |
| +} |
| + |
| +} // namespace |
| + |
| namespace safe_browsing { |
| using AnalysisEvent = OffDomainInclusionDetector::AnalysisEvent; |
| @@ -61,9 +116,11 @@ static_assert( |
| "Expected resource types list aren't comprehensive"); |
| // A set of test cases to run each parametrized test case below through. |
| -enum class OffDomainInclusionTestCases { |
| - OFF_DOMAIN_INCLUSION_WHITELISTED, |
| - OFF_DOMAIN_INCLUSION_UNKNOWN, |
| +enum class TestCase { |
| + WHITELISTED, |
| + IN_HISTORY, |
| + IN_HISTORY_AND_WHITELISTED, |
| + UNKNOWN, |
| }; |
| class MockSafeBrowsingDatabaseManager : public SafeBrowsingDatabaseManager { |
| @@ -81,29 +138,74 @@ class MockSafeBrowsingDatabaseManager : public SafeBrowsingDatabaseManager { |
| DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager); |
| }; |
| -class OffDomainInclusionDetectorTest |
| - : public testing::TestWithParam<OffDomainInclusionTestCases> { |
| +// A mock OffDomainInclusionDetector which mocks out |
| +// |ProfileFromRenderProcessId()| so that tests can inject their TestingProfile |
| +// into the OffDomainInclusionDetector. |
| +class MockOffDomainInclusionDetector |
| + : public NiceMock<OffDomainInclusionDetector> { |
| + public: |
| + MockOffDomainInclusionDetector( |
| + const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager, |
| + const ReportAnalysisEventCallback& report_analysis_event_callback) |
| + : NiceMock<OffDomainInclusionDetector>(database_manager, |
| + report_analysis_event_callback) {} |
| + |
| + MOCK_METHOD1(ProfileFromRenderProcessId, Profile*(int render_process_id)); |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(MockOffDomainInclusionDetector); |
| +}; |
| + |
| +// Adds |url| to |history_service| upon being constructed and removes it upon |
| +// being destructed. |
| +class ScopedHistoryEntry { |
| + public: |
| + ScopedHistoryEntry(HistoryService* history_service, const GURL& url) |
| + : history_service_(history_service), url_(url) { |
| + base::RunLoop run_loop; |
| + |
| + history_service_->AddPage(url_, base::Time::Now(), history::SOURCE_BROWSED); |
| + |
| + // Flush tasks posted on the history thread. |
| + history_service_->FlushForTest(run_loop.QuitClosure()); |
| + run_loop.Run(); |
| + // Make sure anything bounced back to the main thread has been handled. |
| + base::RunLoop().RunUntilIdle(); |
| + } |
| + |
| + ~ScopedHistoryEntry() { |
| + base::RunLoop run_loop; |
| + |
| + history_service_->DeleteURL(url_); |
| + |
| + history_service_->FlushForTest(run_loop.QuitClosure()); |
| + run_loop.Run(); |
| + base::RunLoop().RunUntilIdle(); |
| + } |
| + |
| + private: |
| + HistoryService* history_service_; |
| + const GURL url_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(ScopedHistoryEntry); |
| +}; |
| + |
| +class OffDomainInclusionDetectorTest : public testing::TestWithParam<TestCase> { |
| protected: |
| OffDomainInclusionDetectorTest() |
| - : observed_analysis_event_(AnalysisEvent::NO_EVENT) {} |
| + : testing_profile_(nullptr), |
| + observed_analysis_event_(AnalysisEvent::NO_EVENT) {} |
| void SetUp() override { |
| - // Only used for initializing MockSafeBrowsingDatabaseManager. |
| - scoped_refptr<SafeBrowsingService> sb_service = |
| - SafeBrowsingService::CreateSafeBrowsingService(); |
| - |
| - scoped_refptr<MockSafeBrowsingDatabaseManager> |
| - mock_safe_browsing_database_manager = |
| - new NiceMock<MockSafeBrowsingDatabaseManager>(sb_service); |
| - ON_CALL(*mock_safe_browsing_database_manager, MatchInclusionWhitelistUrl(_)) |
| - .WillByDefault(Return( |
| - GetParam() == |
| - OffDomainInclusionTestCases::OFF_DOMAIN_INCLUSION_WHITELISTED)); |
| + InitTestProfile(); |
| + InitOffDomainInclusionDetector(); |
| + } |
| - off_domain_inclusion_detector_.reset(new OffDomainInclusionDetector( |
| - mock_safe_browsing_database_manager, |
| - base::Bind(&OffDomainInclusionDetectorTest::OnOffDomainInclusionEvent, |
| - base::Unretained(this)))); |
| + void TearDown() override { |
| + // Shut down the history service. |
| + testing_profile_->AsTestingProfile()->DestroyHistoryService(); |
| + profile_manager_.reset(); |
| + TestingBrowserProcess::DeleteInstance(); |
| } |
| AnalysisEvent GetLastEventAndReset() { |
| @@ -112,13 +214,21 @@ class OffDomainInclusionDetectorTest |
| return last_event; |
| } |
| - void SimulateTestURLRequest(const std::string& url, |
| + void SimulateTestURLRequest(const std::string& url_str, |
| const std::string& referrer, |
| content::ResourceType resource_type, |
| bool is_main_frame, |
| bool parent_is_main_frame) const { |
| + const GURL url(url_str); |
| + |
| + HistoryService* history_service = HistoryServiceFactory::GetForProfile( |
| + testing_profile_, ServiceAccessType::EXPLICIT_ACCESS); |
| + scoped_ptr<ScopedHistoryEntry> scoped_history_entry; |
| + if (ShouldAddSimulatedURLsToHistory()) |
| + scoped_history_entry.reset(new ScopedHistoryEntry(history_service, url)); |
| + |
| scoped_ptr<net::URLRequest> url_request( |
| - context_.CreateRequest(GURL(url), net::DEFAULT_PRIORITY, NULL, NULL)); |
| + context_.CreateRequest(url, net::DEFAULT_PRIORITY, NULL, NULL)); |
| if (!referrer.empty()) |
| url_request->SetReferrer(referrer); |
| @@ -127,9 +237,9 @@ class OffDomainInclusionDetectorTest |
| url_request.get(), |
| resource_type, |
| NULL, // resource_context |
| - 0, // render_process_id |
| + kFakeRenderProcessId, // render_process_id |
| 0, // render_view_id |
| - MSG_ROUTING_NONE, // render_frame_id |
| + 0, // render_frame_id |
| is_main_frame, // is_main_frame |
| parent_is_main_frame, // parent_is_main_frame |
| true, // allow_download |
| @@ -137,35 +247,132 @@ class OffDomainInclusionDetectorTest |
| off_domain_inclusion_detector_->OnResourceRequest(url_request.get()); |
| - // OffDomainInclusionDetector::OnResourceRequest() sometimes completes |
| + // OffDomainInclusionDetector::OnResourceRequest() may complete |
| // asynchronously, run all message loops (i.e. this message loop in unit |
| // tests) until idle. |
| base::RunLoop().RunUntilIdle(); |
| + |
| + // Make sure any task posted by the OffDomainInclusionDetector to the |
| + // history thread has occurred. |
| + base::RunLoop run_loop; |
| + history_service->FlushForTest(run_loop.QuitClosure()); |
| + run_loop.Run(); |
| + |
| + // Finally, finish anything the history thread may have bounced back to the |
| + // main thread. |
| + base::RunLoop().RunUntilIdle(); |
| } |
| // Returns the expected AnalysisEvent produced when facing an off-domain |
| // inclusion in the current test configuration. |
| AnalysisEvent GetExpectedOffDomainInclusionEventType() { |
| - return GetParam() == |
| - OffDomainInclusionTestCases::OFF_DOMAIN_INCLUSION_WHITELISTED |
| - ? AnalysisEvent::OFF_DOMAIN_INCLUSION_WHITELISTED |
| - : AnalysisEvent::OFF_DOMAIN_INCLUSION_SUSPICIOUS; |
| + switch (GetParam()) { |
| + case TestCase::WHITELISTED: |
| + return AnalysisEvent::OFF_DOMAIN_INCLUSION_WHITELISTED; |
| + case TestCase::IN_HISTORY: |
| + return AnalysisEvent::OFF_DOMAIN_INCLUSION_IN_HISTORY; |
| + case TestCase::IN_HISTORY_AND_WHITELISTED: |
| + return AnalysisEvent::OFF_DOMAIN_INCLUSION_WHITELISTED; |
| + case TestCase::UNKNOWN: |
| + return AnalysisEvent::OFF_DOMAIN_INCLUSION_SUSPICIOUS; |
| + } |
| + NOTREACHED(); |
| + return AnalysisEvent::NO_EVENT; |
| } |
| - |
| private: |
| + // Returns true if the inclusion whitelist should be mocked to return a match |
| + // on all requests. |
| + bool ShouldWhitelistEverything() const { |
| + return GetParam() == TestCase::WHITELISTED || |
| + GetParam() == TestCase::IN_HISTORY_AND_WHITELISTED; |
| + } |
| + |
| + // Returns true if simulated URLs should be added to history. |
| + bool ShouldAddSimulatedURLsToHistory() const { |
| + return GetParam() == TestCase::IN_HISTORY || |
| + GetParam() == TestCase::IN_HISTORY_AND_WHITELISTED; |
| + } |
| + |
| void OnOffDomainInclusionEvent(AnalysisEvent event) { |
| // Make sure no other AnalysisEvent was previously reported. |
| EXPECT_EQ(AnalysisEvent::NO_EVENT, observed_analysis_event_); |
| observed_analysis_event_ = event; |
| } |
| + // Initializes |profile_manager_| and |testing_profile_|. |
| + void InitTestProfile() { |
| + ASSERT_FALSE(profile_manager_); |
| + ASSERT_FALSE(testing_profile_); |
| + |
| + profile_manager_.reset( |
| + new TestingProfileManager(TestingBrowserProcess::GetGlobal())); |
| + ASSERT_TRUE(profile_manager_->SetUp()); |
| + |
| + // Set up a mock keyed service factory for the HistoryService. |
| + TestingProfile::TestingFactories factories; |
| + factories.push_back(std::make_pair(HistoryServiceFactory::GetInstance(), |
| + &BuildHistoryService)); |
| + // Suppress WebHistoryService since it makes network requests. |
| + factories.push_back(std::make_pair( |
| + WebHistoryServiceFactory::GetInstance(), |
| + static_cast<BrowserContextKeyedServiceFactory::TestingFactoryFunction>( |
| + NULL))); |
| + |
| + // Create default prefs for the test profile. |
| + scoped_ptr<TestingPrefServiceSyncable> prefs( |
| + new TestingPrefServiceSyncable); |
| + chrome::RegisterUserProfilePrefs(prefs->registry()); |
| + |
| + // |testing_profile_| is owned by |profile_manager_|. |
| + testing_profile_ = profile_manager_->CreateTestingProfile( |
| + "profile", // profile_name |
| + prefs.Pass(), |
| + base::UTF8ToUTF16("user"), // user_name |
| + 0, // avatar_id |
| + std::string(), // supervised_user_id |
| + factories); |
| + } |
| + |
| + // Initializes |off_domain_inclusion_detector_|, assumes |testing_profile_| is |
| + // already initialized. |
| + void InitOffDomainInclusionDetector() { |
| + ASSERT_TRUE(testing_profile_); |
| + ASSERT_FALSE(off_domain_inclusion_detector_); |
| + |
| + // Only used for initializing MockSafeBrowsingDatabaseManager. |
| + scoped_refptr<SafeBrowsingService> sb_service = |
| + SafeBrowsingService::CreateSafeBrowsingService(); |
| + |
| + scoped_refptr<MockSafeBrowsingDatabaseManager> |
| + mock_safe_browsing_database_manager = |
| + new NiceMock<MockSafeBrowsingDatabaseManager>(sb_service); |
| + ON_CALL(*mock_safe_browsing_database_manager, MatchInclusionWhitelistUrl(_)) |
| + .WillByDefault(Return(ShouldWhitelistEverything())); |
| + |
| + off_domain_inclusion_detector_.reset(new MockOffDomainInclusionDetector( |
|
grt (UTC plus 2)
2015/01/28 21:10:51
new NiceMock<MockOffDomainInclusionDetector> here
|
| + mock_safe_browsing_database_manager, |
| + base::Bind(&OffDomainInclusionDetectorTest::OnOffDomainInclusionEvent, |
| + base::Unretained(this)))); |
| + // ProfileFromRenderProcessId doesn't *have* to be called, but if it is, it |
| + // should always request the profile for |kFakeRenderProcessId| and this |
| + // test will always return it |testing_profile_|. |
| + ON_CALL(*off_domain_inclusion_detector_, |
| + ProfileFromRenderProcessId(Eq(kFakeRenderProcessId))) |
| + .WillByDefault(Return(testing_profile_)); |
| + } |
| + |
| + Profile* testing_profile_; |
| + scoped_ptr<TestingProfileManager> profile_manager_; |
| + |
| content::TestBrowserThreadBundle thread_bundle_; |
| net::TestURLRequestContext context_; |
| AnalysisEvent observed_analysis_event_; |
| - scoped_ptr<OffDomainInclusionDetector> off_domain_inclusion_detector_; |
| + scoped_ptr<MockOffDomainInclusionDetector> off_domain_inclusion_detector_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(OffDomainInclusionDetectorTest); |
| }; |
| TEST_P(OffDomainInclusionDetectorTest, NoEventForIgnoredResourceTypes) { |
| @@ -334,14 +541,16 @@ TEST_P(OffDomainInclusionDetectorTest, |
| true, // is_main_frame |
| false); // parent_is_main_frame |
| - EXPECT_EQ(AnalysisEvent::EMPTY_MAIN_FRAME_URL, GetLastEventAndReset()); |
| + EXPECT_EQ(AnalysisEvent::ABORT_EMPTY_MAIN_FRAME_URL, |
| + GetLastEventAndReset()); |
| } |
| } |
| -INSTANTIATE_TEST_CASE_P( |
| - OffDomainInclusionDetectorTestInstance, |
| - OffDomainInclusionDetectorTest, |
| - Values(OffDomainInclusionTestCases::OFF_DOMAIN_INCLUSION_WHITELISTED, |
| - OffDomainInclusionTestCases::OFF_DOMAIN_INCLUSION_UNKNOWN)); |
| +INSTANTIATE_TEST_CASE_P(OffDomainInclusionDetectorTestInstance, |
| + OffDomainInclusionDetectorTest, |
| + Values(TestCase::WHITELISTED, |
| + TestCase::IN_HISTORY, |
| + TestCase::IN_HISTORY_AND_WHITELISTED, |
| + TestCase::UNKNOWN)); |
| } // namespace safe_browsing |