blob: 3565f3a61ae69a93e1640db008b5aa06eccc87cd [file] [log] [blame]
// Copyright 2019 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 <memory>
#include "base/memory/weak_ptr.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "chrome/renderer/subresource_redirect/login_robots_decider_test_util.h"
#include "chrome/renderer/subresource_redirect/robots_rules_parser.h"
#include "components/data_reduction_proxy/proto/robots_rules.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
namespace subresource_redirect {
constexpr char kTestOrigin[] = "https://test.com";
class CheckResultReceiver {
public:
void OnCheckRobotsRulesResult(RobotsRulesParser::CheckResult check_result) {
EXPECT_FALSE(did_receive_result_);
did_receive_result_ = true;
check_result_ = check_result;
}
RobotsRulesParser::CheckResultCallback GetCallback() {
return base::BindOnce(&CheckResultReceiver::OnCheckRobotsRulesResult,
weak_ptr_factory_.GetWeakPtr());
}
RobotsRulesParser::CheckResult check_result() const { return check_result_; }
bool did_receive_result() const { return did_receive_result_; }
private:
RobotsRulesParser::CheckResult check_result_;
bool did_receive_result_ = false;
base::WeakPtrFactory<CheckResultReceiver> weak_ptr_factory_{this};
};
class SubresourceRedirectRobotsRulesParserTest : public testing::Test {
public:
SubresourceRedirectRobotsRulesParserTest()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
void SetUp() override {
scoped_feature_list_.InitWithFeaturesAndParameters(
{{blink::features::kSubresourceRedirect, {{}}}}, {});
}
void SetUpRobotsRules(const std::vector<Rule>& patterns) {
robots_rules_parser_.UpdateRobotsRules(GetRobotsRulesProtoString(patterns));
VerifyRulesReceiveState(RobotsRulesParser::RulesReceiveState::kSuccess);
}
// Verify robots rules check result is received synchronously with the
// expected result.
void CheckRobotsRules(const std::string& url_path_with_query,
RobotsRulesParser::CheckResult expected_result) {
CheckResultReceiver result_receiver;
auto result = robots_rules_parser_.CheckRobotsRules(
GURL(kTestOrigin + url_path_with_query), result_receiver.GetCallback());
EXPECT_FALSE(result_receiver.did_receive_result());
EXPECT_EQ(expected_result, result);
}
// Verify robots rules check result is received asynchronously, and returns
// the receiver that can be used to check the result.
std::unique_ptr<CheckResultReceiver> CheckRobotsRulesAsync(
const std::string& url_path_with_query) {
auto result_receiver = std::make_unique<CheckResultReceiver>();
EXPECT_FALSE(robots_rules_parser_.CheckRobotsRules(
GURL(kTestOrigin + url_path_with_query),
result_receiver->GetCallback()));
return result_receiver;
}
void VerifyRulesReceiveState(
RobotsRulesParser::RulesReceiveState expected_rules_receive_state) {
EXPECT_EQ(robots_rules_parser_.rules_receive_state_,
expected_rules_receive_state);
}
void VerifyRobotsRulesReceiveResultHistogram(
RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult result) {
histogram_tester().ExpectUniqueSample(
"SubresourceRedirect.RobotRulesDecider.ReceiveResult", result, 1);
}
void VerifyReceivedRobotsRulesCountHistogram(size_t count) {
histogram_tester().ExpectUniqueSample(
"SubresourceRedirect.RobotRulesDecider.Count", count, 1);
}
void VerifyTotalRobotsRulesApplyHistograms(size_t total) {
histogram_tester().ExpectTotalCount(
"SubresourceRedirect.RobotRulesDecider.ApplyDuration", total);
}
const base::HistogramTester& histogram_tester() { return histogram_tester_; }
protected:
base::test::ScopedFeatureList scoped_feature_list_;
base::test::TaskEnvironment task_environment_;
base::HistogramTester histogram_tester_;
RobotsRulesParser robots_rules_parser_;
};
TEST_F(SubresourceRedirectRobotsRulesParserTest,
InvalidProtoParseErrorDisallowsAllPaths) {
VerifyRulesReceiveState(RobotsRulesParser::RulesReceiveState::kTimerRunning);
robots_rules_parser_.UpdateRobotsRules("INVALID PROTO");
VerifyRulesReceiveState(RobotsRulesParser::RulesReceiveState::kParseFailed);
VerifyRobotsRulesReceiveResultHistogram(
RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::
kParseError);
histogram_tester().ExpectTotalCount(
"SubresourceRedirect.RobotRulesDecider.Count", 0);
// All url paths should be disallowed.
CheckRobotsRules("", RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("/", RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("/foo.jpg", RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("/foo/bar.jpg", RobotsRulesParser::CheckResult::kDisallowed);
VerifyTotalRobotsRulesApplyHistograms(0);
}
TEST_F(SubresourceRedirectRobotsRulesParserTest, EmptyRulesAllowsAllPaths) {
VerifyRulesReceiveState(RobotsRulesParser::RulesReceiveState::kTimerRunning);
SetUpRobotsRules({});
VerifyRobotsRulesReceiveResultHistogram(
RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kSuccess);
VerifyReceivedRobotsRulesCountHistogram(0);
// All url paths should be allowed.
CheckRobotsRules("", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/foo.jpg", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/foo/bar.jpg", RobotsRulesParser::CheckResult::kAllowed);
VerifyTotalRobotsRulesApplyHistograms(4);
}
TEST_F(SubresourceRedirectRobotsRulesParserTest,
RulesReceiveTimeoutDisallowsAllPaths) {
// Let the rule fetch timeout.
VerifyRulesReceiveState(RobotsRulesParser::RulesReceiveState::kTimerRunning);
task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(10));
VerifyRobotsRulesReceiveResultHistogram(
RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kTimeout);
VerifyRulesReceiveState(RobotsRulesParser::RulesReceiveState::kTimeout);
// All url paths should be disallowed due to timeout.
CheckRobotsRules("", RobotsRulesParser::CheckResult::kDisallowedAfterTimeout);
CheckRobotsRules("/",
RobotsRulesParser::CheckResult::kDisallowedAfterTimeout);
CheckRobotsRules("/foo.jpg",
RobotsRulesParser::CheckResult::kDisallowedAfterTimeout);
CheckRobotsRules("/foo/bar.jpg",
RobotsRulesParser::CheckResult::kDisallowedAfterTimeout);
VerifyTotalRobotsRulesApplyHistograms(0);
}
TEST_F(SubresourceRedirectRobotsRulesParserTest,
CheckResultCallbackAfterRulesReceived) {
auto receiver1 = CheckRobotsRulesAsync("/foo.jpg");
auto receiver2 = CheckRobotsRulesAsync("/bar");
EXPECT_FALSE(receiver1->did_receive_result());
EXPECT_FALSE(receiver2->did_receive_result());
VerifyTotalRobotsRulesApplyHistograms(0);
// Once the rules are received the callback should get called with the result.
VerifyRulesReceiveState(RobotsRulesParser::RulesReceiveState::kTimerRunning);
SetUpRobotsRules({{kRuleTypeAllow, "/foo"}, {kRuleTypeDisallow, "/"}});
VerifyRobotsRulesReceiveResultHistogram(
RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kSuccess);
VerifyReceivedRobotsRulesCountHistogram(2);
EXPECT_TRUE(receiver1->did_receive_result());
EXPECT_TRUE(receiver2->did_receive_result());
EXPECT_EQ(RobotsRulesParser::CheckResult::kAllowed,
receiver1->check_result());
EXPECT_EQ(RobotsRulesParser::CheckResult::kDisallowed,
receiver2->check_result());
VerifyTotalRobotsRulesApplyHistograms(2);
CheckRobotsRules("/foo.png", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/bar", RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("/baz", RobotsRulesParser::CheckResult::kDisallowed);
VerifyTotalRobotsRulesApplyHistograms(5);
}
// Verify if the callback is called before the decider gets destroyed.
TEST_F(SubresourceRedirectRobotsRulesParserTest,
VerifyCallbackCalledBeforeDeciderDestroy) {
auto robots_rules_parser = std::make_unique<RobotsRulesParser>();
auto receiver1 = std::make_unique<CheckResultReceiver>();
auto receiver2 = std::make_unique<CheckResultReceiver>();
robots_rules_parser->CheckRobotsRules(GURL("https://test.com/foo.jpg"),
receiver1->GetCallback());
robots_rules_parser->CheckRobotsRules(GURL("https://test.com/bar"),
receiver2->GetCallback());
EXPECT_FALSE(receiver1->did_receive_result());
EXPECT_FALSE(receiver2->did_receive_result());
VerifyTotalRobotsRulesApplyHistograms(0);
robots_rules_parser->UpdateRobotsRules(GetRobotsRulesProtoString(
{{kRuleTypeAllow, "/foo"}, {kRuleTypeDisallow, "/"}}));
// Destroying the decider should trigger the callbacks.
robots_rules_parser.reset();
EXPECT_TRUE(receiver1->did_receive_result());
EXPECT_TRUE(receiver2->did_receive_result());
EXPECT_EQ(RobotsRulesParser::CheckResult::kAllowed,
receiver1->check_result());
EXPECT_EQ(RobotsRulesParser::CheckResult::kDisallowed,
receiver2->check_result());
VerifyTotalRobotsRulesApplyHistograms(2);
}
TEST_F(SubresourceRedirectRobotsRulesParserTest,
CheckResultCallbackAfterRulesReceiveTimeout) {
auto receiver1 = CheckRobotsRulesAsync("/foo.jpg");
auto receiver2 = CheckRobotsRulesAsync("/bar");
EXPECT_FALSE(receiver1->did_receive_result());
EXPECT_FALSE(receiver2->did_receive_result());
VerifyTotalRobotsRulesApplyHistograms(0);
// Once the rule fetch timesout,the callback should get called with the
// result.
task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(10));
VerifyRobotsRulesReceiveResultHistogram(
RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kTimeout);
VerifyRulesReceiveState(RobotsRulesParser::RulesReceiveState::kTimeout);
EXPECT_TRUE(receiver1->did_receive_result());
EXPECT_TRUE(receiver2->did_receive_result());
EXPECT_EQ(RobotsRulesParser::CheckResult::kTimedout,
receiver1->check_result());
EXPECT_EQ(RobotsRulesParser::CheckResult::kTimedout,
receiver2->check_result());
VerifyTotalRobotsRulesApplyHistograms(0);
SetUpRobotsRules({{kRuleTypeAllow, "/foo"}, {kRuleTypeDisallow, "/"}});
CheckRobotsRules("/foo.png", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/bar", RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("/baz", RobotsRulesParser::CheckResult::kDisallowed);
VerifyTotalRobotsRulesApplyHistograms(3);
}
TEST_F(SubresourceRedirectRobotsRulesParserTest, FullAllowRule) {
SetUpRobotsRules({{kRuleTypeAllow, "*"}});
VerifyRobotsRulesReceiveResultHistogram(
RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kSuccess);
VerifyReceivedRobotsRulesCountHistogram(1);
// All url paths should be allowed.
CheckRobotsRules("", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/foo.jpg", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/foo/bar.jpg", RobotsRulesParser::CheckResult::kAllowed);
VerifyTotalRobotsRulesApplyHistograms(4);
}
TEST_F(SubresourceRedirectRobotsRulesParserTest, FullDisallowRule) {
SetUpRobotsRules({{kRuleTypeDisallow, "*"}});
VerifyRobotsRulesReceiveResultHistogram(
RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kSuccess);
VerifyReceivedRobotsRulesCountHistogram(1);
// All url paths should be disallowed.
CheckRobotsRules("", RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("/", RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("/foo.jpg", RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("/foo/bar.jpg", RobotsRulesParser::CheckResult::kDisallowed);
VerifyTotalRobotsRulesApplyHistograms(4);
}
TEST_F(SubresourceRedirectRobotsRulesParserTest, TwoAllowRules) {
SetUpRobotsRules({{kRuleTypeAllow, "/foo"},
{kRuleTypeAllow, "/bar$"},
{kRuleTypeDisallow, "*"}});
VerifyRobotsRulesReceiveResultHistogram(
RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kSuccess);
VerifyReceivedRobotsRulesCountHistogram(3);
CheckRobotsRules("", RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("/", RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("/foo.jpg", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/foo/baz.jpg", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/bar", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/bar.jpg", RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("/baz", RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("foo", RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("bar", RobotsRulesParser::CheckResult::kDisallowed);
VerifyTotalRobotsRulesApplyHistograms(9);
}
// When the URL path matches multiple allow and disallow rules, whichever rule
// that comes first in the list should take precedence.
TEST_F(SubresourceRedirectRobotsRulesParserTest,
FirstAllowRuleMatchOverridesLaterDisallowRule) {
SetUpRobotsRules({{kRuleTypeAllow, "/foo"}, {kRuleTypeDisallow, "/foo"}});
VerifyRobotsRulesReceiveResultHistogram(
RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kSuccess);
VerifyReceivedRobotsRulesCountHistogram(2);
CheckRobotsRules("/foo", RobotsRulesParser::CheckResult::kAllowed);
SetUpRobotsRules({{kRuleTypeDisallow, "/foo"}, {kRuleTypeAllow, "/foo"}});
CheckRobotsRules("/foo", RobotsRulesParser::CheckResult::kDisallowed);
SetUpRobotsRules({{kRuleTypeAllow, "/foo.jpg"}, {kRuleTypeDisallow, "/foo"}});
CheckRobotsRules("/foo.jpg", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/foo", RobotsRulesParser::CheckResult::kDisallowed);
SetUpRobotsRules({{kRuleTypeDisallow, "/foo.jpg"}, {kRuleTypeAllow, "/foo"}});
CheckRobotsRules("/foo.jpg", RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("/foo", RobotsRulesParser::CheckResult::kAllowed);
VerifyTotalRobotsRulesApplyHistograms(6);
}
TEST_F(SubresourceRedirectRobotsRulesParserTest, TestURLWithArguments) {
SetUpRobotsRules({{kRuleTypeAllow, "/*.jpg$"},
{kRuleTypeDisallow, "/*.png?*arg_disallowed"},
{kRuleTypeAllow, "/*.png"},
{kRuleTypeDisallow, "/*.gif?*arg_disallowed"},
{kRuleTypeAllow, "/*.gif"},
{kRuleTypeDisallow, "/"}});
VerifyRobotsRulesReceiveResultHistogram(
RobotsRulesParser::SubresourceRedirectRobotsRulesReceiveResult::kSuccess);
VerifyReceivedRobotsRulesCountHistogram(6);
CheckRobotsRules("/allowed.jpg", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/allowed.png", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/allowed.gif", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/disallowed.jpg?arg",
RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("/allowed.png?arg",
RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/allowed.png?arg_allowed",
RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/allowed.png?arg_allowed&arg_allowed2",
RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/allowed.png?arg_disallowed",
RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("/allowed.png?arg_disallowed&arg_disallowed2",
RobotsRulesParser::CheckResult::kDisallowed);
VerifyTotalRobotsRulesApplyHistograms(9);
}
TEST_F(SubresourceRedirectRobotsRulesParserTest, TestRulesAreCaseSensitive) {
SetUpRobotsRules({{kRuleTypeAllow, "/allowed"},
{kRuleTypeAllow, "/CamelCase"},
{kRuleTypeAllow, "/CAPITALIZE"},
{kRuleTypeDisallow, "/"}});
VerifyReceivedRobotsRulesCountHistogram(4);
CheckRobotsRules("/allowed.jpg", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/CamelCase.jpg", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/CAPITALIZE.jpg", RobotsRulesParser::CheckResult::kAllowed);
CheckRobotsRules("/Allowed.jpg", RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("/camelcase.jpg",
RobotsRulesParser::CheckResult::kDisallowed);
CheckRobotsRules("/capitalize.jpg",
RobotsRulesParser::CheckResult::kDisallowed);
VerifyTotalRobotsRulesApplyHistograms(6);
}
} // namespace subresource_redirect