| Index: chrome/renderer/safe_browsing/phishing_dom_feature_extractor_browsertest.cc
|
| diff --git a/chrome/renderer/safe_browsing/phishing_dom_feature_extractor_browsertest.cc b/chrome/renderer/safe_browsing/phishing_dom_feature_extractor_browsertest.cc
|
| index 965d1b54b06779c7ab82298d0ed425620c3e5f7b..1d494bca9b230ec9611c0418f642fc17440816af 100644
|
| --- a/chrome/renderer/safe_browsing/phishing_dom_feature_extractor_browsertest.cc
|
| +++ b/chrome/renderer/safe_browsing/phishing_dom_feature_extractor_browsertest.cc
|
| @@ -18,6 +18,7 @@
|
| #include "base/message_loop.h"
|
| #include "base/process.h"
|
| #include "base/string_util.h"
|
| +#include "base/time.h"
|
| #include "chrome/common/main_function_params.h"
|
| #include "chrome/common/render_messages.h"
|
| #include "chrome/common/sandbox_init_wrapper.h"
|
| @@ -26,6 +27,7 @@
|
| #include "chrome/renderer/render_view.h"
|
| #include "chrome/renderer/render_view_visitor.h"
|
| #include "chrome/renderer/renderer_main_platform_delegate.h"
|
| +#include "chrome/renderer/safe_browsing/feature_extractor_clock.h"
|
| #include "chrome/renderer/safe_browsing/features.h"
|
| #include "googleurl/src/gurl.h"
|
| #include "ipc/ipc_channel.h"
|
| @@ -38,6 +40,7 @@
|
| #include "webkit/glue/webkit_glue.h"
|
|
|
| using ::testing::ContainerEq;
|
| +using ::testing::Return;
|
|
|
| namespace safe_browsing {
|
|
|
| @@ -61,6 +64,11 @@ class PhishingDOMFeatureExtractorTest : public ::testing::Test,
|
| }
|
|
|
| protected:
|
| + class MockClock : public FeatureExtractorClock {
|
| + public:
|
| + MOCK_METHOD0(Now, base::TimeTicks());
|
| + };
|
| +
|
| virtual void SetUp() {
|
| // Set up the renderer. This code is largely adapted from
|
| // render_view_test.cc and renderer_main.cc. Note that we use a
|
| @@ -96,7 +104,8 @@ class PhishingDOMFeatureExtractorTest : public ::testing::Test,
|
| ASSERT_TRUE(channel_->Send(new ViewMsg_New(params)));
|
| msg_loop_.Run();
|
|
|
| - extractor_.reset(new PhishingDOMFeatureExtractor(view_));
|
| + clock_ = new MockClock();
|
| + extractor_.reset(new PhishingDOMFeatureExtractor(view_, clock_));
|
| }
|
|
|
| virtual void TearDown() {
|
| @@ -232,6 +241,7 @@ class PhishingDOMFeatureExtractorTest : public ::testing::Test,
|
| scoped_ptr<SandboxInitWrapper> sandbox_init_wrapper_;
|
|
|
| scoped_ptr<PhishingDOMFeatureExtractor> extractor_;
|
| + MockClock* clock_; // owned by extractor_
|
| // Map of URL -> response body for network requests from the renderer.
|
| // Any URLs not in this map are served a 404 error.
|
| std::map<std::string, std::string> responses_;
|
| @@ -241,6 +251,8 @@ class PhishingDOMFeatureExtractorTest : public ::testing::Test,
|
| int PhishingDOMFeatureExtractorTest::next_thread_id_ = 0;
|
|
|
| TEST_F(PhishingDOMFeatureExtractorTest, FormFeatures) {
|
| + // This test doesn't exercise the extraction timing.
|
| + EXPECT_CALL(*clock_, Now()).WillRepeatedly(Return(base::TimeTicks::Now()));
|
| responses_["http://host.com/"] =
|
| "<html><head><body>"
|
| "<form action=\"query\"><input type=text><input type=checkbox></form>"
|
| @@ -297,6 +309,8 @@ TEST_F(PhishingDOMFeatureExtractorTest, FormFeatures) {
|
| }
|
|
|
| TEST_F(PhishingDOMFeatureExtractorTest, LinkFeatures) {
|
| + // This test doesn't exercise the extraction timing.
|
| + EXPECT_CALL(*clock_, Now()).WillRepeatedly(Return(base::TimeTicks::Now()));
|
| responses_["http://www.host.com/"] =
|
| "<html><head><body>"
|
| "<a href=\"http://www2.host.com/abc\">link</a>"
|
| @@ -337,6 +351,8 @@ TEST_F(PhishingDOMFeatureExtractorTest, LinkFeatures) {
|
| }
|
|
|
| TEST_F(PhishingDOMFeatureExtractorTest, ScriptAndImageFeatures) {
|
| + // This test doesn't exercise the extraction timing.
|
| + EXPECT_CALL(*clock_, Now()).WillRepeatedly(Return(base::TimeTicks::Now()));
|
| responses_["http://host.com/"] =
|
| "<html><head><script></script><script></script></head></html>";
|
|
|
| @@ -366,6 +382,9 @@ TEST_F(PhishingDOMFeatureExtractorTest, ScriptAndImageFeatures) {
|
| }
|
|
|
| TEST_F(PhishingDOMFeatureExtractorTest, SubFrames) {
|
| + // This test doesn't exercise the extraction timing.
|
| + EXPECT_CALL(*clock_, Now()).WillRepeatedly(Return(base::TimeTicks::Now()));
|
| +
|
| // Test that features are aggregated across all frames.
|
| responses_["http://host.com/"] =
|
| "<html><body><input type=text><a href=\"info.html\">link</a>"
|
| @@ -414,7 +433,87 @@ TEST_F(PhishingDOMFeatureExtractorTest, SubFrames) {
|
| EXPECT_THAT(features.features(), ContainerEq(expected_features.features()));
|
| }
|
|
|
| -// TODO(bryner): Test extraction with multiple passes, including the case where
|
| -// the node we stopped on is removed from the document.
|
| +TEST_F(PhishingDOMFeatureExtractorTest, Continuation) {
|
| + // For this test, we'll cause the feature extraction to run multiple
|
| + // iterations by incrementing the clock.
|
| +
|
| + // This page has a total of 50 elements. For the external forms feature to
|
| + // be computed correctly, the extractor has to examine the whole document.
|
| + // Note: the empty HEAD is important -- WebKit will synthesize a HEAD if
|
| + // there isn't one present, which can be confusing for the element counts.
|
| + std::string response = "<html><head></head><body>"
|
| + "<form action=\"ondomain\"></form>";
|
| + for (int i = 0; i < 45; ++i) {
|
| + response.append("<p>");
|
| + }
|
| + response.append("<form action=\"http://host2.com/\"></form></body></html>");
|
| + responses_["http://host.com/"] = response;
|
| +
|
| + // Advance the clock 30 ms every 10 elements processed, 10 ms between chunks.
|
| + // Note that this assumes kClockCheckGranularity = 10 and
|
| + // kMaxTimePerChunkMs = 50.
|
| + base::TimeTicks now = base::TimeTicks::Now();
|
| + EXPECT_CALL(*clock_, Now())
|
| + // Time check at the start of extraction.
|
| + .WillOnce(Return(now))
|
| + // Time check at the start of the first chunk of work.
|
| + .WillOnce(Return(now))
|
| + // Time check after the first 10 elements.
|
| + .WillOnce(Return(now + base::TimeDelta::FromMilliseconds(30)))
|
| + // Time check after the next 10 elements. This is over the chunk
|
| + // time limit, so a continuation task will be posted.
|
| + .WillOnce(Return(now + base::TimeDelta::FromMilliseconds(60)))
|
| + // Time check at the start of the second chunk of work.
|
| + .WillOnce(Return(now + base::TimeDelta::FromMilliseconds(70)))
|
| + // Time check after resuming iteration for the second chunk.
|
| + .WillOnce(Return(now + base::TimeDelta::FromMilliseconds(72)))
|
| + // Time check after the next 10 elements.
|
| + .WillOnce(Return(now + base::TimeDelta::FromMilliseconds(100)))
|
| + // Time check after the next 10 elements. This will trigger another
|
| + // continuation task.
|
| + .WillOnce(Return(now + base::TimeDelta::FromMilliseconds(130)))
|
| + // Time check at the start of the third chunk of work.
|
| + .WillOnce(Return(now + base::TimeDelta::FromMilliseconds(140)))
|
| + // Time check after resuming iteration for the third chunk.
|
| + .WillOnce(Return(now + base::TimeDelta::FromMilliseconds(142)))
|
| + // Time check after the last 10 elements.
|
| + .WillOnce(Return(now + base::TimeDelta::FromMilliseconds(170)))
|
| + // A final time check for the histograms.
|
| + .WillOnce(Return(now + base::TimeDelta::FromMilliseconds(180)));
|
| +
|
| + FeatureMap expected_features;
|
| + expected_features.AddBooleanFeature(features::kPageHasForms);
|
| + expected_features.AddRealFeature(features::kPageActionOtherDomainFreq, 0.5);
|
| +
|
| + FeatureMap features;
|
| + LoadURL("http://host.com/");
|
| + ASSERT_TRUE(ExtractFeatures(&features));
|
| + EXPECT_THAT(features.features(), ContainerEq(expected_features.features()));
|
| + // Make sure none of the mock expectations carry over to the next test.
|
| + ::testing::Mock::VerifyAndClearExpectations(clock_);
|
| +
|
| + // Now repeat the test with the same page, but advance the clock faster so
|
| + // that the extraction time exceeds the maximum total time for the feature
|
| + // extractor. Extraction should fail. Note that this assumes
|
| + // kMaxTotalTimeMs = 500.
|
| + EXPECT_CALL(*clock_, Now())
|
| + // Time check at the start of extraction.
|
| + .WillOnce(Return(now))
|
| + // Time check at the start of the first chunk of work.
|
| + .WillOnce(Return(now))
|
| + // Time check after the first 10 elements.
|
| + .WillOnce(Return(now + base::TimeDelta::FromMilliseconds(300)))
|
| + // Time check at the start of the second chunk of work.
|
| + .WillOnce(Return(now + base::TimeDelta::FromMilliseconds(350)))
|
| + // Time check after resuming iteration for the second chunk.
|
| + .WillOnce(Return(now + base::TimeDelta::FromMilliseconds(360)))
|
| + // Time check after the next 10 elements. This is over the limit.
|
| + .WillOnce(Return(now + base::TimeDelta::FromMilliseconds(600)))
|
| + // A final time check for the histograms.
|
| + .WillOnce(Return(now + base::TimeDelta::FromMilliseconds(620)));
|
| +
|
| + features.Clear();
|
| + EXPECT_FALSE(ExtractFeatures(&features));
|
| +}
|
|
|
| } // namespace safe_browsing
|
|
|