blob: 1a56913feac089b728dc0f3e20365e6f2048c559 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/tracing/background_tracing_rule.h"
#include <memory>
#include "base/base_paths.h"
#include "base/metrics/histogram_macros_local.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/system/sys_info.h"
#include "base/test/bind.h"
#include "base/test/test_proto_loader.h"
#include "base/test/test_timeouts.h"
#include "base/time/time.h"
#include "base/trace_event/named_trigger.h"
#include "build/build_config.h"
#include "content/public/browser/background_tracing_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/perfetto/protos/perfetto/config/chrome/scenario_config.gen.h"
namespace content {
namespace {
base::FilePath GetTestDataRoot() {
return base::PathService::CheckedGet(base::DIR_GEN_TEST_DATA_ROOT);
}
void CreateRuleConfig(const std::string& proto_text,
perfetto::protos::gen::TriggerRule& destination) {
base::TestProtoLoader loader(GetTestDataRoot().Append(FILE_PATH_LITERAL(
"third_party/perfetto/protos/perfetto/"
"config/chrome/scenario_config.descriptor")),
"perfetto.protos.TriggerRule");
std::string serialized_message;
loader.ParseFromText(proto_text, serialized_message);
ASSERT_TRUE(destination.ParseFromString(serialized_message));
}
} // namespace
class BackgroundTracingRuleTest : public testing::Test {
public:
BackgroundTracingRuleTest() = default;
protected:
BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
content::TracingDelegate tracing_delegate;
std::unique_ptr<content::BackgroundTracingManager>
background_tracing_manager =
content::BackgroundTracingManager::CreateInstance(&tracing_delegate);
};
TEST_F(BackgroundTracingRuleTest, HistogramRuleFromValidProto) {
perfetto::protos::gen::TriggerRule config;
CreateRuleConfig(
R"pb(
name: "test_rule"
trigger_chance: 0.5
delay_ms: 500
histogram: { histogram_name: "foo" min_value: 1 max_value: 2 }
)pb",
config);
auto rule = BackgroundTracingRule::Create(config);
auto result = rule->ToProtoForTesting();
EXPECT_EQ("test_rule", result.name());
EXPECT_EQ(0.5, result.trigger_chance());
EXPECT_EQ(500U, result.delay_ms());
EXPECT_TRUE(result.has_histogram());
EXPECT_EQ("foo", result.histogram().histogram_name());
EXPECT_EQ(1, result.histogram().min_value());
EXPECT_EQ(2, result.histogram().max_value());
}
TEST_F(BackgroundTracingRuleTest, HistogramRuleSucceedsOnLowerReferenceValue) {
perfetto::protos::gen::TriggerRule config;
CreateRuleConfig(
R"pb(
name: "test_rule"
histogram: { histogram_name: "foo" min_value: 1 max_value: 2 }
)pb",
config);
auto rule = BackgroundTracingRule::Create(config);
base::RunLoop run_loop;
rule->Install(base::BindLambdaForTesting([&](const BackgroundTracingRule*) {
run_loop.Quit();
return true;
}));
LOCAL_HISTOGRAM_COUNTS("foo", 1);
run_loop.Run();
rule->Uninstall();
}
TEST_F(BackgroundTracingRuleTest, HistogramRuleSucceedsOnUpperReferenceValue) {
perfetto::protos::gen::TriggerRule config;
CreateRuleConfig(
R"pb(
name: "test_rule"
histogram: { histogram_name: "foo" min_value: 1 max_value: 2 }
)pb",
config);
auto rule = BackgroundTracingRule::Create(config);
base::RunLoop run_loop;
rule->Install(base::BindLambdaForTesting([&](const BackgroundTracingRule*) {
run_loop.Quit();
return true;
}));
LOCAL_HISTOGRAM_COUNTS("foo", 2);
run_loop.Run();
rule->Uninstall();
}
TEST_F(BackgroundTracingRuleTest, HistogramRuleSucceedsOnSingleEnumValue) {
perfetto::protos::gen::TriggerRule config;
CreateRuleConfig(
R"pb(
name: "test_rule"
histogram: { histogram_name: "foo" min_value: 1 max_value: 1 }
)pb",
config);
auto rule = BackgroundTracingRule::Create(config);
base::RunLoop run_loop;
rule->Install(base::BindLambdaForTesting([&](const BackgroundTracingRule*) {
run_loop.Quit();
return true;
}));
LOCAL_HISTOGRAM_COUNTS("foo", 1);
run_loop.Run();
rule->Uninstall();
}
TEST_F(BackgroundTracingRuleTest, HistogramRuleFailsOnLowerHistogramSample) {
perfetto::protos::gen::TriggerRule config;
CreateRuleConfig(
R"pb(
name: "test_rule"
histogram: { histogram_name: "foo" min_value: 1 max_value: 2 }
)pb",
config);
auto rule = BackgroundTracingRule::Create(config);
rule->Install(
base::BindLambdaForTesting([](const BackgroundTracingRule*) -> bool {
ADD_FAILURE();
return true;
}));
LOCAL_HISTOGRAM_COUNTS("foo", 0);
task_environment_.FastForwardBy(TestTimeouts::tiny_timeout());
rule->Uninstall();
}
TEST_F(BackgroundTracingRuleTest, HistogramRuleFailsOnHigherHistogramSample) {
perfetto::protos::gen::TriggerRule config;
CreateRuleConfig(
R"pb(
name: "test_rule"
histogram: { histogram_name: "foo" min_value: 1 max_value: 2 }
)pb",
config);
auto rule = BackgroundTracingRule::Create(config);
rule->Install(
base::BindLambdaForTesting([](const BackgroundTracingRule*) -> bool {
ADD_FAILURE();
return true;
}));
LOCAL_HISTOGRAM_COUNTS("foo", 3);
task_environment_.FastForwardBy(TestTimeouts::tiny_timeout());
rule->Uninstall();
}
TEST_F(BackgroundTracingRuleTest, NamedRuleFromValidProto) {
perfetto::protos::gen::TriggerRule config;
CreateRuleConfig(R"pb(
name: "test_rule"
trigger_chance: 0.5
delay_ms: 500
manual_trigger_name: "test_trigger"
)pb",
config);
auto rule = BackgroundTracingRule::Create(config);
auto result = rule->ToProtoForTesting();
EXPECT_EQ("test_rule", result.name());
EXPECT_EQ(0.5, result.trigger_chance());
EXPECT_EQ(500U, result.delay_ms());
EXPECT_EQ("test_trigger", result.manual_trigger_name());
}
TEST_F(BackgroundTracingRuleTest, RuleFromEmptyProto) {
perfetto::protos::gen::TriggerRule config;
CreateRuleConfig(R"pb(
name: "test_rule"
)pb",
config);
auto rule = BackgroundTracingRule::Create(config);
EXPECT_EQ(nullptr, rule);
}
TEST_F(BackgroundTracingRuleTest, TimerRuleFromValidProto) {
perfetto::protos::gen::TriggerRule config;
CreateRuleConfig(R"pb(
name: "test_rule" trigger_chance: 0.5 delay_ms: 500
)pb",
config);
auto rule = BackgroundTracingRule::Create(config);
auto result = rule->ToProtoForTesting();
EXPECT_EQ("test_rule", result.name());
EXPECT_EQ(0.5, result.trigger_chance());
EXPECT_EQ(500U, result.delay_ms());
}
TEST_F(BackgroundTracingRuleTest, TimerRuleTriggersAfterDelay) {
perfetto::protos::gen::TriggerRule config;
CreateRuleConfig(R"pb(
name: "test_rule" delay_ms: 10000
)pb",
config);
base::TimeTicks start = base::TimeTicks::Now();
auto rule = BackgroundTracingRule::Create(config);
base::RunLoop run_loop;
rule->Install(base::BindLambdaForTesting([&](const BackgroundTracingRule*) {
run_loop.Quit();
return true;
}));
run_loop.Run();
DCHECK_GE(base::TimeTicks::Now(), start + base::Milliseconds(10000));
rule->Uninstall();
}
TEST_F(BackgroundTracingRuleTest, RuleActivatesAfterDelay) {
perfetto::protos::gen::TriggerRule config;
CreateRuleConfig(R"pb(
name: "test_rule"
manual_trigger_name: "test_rule"
activation_delay_ms: 10000
)pb",
config);
auto rule = BackgroundTracingRule::Create(config);
base::RunLoop run_loop;
rule->Install(base::BindLambdaForTesting([&](const BackgroundTracingRule*) {
run_loop.Quit();
return true;
}));
// Rule is not activated yet.
EXPECT_FALSE(base::trace_event::EmitNamedTrigger("test_rule"));
task_environment_.FastForwardBy(base::Seconds(10));
EXPECT_TRUE(base::trace_event::EmitNamedTrigger("test_rule"));
run_loop.Run();
rule->Uninstall();
}
TEST_F(BackgroundTracingRuleTest, RepeatingIntervalRuleFromValidProto) {
perfetto::protos::gen::TriggerRule config;
CreateRuleConfig(
R"pb(
name: "test_rule"
trigger_chance: 0.5
delay_ms: 500
repeating_interval: { period_ms: 1000 randomized: true }
)pb",
config);
auto rule = BackgroundTracingRule::Create(config);
auto result = rule->ToProtoForTesting();
EXPECT_EQ("test_rule", result.name());
EXPECT_EQ(0.5, result.trigger_chance());
EXPECT_EQ(500U, result.delay_ms());
EXPECT_TRUE(result.has_repeating_interval());
EXPECT_EQ(1000U, result.repeating_interval().period_ms());
EXPECT_TRUE(result.repeating_interval().randomized());
}
TEST_F(BackgroundTracingRuleTest, RepeatingIntervalRuleTriggersAfterDelay) {
perfetto::protos::gen::TriggerRule config;
CreateRuleConfig(R"pb(
name: "test_rule"
repeating_interval: { period_ms: 2000 }
)pb",
config);
base::TimeTicks start = base::TimeTicks::Now();
auto rule = BackgroundTracingRule::Create(config);
std::vector<base::TimeTicks> trigger_times;
auto callback = base::BindLambdaForTesting([&](const BackgroundTracingRule*) {
trigger_times.push_back(base::TimeTicks::Now());
return true;
});
rule->Install(callback);
task_environment_.FastForwardBy(base::Seconds(2)); // Triggers at 2s
rule->Uninstall();
task_environment_.FastForwardBy(base::Seconds(1));
rule->Install(callback);
task_environment_.FastForwardBy(base::Seconds(2)); // Triggers at 4s
rule->Uninstall();
task_environment_.FastForwardBy(base::Seconds(2)); // Skips 6s
rule->Install(callback);
task_environment_.FastForwardBy(base::Seconds(1)); // Triggers at 8s
EXPECT_EQ(std::vector<base::TimeTicks>({
start + base::Seconds(2),
start + base::Seconds(4),
start + base::Seconds(8),
}),
trigger_times);
rule->Uninstall();
}
TEST_F(BackgroundTracingRuleTest, RepeatingIntervalRuleTriggersRandomized) {
perfetto::protos::gen::TriggerRule config;
CreateRuleConfig(
R"pb(
name: "test_rule"
repeating_interval: { period_ms: 2000 randomized: true }
)pb",
config);
base::TimeTicks start = base::TimeTicks::Now();
auto rule = BackgroundTracingRule::Create(config);
std::vector<base::TimeTicks> trigger_times;
auto callback = base::BindLambdaForTesting([&](const BackgroundTracingRule*) {
trigger_times.push_back(base::TimeTicks::Now());
return true;
});
rule->Install(callback);
task_environment_.FastForwardBy(base::Seconds(4)); // Triggers twice
ASSERT_EQ(2U, trigger_times.size());
EXPECT_GE(trigger_times[0], start);
EXPECT_LT(trigger_times[0], trigger_times[1]);
EXPECT_GE(trigger_times[1], start + base::Seconds(2));
rule->Uninstall();
}
} // namespace content