| // Copyright 2023 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/tracing_scenario.h" |
| |
| #include <memory> |
| |
| #include "base/files/file_path.h" |
| #include "base/path_service.h" |
| #include "base/task/thread_pool.h" |
| #include "base/test/gmock_callback_support.h" |
| #include "base/test/test_proto_loader.h" |
| #include "base/token.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 "services/tracing/perfetto/test_utils.h" |
| #include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| #if BUILDFLAG(IS_POSIX) |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/task/current_thread.h" |
| #include "base/test/bind.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "services/tracing/perfetto/system_test_utils.h" |
| #include "services/tracing/public/cpp/tracing_features.h" |
| #include "services/tracing/tracing_service.h" |
| #endif // BUILDFLAG(IS_POSIX) |
| |
| namespace content { |
| namespace { |
| |
| const char* kDefaultNestedConfig = R"pb( |
| scenario_name: "test_nested_scenario" |
| start_rules: { name: "start_trigger" manual_trigger_name: "start_trigger" } |
| stop_rules: { name: "stop_trigger" manual_trigger_name: "stop_trigger" } |
| upload_rules: { name: "upload_trigger" manual_trigger_name: "upload_trigger" } |
| )pb"; |
| |
| const char* kDefaultConfig = R"pb( |
| scenario_name: "test_scenario" |
| setup_rules: { name: "setup_trigger" manual_trigger_name: "setup_trigger" } |
| start_rules: { name: "start_trigger" manual_trigger_name: "start_trigger" } |
| stop_rules: { name: "stop_trigger" manual_trigger_name: "stop_trigger" } |
| upload_rules: { name: "upload_trigger" manual_trigger_name: "upload_trigger" } |
| trace_config: { |
| data_sources: { config: { name: "org.chromium.trace_metadata" } } |
| } |
| nested_scenarios: { |
| scenario_name: "nested_scenario" |
| start_rules: { |
| name: "nested_start_trigger" |
| manual_trigger_name: "nested_start_trigger" |
| } |
| stop_rules: { |
| name: "nested_stop_trigger" |
| manual_trigger_name: "nested_stop_trigger" |
| } |
| upload_rules: { |
| name: "nested_upload_trigger" |
| manual_trigger_name: "nested_upload_trigger" |
| } |
| } |
| nested_scenarios: { |
| scenario_name: "other_nested_scenario" |
| start_rules: { |
| name: "other_nested_start_trigger" |
| manual_trigger_name: "other_nested_start_trigger" |
| } |
| stop_rules: { |
| name: "other_nested_stop_trigger" |
| manual_trigger_name: "other_nested_stop_trigger" |
| } |
| upload_rules: { |
| name: "other_nested_upload_trigger" |
| manual_trigger_name: "other_nested_upload_trigger" |
| } |
| } |
| )pb"; |
| |
| using testing::_; |
| |
| class TestTracingScenarioDelegate : public TracingScenario::Delegate { |
| public: |
| ~TestTracingScenarioDelegate() = default; |
| |
| MOCK_METHOD(bool, OnScenarioActive, (TracingScenario * scenario), (override)); |
| MOCK_METHOD(bool, OnScenarioIdle, (TracingScenario * scenario), (override)); |
| MOCK_METHOD(bool, OnScenarioCloned, (TracingScenario * scenario), (override)); |
| MOCK_METHOD(void, |
| OnScenarioError, |
| (TracingScenario * scenario, perfetto::TracingError), |
| (override)); |
| MOCK_METHOD(void, |
| OnScenarioRecording, |
| (TracingScenario * scenario), |
| (override)); |
| MOCK_METHOD(void, |
| SaveTrace, |
| (TracingScenario * scenario, |
| base::Token trace_uuid, |
| const BackgroundTracingRule* triggered_rule, |
| std::string&& trace_data), |
| (override)); |
| }; |
| |
| class TestNestedTracingScenarioDelegate |
| : public NestedTracingScenario::Delegate { |
| public: |
| ~TestNestedTracingScenarioDelegate() = default; |
| |
| MOCK_METHOD(void, |
| OnNestedScenarioStart, |
| (NestedTracingScenario * scenario), |
| (override)); |
| MOCK_METHOD(void, |
| OnNestedScenarioStop, |
| (NestedTracingScenario * scenario), |
| (override)); |
| MOCK_METHOD(void, |
| OnNestedScenarioUpload, |
| (NestedTracingScenario * scenario, |
| const BackgroundTracingRule* triggered_rule), |
| (override)); |
| }; |
| |
| // Fake perfetto::TracingSession. |
| class TestTracingSession : public perfetto::TracingSession { |
| public: |
| static constexpr base::Token kClonedSessionId = base::Token(0xAB, 0xCD); |
| |
| TestTracingSession() = default; |
| ~TestTracingSession() override = default; |
| |
| void Setup(const perfetto::TraceConfig& config, int fd = -1) override { |
| if (!config.data_sources().empty()) { |
| start_should_fail_ = |
| config.data_sources()[0].config().name() == "Invalid"; |
| should_spuriously_stop = |
| config.data_sources()[0].config().name() == "Stop"; |
| } |
| } |
| |
| void Start() override { |
| if (start_should_fail_) { |
| base::ThreadPool::PostTask( |
| FROM_HERE, |
| {base::MayBlock(), base::TaskPriority::BEST_EFFORT, |
| base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, |
| base::BindOnce( |
| [](std::function<void(perfetto::TracingError)> // nocheck |
| on_error_callback) { |
| on_error_callback( |
| {perfetto::TracingError::kTracingFailed, "error"}); |
| }, |
| on_error_callback_)); |
| return; |
| } |
| if (should_spuriously_stop) { |
| Stop(); |
| return; |
| } |
| // perfetto::TracingSession runs callbacks from its own background thread. |
| base::ThreadPool::PostTask( |
| FROM_HERE, |
| {base::MayBlock(), base::TaskPriority::BEST_EFFORT, |
| base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, |
| base::BindOnce( |
| [](std::function<void()> on_start_callback) { // nocheck |
| on_start_callback(); |
| }, |
| on_start_callback_)); |
| } |
| |
| void StartBlocking() override { NOTIMPLEMENTED(); } |
| |
| void SetOnStartCallback(std::function<void()> on_start) override { // nocheck |
| on_start_callback_ = on_start; |
| } |
| |
| void SetOnErrorCallback( |
| std::function<void(perfetto::TracingError)> on_error) // nocheck |
| override { |
| on_error_callback_ = on_error; |
| } |
| |
| void Flush(std::function<void(bool)>, uint32_t timeout_ms = 0) // nocheck |
| override { |
| NOTIMPLEMENTED(); |
| } |
| |
| void Stop() override { |
| // perfetto::TracingSession runs callbacks from its own background thread. |
| base::ThreadPool::PostTask( |
| FROM_HERE, |
| {base::MayBlock(), base::TaskPriority::BEST_EFFORT, |
| base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, |
| base::BindOnce( |
| [](std::function<void()> on_stop_callback) { // nocheck |
| on_stop_callback(); |
| }, |
| on_stop_callback_)); |
| } |
| |
| void CloneTrace(CloneTraceArgs args, |
| CloneTraceCallback on_session_cloned) override { |
| // perfetto::TracingSession runs callbacks from its own background thread. |
| base::ThreadPool::PostTask( |
| FROM_HERE, |
| {base::MayBlock(), base::TaskPriority::BEST_EFFORT, |
| base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, |
| base::BindOnce( |
| [](CloneTraceCallback on_session_cloned) { // nocheck |
| on_session_cloned( |
| {true, "", kClonedSessionId.low(), kClonedSessionId.high()}); |
| }, |
| on_session_cloned)); |
| } |
| |
| void StopBlocking() override { NOTIMPLEMENTED(); } |
| |
| void SetOnStopCallback(std::function<void()> on_stop) override { // nocheck |
| on_stop_callback_ = on_stop; |
| } |
| |
| void ChangeTraceConfig(const perfetto::TraceConfig&) override { |
| NOTIMPLEMENTED(); |
| } |
| void ReadTrace(ReadTraceCallback read_callback) override { |
| base::ThreadPool::PostTask( |
| FROM_HERE, |
| {base::MayBlock(), base::TaskPriority::BEST_EFFORT, |
| base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, |
| base::BindOnce( |
| [](ReadTraceCallback read_callback) { // nocheck |
| std::string trace_content = "this is a trace"; |
| read_callback({trace_content.data(), trace_content.size(), |
| /*has_more=*/false}); |
| }, |
| read_callback)); |
| } |
| void GetTraceStats(GetTraceStatsCallback) override { NOTIMPLEMENTED(); } |
| void QueryServiceState(QueryServiceStateCallback) override { |
| NOTIMPLEMENTED(); |
| } |
| |
| private: |
| std::function<void()> on_start_callback_; // nocheck |
| std::function<void()> on_stop_callback_; // nocheck |
| std::function<void(perfetto::TracingError)> on_error_callback_; // nocheck |
| bool start_should_fail_ = false; |
| bool should_spuriously_stop = false; |
| }; |
| |
| class TracingScenarioForTesting : public TracingScenario { |
| public: |
| TracingScenarioForTesting(const perfetto::protos::gen::ScenarioConfig& config, |
| TestTracingScenarioDelegate* delegate) |
| : TracingScenario(config, |
| delegate, |
| /*enable_privacy_filter=*/false, |
| /*is_local_scenario=*/true, |
| /*request_startup_tracing=*/false) { |
| EXPECT_TRUE(Initialize(config, false)); |
| } |
| |
| protected: |
| std::unique_ptr<perfetto::TracingSession> CreateTracingSession() override { |
| return std::make_unique<TestTracingSession>(); |
| } |
| }; |
| |
| class NestedTracingScenarioForTesting : public NestedTracingScenario { |
| public: |
| NestedTracingScenarioForTesting( |
| const perfetto::protos::gen::NestedScenarioConfig& config, |
| NestedTracingScenario::Delegate* delegate) |
| : NestedTracingScenario(config, delegate) { |
| EXPECT_TRUE(Initialize(config)); |
| } |
| }; |
| |
| perfetto::protos::gen::ScenarioConfig ParseScenarioConfigFromText( |
| const std::string& proto_text) { |
| base::TestProtoLoader config_loader( |
| base::PathService::CheckedGet(base::DIR_GEN_TEST_DATA_ROOT) |
| .Append( |
| FILE_PATH_LITERAL("third_party/perfetto/protos/perfetto/" |
| "config/chrome/scenario_config.descriptor")), |
| "perfetto.protos.ScenarioConfig"); |
| std::string serialized_message; |
| config_loader.ParseFromText(proto_text, serialized_message); |
| perfetto::protos::gen::ScenarioConfig destination; |
| destination.ParseFromString(serialized_message); |
| return destination; |
| } |
| |
| perfetto::protos::gen::NestedScenarioConfig ParseNestedScenarioConfigFromText( |
| const std::string& proto_text) { |
| base::TestProtoLoader config_loader( |
| base::PathService::CheckedGet(base::DIR_GEN_TEST_DATA_ROOT) |
| .Append( |
| FILE_PATH_LITERAL("third_party/perfetto/protos/perfetto/" |
| "config/chrome/scenario_config.descriptor")), |
| "perfetto.protos.NestedScenarioConfig"); |
| std::string serialized_message; |
| config_loader.ParseFromText(proto_text, serialized_message); |
| perfetto::protos::gen::NestedScenarioConfig destination; |
| destination.ParseFromString(serialized_message); |
| return destination; |
| } |
| |
| class TracingScenarioTest : public testing::Test { |
| public: |
| TracingScenarioTest() |
| : background_tracing_manager_( |
| content::BackgroundTracingManager::CreateInstance()) {} |
| |
| protected: |
| BrowserTaskEnvironment task_environment; |
| tracing::TracedProcessForTesting traced_process{ |
| base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})}; |
| TestTracingScenarioDelegate delegate; |
| TestNestedTracingScenarioDelegate nested_delegate; |
| std::unique_ptr<content::BackgroundTracingManager> |
| background_tracing_manager_; |
| }; |
| |
| class NestedTracingScenarioTest : public testing::Test { |
| public: |
| NestedTracingScenarioTest() |
| : background_tracing_manager_( |
| content::BackgroundTracingManager::CreateInstance()) {} |
| |
| protected: |
| BrowserTaskEnvironment task_environment; |
| TestNestedTracingScenarioDelegate delegate; |
| std::unique_ptr<content::BackgroundTracingManager> |
| background_tracing_manager_; |
| }; |
| |
| } // namespace |
| |
| TEST_F(TracingScenarioTest, Init) { |
| TracingScenarioForTesting tracing_scenario( |
| ParseScenarioConfigFromText(R"pb( |
| scenario_name: "test_scenario" |
| trace_config: { |
| data_sources: { config: { name: "org.chromium.trace_metadata" } } |
| } |
| )pb"), |
| &delegate); |
| EXPECT_EQ("test_scenario", tracing_scenario.scenario_name()); |
| EXPECT_EQ(TracingScenario::State::kDisabled, |
| tracing_scenario.current_state()); |
| } |
| |
| TEST_F(TracingScenarioTest, Disabled) { |
| TracingScenarioForTesting tracing_scenario( |
| ParseScenarioConfigFromText(kDefaultConfig), &delegate); |
| |
| EXPECT_CALL(delegate, OnScenarioActive(&tracing_scenario)).Times(0); |
| |
| EXPECT_FALSE(base::trace_event::EmitNamedTrigger("setup_trigger")); |
| EXPECT_FALSE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| EXPECT_FALSE(base::trace_event::EmitNamedTrigger("nested_start_trigger")); |
| |
| tracing_scenario.Enable(); |
| EXPECT_EQ(TracingScenario::State::kEnabled, tracing_scenario.current_state()); |
| tracing_scenario.Disable(); |
| EXPECT_EQ(TracingScenario::State::kDisabled, |
| tracing_scenario.current_state()); |
| |
| EXPECT_FALSE(base::trace_event::EmitNamedTrigger("setup_trigger")); |
| EXPECT_FALSE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| EXPECT_FALSE(base::trace_event::EmitNamedTrigger("nested_start_trigger")); |
| } |
| |
| TEST_F(TracingScenarioTest, StartStop) { |
| TracingScenarioForTesting tracing_scenario( |
| ParseScenarioConfigFromText(kDefaultConfig), &delegate); |
| |
| tracing_scenario.Enable(); |
| EXPECT_EQ(TracingScenario::State::kEnabled, tracing_scenario.current_state()); |
| EXPECT_CALL(delegate, OnScenarioActive(&tracing_scenario)) |
| .WillOnce(testing::Return(true)); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| |
| base::RunLoop run_loop; |
| EXPECT_CALL(delegate, OnScenarioIdle(&tracing_scenario)) |
| .WillOnce([&run_loop]() { |
| run_loop.Quit(); |
| return true; |
| }); |
| |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("stop_trigger")); |
| run_loop.Run(); |
| EXPECT_EQ(TracingScenario::State::kDisabled, |
| tracing_scenario.current_state()); |
| } |
| |
| TEST_F(TracingScenarioTest, NestedStartStop) { |
| TracingScenarioForTesting tracing_scenario( |
| ParseScenarioConfigFromText(kDefaultConfig), &delegate); |
| |
| tracing_scenario.Enable(); |
| EXPECT_EQ(TracingScenario::State::kEnabled, tracing_scenario.current_state()); |
| EXPECT_CALL(delegate, OnScenarioActive(&tracing_scenario)) |
| .WillOnce(testing::Return(true)); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("nested_start_trigger")); |
| |
| EXPECT_FALSE(base::trace_event::EmitNamedTrigger("stop_trigger")); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("nested_stop_trigger")); |
| |
| base::RunLoop run_loop; |
| EXPECT_CALL(delegate, OnScenarioIdle(&tracing_scenario)) |
| .WillOnce([&run_loop]() { |
| run_loop.Quit(); |
| return true; |
| }); |
| |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("stop_trigger")); |
| run_loop.Run(); |
| EXPECT_EQ(TracingScenario::State::kDisabled, |
| tracing_scenario.current_state()); |
| } |
| |
| TEST_F(TracingScenarioTest, StartFail) { |
| TracingScenarioForTesting tracing_scenario( |
| ParseScenarioConfigFromText(R"pb( |
| scenario_name: "test_scenario" |
| start_rules: { |
| name: "start_trigger" |
| manual_trigger_name: "start_trigger" |
| } |
| stop_rules: { name: "stop_trigger" manual_trigger_name: "stop_trigger" } |
| trace_config: { data_sources: { config: { name: "Invalid" } } } |
| )pb"), |
| &delegate); |
| |
| tracing_scenario.Enable(); |
| EXPECT_EQ(TracingScenario::State::kEnabled, tracing_scenario.current_state()); |
| EXPECT_CALL(delegate, OnScenarioActive(&tracing_scenario)) |
| .WillOnce(testing::Return(true)); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| |
| base::RunLoop run_loop; |
| EXPECT_CALL(delegate, OnScenarioIdle(&tracing_scenario)) |
| .WillOnce([&run_loop]() { |
| run_loop.Quit(); |
| return true; |
| }); |
| |
| run_loop.Run(); |
| EXPECT_EQ(TracingScenario::State::kDisabled, |
| tracing_scenario.current_state()); |
| EXPECT_FALSE(base::trace_event::EmitNamedTrigger("stop_trigger")); |
| } |
| |
| TEST_F(TracingScenarioTest, SpuriousStop) { |
| TracingScenarioForTesting tracing_scenario( |
| ParseScenarioConfigFromText(R"pb( |
| scenario_name: "test_scenario" |
| start_rules: { |
| name: "start_trigger" |
| manual_trigger_name: "start_trigger" |
| } |
| stop_rules: { name: "stop_trigger" manual_trigger_name: "stop_trigger" } |
| trace_config: { data_sources: { config: { name: "Stop" } } } |
| )pb"), |
| &delegate); |
| |
| tracing_scenario.Enable(); |
| EXPECT_EQ(TracingScenario::State::kEnabled, tracing_scenario.current_state()); |
| EXPECT_CALL(delegate, OnScenarioActive(&tracing_scenario)) |
| .WillOnce(testing::Return(true)); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| |
| base::RunLoop run_loop; |
| EXPECT_CALL(delegate, OnScenarioIdle(&tracing_scenario)) |
| .WillOnce([&run_loop]() { |
| run_loop.Quit(); |
| return true; |
| }); |
| |
| run_loop.Run(); |
| EXPECT_EQ(TracingScenario::State::kDisabled, |
| tracing_scenario.current_state()); |
| EXPECT_FALSE(base::trace_event::EmitNamedTrigger("stop_trigger")); |
| } |
| |
| TEST_F(TracingScenarioTest, SetupStop) { |
| TracingScenarioForTesting tracing_scenario( |
| ParseScenarioConfigFromText(kDefaultConfig), &delegate); |
| |
| tracing_scenario.Enable(); |
| EXPECT_CALL(delegate, OnScenarioActive(&tracing_scenario)) |
| .WillOnce(testing::Return(true)); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("setup_trigger")); |
| EXPECT_EQ(TracingScenario::State::kSetup, tracing_scenario.current_state()); |
| |
| base::RunLoop run_loop; |
| EXPECT_CALL(delegate, OnScenarioIdle(&tracing_scenario)) |
| .WillOnce([&run_loop]() { |
| run_loop.Quit(); |
| return true; |
| }); |
| |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("stop_trigger")); |
| run_loop.Run(); |
| EXPECT_EQ(TracingScenario::State::kDisabled, |
| tracing_scenario.current_state()); |
| } |
| |
| TEST_F(TracingScenarioTest, SetupUpload) { |
| TracingScenarioForTesting tracing_scenario( |
| ParseScenarioConfigFromText(kDefaultConfig), &delegate); |
| |
| tracing_scenario.Enable(); |
| EXPECT_CALL(delegate, OnScenarioActive(&tracing_scenario)) |
| .WillOnce(testing::Return(true)); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("setup_trigger")); |
| EXPECT_EQ(TracingScenario::State::kSetup, tracing_scenario.current_state()); |
| |
| base::Token trace_uuid = tracing_scenario.GetSessionID(); |
| base::RunLoop run_loop; |
| EXPECT_CALL(delegate, SaveTrace(_, trace_uuid, _, _)).Times(0); |
| EXPECT_CALL(delegate, OnScenarioIdle(&tracing_scenario)) |
| .WillOnce([&run_loop]() { |
| run_loop.Quit(); |
| return true; |
| }); |
| |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("upload_trigger")); |
| run_loop.Run(); |
| EXPECT_EQ(TracingScenario::State::kDisabled, |
| tracing_scenario.current_state()); |
| } |
| |
| TEST_F(TracingScenarioTest, SetupStartStop) { |
| TracingScenarioForTesting tracing_scenario( |
| ParseScenarioConfigFromText(kDefaultConfig), &delegate); |
| |
| tracing_scenario.Enable(); |
| EXPECT_CALL(delegate, OnScenarioActive(&tracing_scenario)) |
| .WillOnce(testing::Return(true)); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("setup_trigger")); |
| EXPECT_EQ(TracingScenario::State::kSetup, tracing_scenario.current_state()); |
| |
| EXPECT_CALL(delegate, OnScenarioActive(&tracing_scenario)).Times(0); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| EXPECT_EQ(TracingScenario::State::kRecording, |
| tracing_scenario.current_state()); |
| |
| base::RunLoop run_loop; |
| EXPECT_CALL(delegate, OnScenarioIdle(&tracing_scenario)) |
| .WillOnce([&run_loop]() { |
| run_loop.Quit(); |
| return true; |
| }); |
| |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("stop_trigger")); |
| run_loop.Run(); |
| EXPECT_EQ(TracingScenario::State::kDisabled, |
| tracing_scenario.current_state()); |
| } |
| |
| TEST_F(TracingScenarioTest, SetupNestedStartStop) { |
| TracingScenarioForTesting tracing_scenario( |
| ParseScenarioConfigFromText(kDefaultConfig), &delegate); |
| |
| tracing_scenario.Enable(); |
| EXPECT_CALL(delegate, OnScenarioActive(&tracing_scenario)) |
| .WillOnce(testing::Return(true)); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("setup_trigger")); |
| EXPECT_EQ(TracingScenario::State::kSetup, tracing_scenario.current_state()); |
| |
| EXPECT_CALL(delegate, OnScenarioActive(&tracing_scenario)).Times(0); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("nested_start_trigger")); |
| EXPECT_EQ(TracingScenario::State::kRecording, |
| tracing_scenario.current_state()); |
| EXPECT_FALSE( |
| base::trace_event::EmitNamedTrigger("other_nested_start_trigger")); |
| EXPECT_FALSE(base::trace_event::EmitNamedTrigger("stop_trigger")); |
| |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("nested_stop_trigger")); |
| |
| base::RunLoop run_loop; |
| EXPECT_CALL(delegate, OnScenarioIdle(&tracing_scenario)) |
| .WillOnce([&run_loop]() { |
| run_loop.Quit(); |
| return true; |
| }); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("stop_trigger")); |
| run_loop.Run(); |
| EXPECT_EQ(TracingScenario::State::kDisabled, |
| tracing_scenario.current_state()); |
| } |
| |
| TEST_F(TracingScenarioTest, Abort) { |
| TracingScenarioForTesting tracing_scenario( |
| ParseScenarioConfigFromText(kDefaultConfig), &delegate); |
| |
| tracing_scenario.Enable(); |
| EXPECT_CALL(delegate, OnScenarioActive(&tracing_scenario)) |
| .WillOnce(testing::Return(true)); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| EXPECT_EQ(TracingScenario::State::kRecording, |
| tracing_scenario.current_state()); |
| |
| base::RunLoop run_loop; |
| EXPECT_CALL(delegate, OnScenarioIdle(&tracing_scenario)) |
| .WillOnce([&run_loop]() { |
| run_loop.Quit(); |
| return true; |
| }); |
| |
| tracing_scenario.Abort(); |
| run_loop.Run(); |
| EXPECT_EQ(TracingScenario::State::kDisabled, |
| tracing_scenario.current_state()); |
| EXPECT_FALSE(base::trace_event::EmitNamedTrigger("stop_trigger")); |
| } |
| |
| TEST_F(TracingScenarioTest, Upload) { |
| TracingScenarioForTesting tracing_scenario( |
| ParseScenarioConfigFromText(kDefaultConfig), &delegate); |
| |
| tracing_scenario.Enable(); |
| EXPECT_CALL(delegate, OnScenarioActive(&tracing_scenario)) |
| .WillOnce(testing::Return(true)); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| |
| base::Token trace_uuid = tracing_scenario.GetSessionID(); |
| base::RunLoop run_loop; |
| EXPECT_CALL(delegate, SaveTrace(&tracing_scenario, trace_uuid, _, |
| std::string("this is a trace"))) |
| .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure())); |
| EXPECT_CALL(delegate, OnScenarioIdle(&tracing_scenario)) |
| .WillOnce(testing::Return(true)); |
| |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("upload_trigger")); |
| |
| run_loop.Run(); |
| EXPECT_EQ(TracingScenario::State::kDisabled, |
| tracing_scenario.current_state()); |
| } |
| |
| TEST_F(TracingScenarioTest, StopUpload) { |
| TracingScenarioForTesting tracing_scenario( |
| ParseScenarioConfigFromText(kDefaultConfig), &delegate); |
| |
| tracing_scenario.Enable(); |
| EXPECT_CALL(delegate, OnScenarioActive(&tracing_scenario)) |
| .WillOnce(testing::Return(true)); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| |
| base::Token trace_uuid = tracing_scenario.GetSessionID(); |
| base::RunLoop run_loop; |
| EXPECT_CALL(delegate, SaveTrace(&tracing_scenario, trace_uuid, _, |
| std::string("this is a trace"))) |
| .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure())); |
| EXPECT_CALL(delegate, OnScenarioIdle(&tracing_scenario)) |
| .WillOnce(testing::Return(true)); |
| |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("stop_trigger")); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("upload_trigger")); |
| |
| run_loop.Run(); |
| EXPECT_EQ(TracingScenario::State::kDisabled, |
| tracing_scenario.current_state()); |
| } |
| |
| TEST_F(TracingScenarioTest, NestedUpload) { |
| TracingScenarioForTesting tracing_scenario( |
| ParseScenarioConfigFromText(kDefaultConfig), &delegate); |
| |
| tracing_scenario.Enable(); |
| EXPECT_CALL(delegate, OnScenarioActive(&tracing_scenario)) |
| .WillOnce(testing::Return(true)); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("nested_start_trigger")); |
| |
| { |
| base::RunLoop run_loop; |
| EXPECT_CALL(delegate, OnScenarioCloned(&tracing_scenario)) |
| .WillOnce(testing::Return(true)); |
| EXPECT_CALL(delegate, SaveTrace(&tracing_scenario, |
| TestTracingSession::kClonedSessionId, _, |
| std::string("this is a trace"))) |
| .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure())); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("nested_upload_trigger")); |
| run_loop.Run(); |
| } |
| |
| { |
| base::RunLoop run_loop; |
| EXPECT_EQ(TracingScenario::State::kRecording, |
| tracing_scenario.current_state()); |
| |
| EXPECT_CALL(delegate, OnScenarioIdle(&tracing_scenario)) |
| .WillOnce([&run_loop]() { |
| run_loop.Quit(); |
| return true; |
| }); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("stop_trigger")); |
| run_loop.Run(); |
| } |
| } |
| |
| TEST_F(NestedTracingScenarioTest, Disabled) { |
| NestedTracingScenarioForTesting tracing_scenario( |
| ParseNestedScenarioConfigFromText(kDefaultNestedConfig), &delegate); |
| |
| EXPECT_CALL(delegate, OnNestedScenarioStart(&tracing_scenario)).Times(0); |
| |
| EXPECT_FALSE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| |
| tracing_scenario.Enable(); |
| EXPECT_EQ(NestedTracingScenario::State::kEnabled, |
| tracing_scenario.current_state()); |
| tracing_scenario.Disable(); |
| EXPECT_EQ(NestedTracingScenario::State::kDisabled, |
| tracing_scenario.current_state()); |
| |
| EXPECT_FALSE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| } |
| |
| TEST_F(NestedTracingScenarioTest, StartStop) { |
| NestedTracingScenarioForTesting tracing_scenario( |
| ParseNestedScenarioConfigFromText(kDefaultNestedConfig), &delegate); |
| |
| tracing_scenario.Enable(); |
| EXPECT_EQ(NestedTracingScenario::State::kEnabled, |
| tracing_scenario.current_state()); |
| EXPECT_CALL(delegate, OnNestedScenarioStart(&tracing_scenario)).Times(1); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| |
| EXPECT_CALL(delegate, OnNestedScenarioStop(&tracing_scenario)).Times(1); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("stop_trigger")); |
| EXPECT_EQ(NestedTracingScenario::State::kStopping, |
| tracing_scenario.current_state()); |
| tracing_scenario.Disable(); |
| EXPECT_EQ(NestedTracingScenario::State::kDisabled, |
| tracing_scenario.current_state()); |
| } |
| |
| TEST_F(NestedTracingScenarioTest, Upload) { |
| NestedTracingScenarioForTesting tracing_scenario( |
| ParseNestedScenarioConfigFromText(kDefaultNestedConfig), &delegate); |
| |
| tracing_scenario.Enable(); |
| EXPECT_CALL(delegate, OnNestedScenarioStart(&tracing_scenario)).Times(1); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| |
| EXPECT_CALL(delegate, OnNestedScenarioUpload(&tracing_scenario, _)).Times(1); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("upload_trigger")); |
| |
| EXPECT_EQ(NestedTracingScenario::State::kDisabled, |
| tracing_scenario.current_state()); |
| } |
| |
| TEST_F(NestedTracingScenarioTest, StopUpload) { |
| NestedTracingScenarioForTesting tracing_scenario( |
| ParseNestedScenarioConfigFromText(kDefaultNestedConfig), &delegate); |
| |
| tracing_scenario.Enable(); |
| EXPECT_CALL(delegate, OnNestedScenarioStart(&tracing_scenario)).Times(1); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| |
| base::RunLoop run_loop; |
| EXPECT_CALL(delegate, OnNestedScenarioUpload(&tracing_scenario, _)) |
| .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure())); |
| EXPECT_CALL(delegate, OnNestedScenarioStop(&tracing_scenario)).Times(1); |
| |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("stop_trigger")); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("upload_trigger")); |
| |
| run_loop.Run(); |
| EXPECT_EQ(NestedTracingScenario::State::kDisabled, |
| tracing_scenario.current_state()); |
| } |
| |
| #if BUILDFLAG(IS_POSIX) |
| |
| const char* kSystemProbeScenarioConfig = R"pb( |
| scenario_name: "test_scenario" |
| setup_rules: { manual_trigger_name: "setup_trigger" } |
| start_rules: { manual_trigger_name: "start_trigger" } |
| stop_rules: { manual_trigger_name: "stop_trigger" } |
| trace_config: { data_sources: { config: { name: "mock_data_source" } } } |
| use_system_backend: true |
| )pb"; |
| |
| class TracingScenarioSystemBackendTest : public testing::Test { |
| public: |
| static constexpr char kPerfettoConsumerSockName[] = |
| "PERFETTO_CONSUMER_SOCK_NAME"; |
| static constexpr char kPerfettoProducerSockName[] = |
| "PERFETTO_PRODUCER_SOCK_NAME"; |
| TracingScenarioSystemBackendTest() |
| : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP) {} |
| |
| void TearDown() override { |
| system_producer_ = nullptr; |
| system_service_ = nullptr; |
| traced_process_ = nullptr; |
| |
| // Restore env variables after shutdown of the above to data races with the |
| // muxer thread. |
| if (saved_consumer_sock_name_) { |
| ASSERT_EQ(0, setenv(kPerfettoConsumerSockName, |
| saved_consumer_sock_name_->c_str(), 1)); |
| } else { |
| ASSERT_EQ(0, unsetenv(kPerfettoConsumerSockName)); |
| } |
| if (saved_producer_sock_name_) { |
| ASSERT_EQ(0, setenv(kPerfettoProducerSockName, |
| saved_producer_sock_name_->c_str(), 1)); |
| } else { |
| ASSERT_EQ(0, unsetenv(kPerfettoConsumerSockName)); |
| } |
| } |
| |
| // This is not implemented as Setup() because system tracing feature flags |
| // affect the behavior of PerfettoTracedProcess::SetupClientLibrary(). The |
| // tests will need to enable/disable features before |traced_process| is |
| // created. |
| void InitTracing() { |
| // The test connects to the mock system service. |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| system_service_ = std::make_unique<tracing::MockSystemService>(temp_dir_); |
| |
| const auto* env_val = getenv(kPerfettoConsumerSockName); |
| if (env_val) { |
| saved_consumer_sock_name_ = env_val; |
| } |
| ASSERT_EQ(0, setenv(kPerfettoConsumerSockName, |
| system_service_->consumer().c_str(), 1)); |
| |
| env_val = getenv(kPerfettoProducerSockName); |
| if (env_val) { |
| saved_producer_sock_name_ = env_val; |
| } |
| ASSERT_EQ(0, setenv(kPerfettoProducerSockName, |
| system_service_->producer().c_str(), 1)); |
| |
| traced_process_ = std::make_unique<tracing::TracedProcessForTesting>( |
| base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})); |
| background_tracing_manager_ = |
| content::BackgroundTracingManager::CreateInstance(); |
| |
| // Connect the producer to the tracing service. |
| system_producer_ = std::make_unique<tracing::MockProducer>(); |
| system_producer_->Connect(system_service_->GetService(), "mock_producer"); |
| system_producer_->RegisterDataSource("mock_data_source"); |
| |
| // Also enable the custom backend. |
| static tracing::mojom::TracingService* s_service; |
| s_service = &tracing_service_; |
| // Check if the consumer backend is used. |
| static bool* consumer_conn_created; |
| consumer_conn_created = &custom_backend_consumer_conn_created_; |
| auto factory = []() -> tracing::mojom::TracingService& { |
| *consumer_conn_created = true; |
| return *s_service; |
| }; |
| tracing::PerfettoTracedProcess::Get().SetConsumerConnectionFactory( |
| factory, base::SequencedTaskRunner::GetCurrentDefault()); |
| |
| // Reset the callback that controls whether system consumer is allowed. |
| tracing::PerfettoTracedProcess::Get().SetAllowSystemTracingConsumerCallback( |
| base::RepeatingCallback<bool()>()); |
| } |
| |
| protected: |
| // Not inheriting from TracingScenarioTest because initialization of |
| // |traced_process_| depends on feature flags and environment variables. |
| BrowserTaskEnvironment task_environment_; |
| std::unique_ptr<tracing::TracedProcessForTesting> traced_process_; |
| TestTracingScenarioDelegate delegate; |
| std::unique_ptr<content::BackgroundTracingManager> |
| background_tracing_manager_; |
| |
| base::ScopedTempDir temp_dir_; |
| std::unique_ptr<tracing::MockSystemService> system_service_; |
| std::optional<std::string> saved_consumer_sock_name_; |
| std::optional<std::string> saved_producer_sock_name_; |
| std::unique_ptr<tracing::MockProducer> system_producer_; |
| |
| tracing::PerfettoService perfetto_service_; |
| tracing::TracingService tracing_service_; |
| |
| // Set to true when a consumer connection of the custom backend is created. |
| bool custom_backend_consumer_conn_created_ = false; |
| }; |
| |
| // Test the system backend by creating a real Perfetto tracing service thread |
| // that listens to producer and consumer sockets. Config the tracing scenario to |
| // use the system backend and check from the producer that a trace session is |
| // started and stopped through the system backend. |
| TEST_F(TracingScenarioSystemBackendTest, StartStop) { |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitWithFeatures( |
| /*enabled_features=*/{features::kEnablePerfettoSystemTracing, |
| features::kEnablePerfettoSystemBackgroundTracing}, |
| /*disabled_features=*/{}); |
| InitTracing(); |
| |
| bool allow_system_consumer_checked = false; |
| auto callback = |
| base::BindLambdaForTesting([&allow_system_consumer_checked]() { |
| allow_system_consumer_checked = true; |
| // Allow system consumer connections. This is typically checked in |
| // ChromeTracingDelegate::IsSystemWideTracingEnabled(). |
| return true; |
| }); |
| tracing::PerfettoTracedProcess::Get().SetAllowSystemTracingConsumerCallback( |
| std::move(callback)); |
| |
| // Create a real tracing scenario. Under the hood the system backend is used. |
| auto tracing_scenario = TracingScenario::Create( |
| ParseScenarioConfigFromText(kSystemProbeScenarioConfig), false, false, |
| false, false, &delegate); |
| |
| tracing_scenario->Enable(); |
| EXPECT_EQ(TracingScenario::State::kEnabled, |
| tracing_scenario->current_state()); |
| EXPECT_CALL(delegate, OnScenarioActive(tracing_scenario.get())) |
| .WillOnce(testing::Return(true)); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("setup_trigger")); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| |
| // Wait until the data source is started. |
| base::RunLoop run_loop_start; |
| EXPECT_CALL(*system_producer_.get(), OnStartDataSource(_, _)) |
| .WillOnce([&run_loop_start]() { |
| run_loop_start.Quit(); |
| return true; |
| }); |
| run_loop_start.Run(); |
| |
| // Stop tracing. Wait until the data source is stopped. |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("stop_trigger")); |
| base::RunLoop run_loop_stop; |
| EXPECT_CALL(*system_producer_.get(), OnStopDataSource(_, _)) |
| .WillOnce([&run_loop_stop]() { |
| run_loop_stop.Quit(); |
| return true; |
| }); |
| run_loop_stop.Run(); |
| |
| // Run until the scenario is idle. |
| base::RunLoop run_loop_idle; |
| EXPECT_CALL(delegate, OnScenarioIdle(tracing_scenario.get())) |
| .WillOnce([&run_loop_idle]() { |
| run_loop_idle.Quit(); |
| return true; |
| }); |
| run_loop_idle.Run(); |
| |
| EXPECT_EQ(TracingScenario::State::kDisabled, |
| tracing_scenario->current_state()); |
| EXPECT_TRUE(allow_system_consumer_checked); |
| } |
| |
| // Test that system consumer connections are denied. |
| TEST_F(TracingScenarioSystemBackendTest, SystemConsumerNotAllowedByCallback) { |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitWithFeatures( |
| /*enabled_features=*/{features::kEnablePerfettoSystemTracing, |
| features::kEnablePerfettoSystemBackgroundTracing}, |
| /*disabled_features=*/{}); |
| InitTracing(); |
| |
| bool allow_system_consumer_checked = false; |
| auto callback = |
| base::BindLambdaForTesting([&allow_system_consumer_checked]() { |
| allow_system_consumer_checked = true; |
| return false; // Deny system consumer connection. |
| }); |
| |
| tracing::PerfettoTracedProcess::Get().SetAllowSystemTracingConsumerCallback( |
| std::move(callback)); |
| |
| // Create a real tracing scenario. |
| auto tracing_scenario = TracingScenario::Create( |
| ParseScenarioConfigFromText(kSystemProbeScenarioConfig), false, false, |
| false, false, &delegate); |
| |
| tracing_scenario->Enable(); |
| EXPECT_EQ(TracingScenario::State::kEnabled, |
| tracing_scenario->current_state()); |
| EXPECT_CALL(delegate, OnScenarioActive(tracing_scenario.get())) |
| .WillOnce(testing::Return(true)); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("setup_trigger")); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| |
| // The system data producer isn't used. |
| EXPECT_CALL(*system_producer_.get(), OnStartDataSource(_, _)).Times(0); |
| // The callback doesn't allow system consumer. The scenario won't be started. |
| |
| tracing_scenario->Abort(); |
| base::RunLoop run_loop_idle; |
| EXPECT_CALL(delegate, OnScenarioIdle(tracing_scenario.get())) |
| .WillOnce([&run_loop_idle]() { |
| run_loop_idle.Quit(); |
| return true; |
| }); |
| run_loop_idle.Run(); |
| EXPECT_TRUE(allow_system_consumer_checked); |
| // Not using the custom backend. |
| EXPECT_FALSE(custom_backend_consumer_conn_created_); |
| } |
| |
| // The scenario is ignored if it requests to use the system backend, but the |
| // system backend is unavailable (feature |
| // "EnablePerfettoSystemBackgroundTracing" isn't enabled). |
| TEST_F(TracingScenarioSystemBackendTest, FeatureNotEnabled_1) { |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitWithFeatures( |
| /*enabled_features=*/{features::kEnablePerfettoSystemTracing}, |
| /*disabled_features=*/{features::kEnablePerfettoSystemBackgroundTracing}); |
| InitTracing(); |
| |
| // Create a real tracing scenario. The system backend isn't used. |
| auto tracing_scenario = TracingScenario::Create( |
| ParseScenarioConfigFromText(kSystemProbeScenarioConfig), false, false, |
| false, false, &delegate); |
| |
| tracing_scenario->Enable(); |
| EXPECT_EQ(TracingScenario::State::kEnabled, |
| tracing_scenario->current_state()); |
| EXPECT_CALL(delegate, OnScenarioActive(tracing_scenario.get())).Times(0); |
| EXPECT_FALSE(base::trace_event::EmitNamedTrigger("setup_trigger")); |
| EXPECT_FALSE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| |
| tracing_scenario->Disable(); |
| EXPECT_EQ(TracingScenario::State::kDisabled, |
| tracing_scenario->current_state()); |
| // Not using the custom backend. |
| EXPECT_FALSE(custom_backend_consumer_conn_created_); |
| } |
| |
| // EnablePerfettoSystemTracing doesn't have an effect on Android. In debugging |
| // (which is always true in content_unittests) system tracing is always enabled. |
| // Disable this test on Android. |
| #if !BUILDFLAG(IS_ANDROID) |
| // The scenario is ignored if it requests to use the system backend, but the |
| // system backend is unavailable (feature "EnablePerfettoSystemTracing" isn't |
| // enabled). |
| TEST_F(TracingScenarioSystemBackendTest, FeatureNotEnabled_2) { |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitWithFeatures( |
| /*enabled_features=*/{features::kEnablePerfettoSystemBackgroundTracing}, |
| /*disabled_features=*/{features::kEnablePerfettoSystemTracing}); |
| |
| InitTracing(); |
| // Create a real tracing scenario. The system backend isn't used. |
| auto tracing_scenario = TracingScenario::Create( |
| ParseScenarioConfigFromText(kSystemProbeScenarioConfig), false, false, |
| false, false, &delegate); |
| |
| tracing_scenario->Enable(); |
| EXPECT_EQ(TracingScenario::State::kEnabled, |
| tracing_scenario->current_state()); |
| EXPECT_CALL(delegate, OnScenarioActive(tracing_scenario.get())).Times(0); |
| EXPECT_FALSE(base::trace_event::EmitNamedTrigger("setup_trigger")); |
| EXPECT_FALSE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| |
| tracing_scenario->Disable(); |
| EXPECT_EQ(TracingScenario::State::kDisabled, |
| tracing_scenario->current_state()); |
| // Not using the custom backend. |
| EXPECT_FALSE(custom_backend_consumer_conn_created_); |
| } |
| #endif |
| |
| const char* kScenarioConfigWithoutSystemBackend = R"pb( |
| scenario_name: "test_scenario" |
| setup_rules: { manual_trigger_name: "setup_trigger" } |
| start_rules: { manual_trigger_name: "start_trigger" } |
| stop_rules: { manual_trigger_name: "stop_trigger" } |
| trace_config: { data_sources: { config: { name: "mock_data_source" } } } |
| )pb"; |
| |
| // The custom backend is used on a platform that has system background tracing |
| // enabled if the scenario doesn't specify the system backend |
| TEST_F(TracingScenarioSystemBackendTest, ScenarioConfigWithoutSystemBackend) { |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitWithFeatures( |
| /*enabled_features=*/{features::kEnablePerfettoSystemTracing, |
| features::kEnablePerfettoSystemBackgroundTracing}, |
| /*disabled_features=*/{}); |
| |
| InitTracing(); |
| |
| // Create a real tracing scenario. The system backend isn't used. |
| auto tracing_scenario = TracingScenario::Create( |
| ParseScenarioConfigFromText(kScenarioConfigWithoutSystemBackend), false, |
| false, false, false, &delegate); |
| |
| tracing_scenario->Enable(); |
| EXPECT_EQ(TracingScenario::State::kEnabled, |
| tracing_scenario->current_state()); |
| EXPECT_CALL(delegate, OnScenarioActive(tracing_scenario.get())) |
| .WillOnce(testing::Return(true)); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("setup_trigger")); |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("start_trigger")); |
| |
| // The system data producer isn't used. |
| EXPECT_CALL(*system_producer_.get(), OnStartDataSource(_, _)).Times(0); |
| // Instead, the custom backend connection factory is called. |
| base::test::RunUntil([&]() { return custom_backend_consumer_conn_created_; }); |
| |
| EXPECT_TRUE(base::trace_event::EmitNamedTrigger("stop_trigger")); |
| // Not waiting until OnScenarioIdle because mojo of |tracing_service_| isn't |
| // fully set up. |
| } |
| |
| #endif // BUILDFLAG(IS_POSIX) |
| |
| } // namespace content |