Index: chrome/browser/safe_browsing/client_side_detection_host_unittest.cc |
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7f47903765c4f289cff27f59866c932f9ad5c0c4 |
--- /dev/null |
+++ b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc |
@@ -0,0 +1,301 @@ |
+// Copyright (c) 2011 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 "base/command_line.h" |
+#include "base/file_path.h" |
+#include "base/ref_counted.h" |
+#include "base/scoped_ptr.h" |
+#include "base/scoped_temp_dir.h" |
+#include "base/task.h" |
+#include "chrome/browser/browser_thread.h" |
+#include "chrome/browser/renderer_host/test/test_render_view_host.h" |
+#include "chrome/browser/safe_browsing/client_side_detection_host.h" |
+#include "chrome/browser/safe_browsing/client_side_detection_service.h" |
+#include "chrome/browser/safe_browsing/safe_browsing_service.h" |
+#include "chrome/browser/tab_contents/test_tab_contents.h" |
+#include "chrome/common/chrome_switches.h" |
+#include "chrome/test/ui_test_utils.h" |
+#include "googleurl/src/gurl.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gmock/include/gmock/gmock-spec-builders.h" |
+ |
+using ::testing::_; |
+using ::testing::DoAll; |
+using ::testing::Mock; |
+using ::testing::SaveArg; |
+ |
+namespace safe_browsing { |
+ |
+class MockClientSideDetectionService : public ClientSideDetectionService { |
+ public: |
+ MockClientSideDetectionService(const FilePath& model_path) |
+ : ClientSideDetectionService(model_path, NULL) {} |
+ virtual ~MockClientSideDetectionService() {}; |
+ |
+ MOCK_METHOD3(SendClientReportPhishingRequest, |
+ void(const GURL&, double, ClientReportPhishingRequestCallback*)); |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(MockClientSideDetectionService); |
+}; |
+ |
+class MockSafeBrowsingService : public SafeBrowsingService { |
+ public: |
+ MockSafeBrowsingService() {} |
+ virtual ~MockSafeBrowsingService() {} |
+ |
+ MOCK_METHOD8(DisplayBlockingPage, |
+ void(const GURL&, const GURL&, const std::vector<GURL>&, |
+ ResourceType::Type, UrlCheckResult, Client*, int, int)); |
+ |
+ // Helper function which calls OnBlockingPageComplete for this client |
+ // object. |
+ void InvokeOnBlockingPageComplete(SafeBrowsingService::Client* client) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ DCHECK(client); |
+ // Note: this will delete the client object in the case of the CsdClient |
+ // implementation. |
+ client->OnBlockingPageComplete(false); |
+ } |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingService); |
+}; |
+ |
+// Helper function which quits the UI message loop from the IO message loop. |
+void QuitUIMessageLoop() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ BrowserThread::PostTask(BrowserThread::UI, |
+ FROM_HERE, |
+ new MessageLoop::QuitTask()); |
+} |
+ |
+class ClientSideDetectionHostTest : public RenderViewHostTestHarness { |
+ public: |
+ virtual void SetUp() { |
+ RenderViewHostTestHarness::SetUp(); |
+ ui_thread_.reset(new BrowserThread(BrowserThread::UI, &message_loop_)); |
+ // Note: we're starting a real IO thread to make sure our DCHECKs that |
+ // verify which thread is running are actually tested. |
+ io_thread_.reset(new BrowserThread(BrowserThread::IO)); |
+ CHECK(io_thread_->Start()); |
Brian Ryner
2011/02/16 22:37:10
I've been told in the past to prefer ASSERT_TRUE o
noelutz
2011/02/16 23:22:36
Done.
|
+ |
+ // Inject service classes. |
+ ScopedTempDir tmp_dir; |
+ ASSERT_TRUE(tmp_dir.CreateUniqueTempDir()); |
+ FilePath model_path = tmp_dir.path().AppendASCII("model"); |
+ |
+ csd_service_.reset(new MockClientSideDetectionService(model_path)); |
+ sb_service_ = new MockSafeBrowsingService(); |
+ csd_host_ = contents()->safebrowsing_detection_host(); |
+ csd_host_->set_client_side_detection_service(csd_service_.get()); |
+ csd_host_->set_safe_browsing_service(sb_service_.get()); |
+ } |
+ |
+ virtual void TearDown() { |
+ io_thread_->Stop(); |
Brian Ryner
2011/02/16 22:37:10
I think the thread stops automatically when it is
noelutz
2011/02/16 23:22:36
Done.
|
+ io_thread_.reset(); |
+ ui_thread_.reset(); |
+ RenderViewHostTestHarness::TearDown(); |
+ } |
+ |
+ void OnDetectedPhishingSite(const GURL& phishing_url, double phishing_score) { |
+ csd_host_->OnDetectedPhishingSite(phishing_url, phishing_score); |
+ } |
+ |
+ protected: |
+ ClientSideDetectionHost* csd_host_; |
+ scoped_ptr<MockClientSideDetectionService> csd_service_; |
+ scoped_refptr<MockSafeBrowsingService> sb_service_; |
+ |
+ private: |
+ scoped_ptr<BrowserThread> ui_thread_; |
+ scoped_ptr<BrowserThread> io_thread_; |
+}; |
+ |
+TEST_F(ClientSideDetectionHostTest, OnDetectedPhishingSiteNotPhishing) { |
+ // Case 1: client thinks the page is phishing. The server does not agree. |
+ // No interstitial is shown. |
+ ClientSideDetectionService::ClientReportPhishingRequestCallback* cb; |
+ GURL phishing_url("http://phishingurl.com/"); |
+ |
+ EXPECT_CALL(*csd_service_, |
+ SendClientReportPhishingRequest(phishing_url, 1.0, _)) |
+ .WillOnce(SaveArg<2>(&cb)); |
+ OnDetectedPhishingSite(phishing_url, 1.0); |
+ EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get())); |
+ ASSERT_TRUE(cb != NULL); |
+ |
+ // Make sure DisplayBlockingPage is not going to be called. |
+ EXPECT_CALL(*sb_service_, |
+ DisplayBlockingPage(_, _, _, _, _, _, _, _)).Times(0); |
+ cb->Run(phishing_url, false); |
+ delete cb; |
+ // If there was a message posted on the IO thread to display the |
+ // interstitial page we know that it would have been posted before |
+ // we put the quit message there. |
+ BrowserThread::PostTask(BrowserThread::IO, |
+ FROM_HERE, |
+ NewRunnableFunction(&QuitUIMessageLoop)); |
+ MessageLoop::current()->Run(); |
+ EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get())); |
+} |
+ |
+TEST_F(ClientSideDetectionHostTest, OnDetectedPhishingSiteDisabled) { |
+ // Case 2: client thinks the page is phishing and so does the server but |
+ // showing the interstitial is disabled => no interstitial is shown. |
+ ClientSideDetectionService::ClientReportPhishingRequestCallback* cb; |
+ GURL phishing_url("http://phishingurl.com/"); |
+ |
+ EXPECT_CALL(*csd_service_, |
+ SendClientReportPhishingRequest(phishing_url, 1.0, _)) |
+ .WillOnce(SaveArg<2>(&cb)); |
+ OnDetectedPhishingSite(phishing_url, 1.0); |
+ EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get())); |
+ ASSERT_TRUE(cb != NULL); |
+ |
+ // Make sure DisplayBlockingPage is not going to be called. |
+ EXPECT_CALL(*sb_service_, |
+ DisplayBlockingPage(_, _, _, _, _, _, _, _)).Times(0); |
+ cb->Run(phishing_url, false); |
+ delete cb; |
+ |
+ // If there was a message posted on the IO thread to display the |
Brian Ryner
2011/02/16 22:37:10
You could factor this out into a helper method, th
noelutz
2011/02/16 23:22:36
Done.
noelutz
2011/02/16 23:22:36
Done.
|
+ // interstitial page we know that it would have been posted before |
+ // we put the quit message there. |
+ BrowserThread::PostTask(BrowserThread::IO, |
+ FROM_HERE, |
+ NewRunnableFunction(&QuitUIMessageLoop)); |
+ MessageLoop::current()->Run(); |
+ EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get())); |
+} |
+ |
+TEST_F(ClientSideDetectionHostTest, OnDetectedPhishingSiteShowInterstitial) { |
+ // Case 3: client thinks the page is phishing and so does the server. |
+ // We show an interstitial. |
+ ClientSideDetectionService::ClientReportPhishingRequestCallback* cb; |
+ GURL phishing_url("http://phishingurl.com/"); |
+ |
+ CommandLine::ForCurrentProcess()->AppendSwitch( |
+ switches::kEnableClientSidePhishingInterstitial); |
+ |
+ EXPECT_CALL(*csd_service_, |
+ SendClientReportPhishingRequest(phishing_url, 1.0, _)) |
+ .WillOnce(SaveArg<2>(&cb)); |
+ OnDetectedPhishingSite(phishing_url, 1.0); |
+ EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get())); |
+ ASSERT_TRUE(cb != NULL); |
+ |
+ SafeBrowsingService::Client* client; |
+ EXPECT_CALL(*sb_service_, |
+ DisplayBlockingPage( |
+ phishing_url, |
+ phishing_url, |
+ _, |
+ ResourceType::MAIN_FRAME, |
+ SafeBrowsingService::URL_PHISHING, |
+ _ /* a CsdClient object */, |
+ contents()->GetRenderProcessHost()->id(), |
+ contents()->render_view_host()->routing_id())) |
+ .WillOnce(SaveArg<5>(&client)); |
+ |
+ cb->Run(phishing_url, true); |
+ delete cb; |
+ |
+ BrowserThread::PostTask(BrowserThread::IO, |
+ FROM_HERE, |
+ NewRunnableFunction(&QuitUIMessageLoop)); |
+ MessageLoop::current()->Run(); |
+ EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get())); |
+ |
+ // Make sure the client object will be deleted. |
+ BrowserThread::PostTask( |
+ BrowserThread::IO, |
+ FROM_HERE, |
+ NewRunnableMethod( |
+ sb_service_.get(), |
+ &MockSafeBrowsingService::InvokeOnBlockingPageComplete, |
+ client)); |
+ // Since the CsdClient object will be deleted on the UI thread I need |
+ // to run the UI message loop. Post a task to stop the UI message loop |
+ // after the client object destructor is called. |
+ BrowserThread::PostTask(BrowserThread::IO, |
+ FROM_HERE, |
+ NewRunnableFunction(&QuitUIMessageLoop)); |
+ MessageLoop::current()->Run(); |
+} |
+ |
+TEST_F(ClientSideDetectionHostTest, OnDetectedPhishingSiteMultiplePings) { |
+ // Case 4 & 5: client thinks a page is phishing then navigates to |
+ // another page which is also considered phishing by the client |
+ // before the server responds with a verdict. After a while the |
+ // server responds for both requests with a phishing verdict. Only |
+ // a single interstitial is shown for the second URL. |
+ ClientSideDetectionService::ClientReportPhishingRequestCallback* cb; |
+ GURL phishing_url("http://phishingurl.com/"); |
+ |
+ CommandLine::ForCurrentProcess()->AppendSwitch( |
+ switches::kEnableClientSidePhishingInterstitial); |
+ |
+ EXPECT_CALL(*csd_service_, |
+ SendClientReportPhishingRequest(phishing_url, 1.0, _)) |
+ .WillOnce(SaveArg<2>(&cb)); |
+ OnDetectedPhishingSite(phishing_url, 1.0); |
+ EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get())); |
+ ASSERT_TRUE(cb != NULL); |
+ GURL other_phishing_url("http://other_phishing_url.com/bla"); |
+ // We navigate away. The callback cb should be revoked. |
+ NavigateAndCommit(other_phishing_url); |
+ |
+ ClientSideDetectionService::ClientReportPhishingRequestCallback* cb_other; |
+ EXPECT_CALL(*csd_service_, |
+ SendClientReportPhishingRequest(other_phishing_url, 0.8, _)) |
+ .WillOnce(SaveArg<2>(&cb_other)); |
+ OnDetectedPhishingSite(other_phishing_url, 0.8); |
+ EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get())); |
+ ASSERT_TRUE(cb_other); |
+ |
+ // We expect that the interstitial is shown for the second phishing URL. |
Brian Ryner
2011/02/16 22:37:10
Do you want to add an EXPECT_CALL for the first ur
|
+ SafeBrowsingService::Client* client; |
+ EXPECT_CALL(*sb_service_, |
+ DisplayBlockingPage( |
+ other_phishing_url, |
+ other_phishing_url, |
+ _, |
+ ResourceType::MAIN_FRAME, |
+ SafeBrowsingService::URL_PHISHING, |
+ _ /* a CsdClient object */, |
+ contents()->GetRenderProcessHost()->id(), |
+ contents()->render_view_host()->routing_id())) |
+ .WillOnce(SaveArg<5>(&client)); |
+ |
+ cb->Run(phishing_url, true); // Should have no effect. |
+ delete cb; |
+ cb_other->Run(other_phishing_url, true); // Should show interstitial. |
+ delete cb_other; |
+ |
+ BrowserThread::PostTask(BrowserThread::IO, |
+ FROM_HERE, |
+ NewRunnableFunction(&QuitUIMessageLoop)); |
+ MessageLoop::current()->Run(); |
+ EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get())); |
+ |
+ // Make sure the client object will be deleted. |
+ BrowserThread::PostTask( |
+ BrowserThread::IO, |
+ FROM_HERE, |
+ NewRunnableMethod( |
+ sb_service_.get(), |
+ &MockSafeBrowsingService::InvokeOnBlockingPageComplete, |
+ client)); |
+ // Since the CsdClient object will be deleted on the UI thread I need |
+ // to run the UI message loop. Post a task to stop the UI message loop |
+ // after the client object destructor is called. |
+ BrowserThread::PostTask(BrowserThread::IO, |
+ FROM_HERE, |
+ NewRunnableFunction(&QuitUIMessageLoop)); |
+ MessageLoop::current()->Run(); |
+} |
+ |
+} // namespace safe_browsing |