|  | // Copyright 2015 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 <utility> | 
|  |  | 
|  | #include "base/base_switches.h" | 
|  | #include "base/bind.h" | 
|  | #include "base/command_line.h" | 
|  | #include "base/prefs/pref_service.h" | 
|  | #include "build/build_config.h" | 
|  | #include "chrome/app/chrome_command_ids.h" | 
|  | #include "chrome/browser/browser_process.h" | 
|  | #include "chrome/browser/tracing/background_tracing_field_trial.h" | 
|  | #include "chrome/browser/ui/browser_commands.h" | 
|  | #include "chrome/browser/ui/browser_list.h" | 
|  | #include "chrome/common/chrome_switches.h" | 
|  | #include "chrome/common/pref_names.h" | 
|  | #include "chrome/test/base/in_process_browser_test.h" | 
|  | #include "components/metrics/metrics_pref_names.h" | 
|  | #include "content/public/browser/background_tracing_config.h" | 
|  | #include "content/public/browser/background_tracing_manager.h" | 
|  | #include "content/public/browser/browser_thread.h" | 
|  | #include "content/public/test/test_utils.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class ChromeTracingDelegateBrowserTest : public InProcessBrowserTest { | 
|  | public: | 
|  | ChromeTracingDelegateBrowserTest() | 
|  | : receive_count_(0), | 
|  | started_finalizations_count_(0), | 
|  | last_on_started_finalizing_success_(false) {} | 
|  |  | 
|  | #if !defined(OS_CHROMEOS) | 
|  | void SetUpOnMainThread() override { | 
|  | PrefService* local_state = g_browser_process->local_state(); | 
|  | DCHECK(local_state); | 
|  | local_state->SetBoolean(metrics::prefs::kMetricsReportingEnabled, true); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | bool StartPreemptiveScenario( | 
|  | const base::Closure& on_upload_callback, | 
|  | content::BackgroundTracingManager::DataFiltering data_filtering) { | 
|  | on_upload_callback_ = on_upload_callback; | 
|  |  | 
|  | base::DictionaryValue dict; | 
|  |  | 
|  | dict.SetString("mode", "PREEMPTIVE_TRACING_MODE"); | 
|  | dict.SetString("category", "BENCHMARK"); | 
|  |  | 
|  | scoped_ptr<base::ListValue> rules_list(new base::ListValue()); | 
|  | { | 
|  | scoped_ptr<base::DictionaryValue> rules_dict(new base::DictionaryValue()); | 
|  | rules_dict->SetString("rule", "MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED"); | 
|  | rules_dict->SetString("trigger_name", "test"); | 
|  | rules_list->Append(std::move(rules_dict)); | 
|  | } | 
|  | dict.Set("configs", std::move(rules_list)); | 
|  |  | 
|  | scoped_ptr<content::BackgroundTracingConfig> config( | 
|  | content::BackgroundTracingConfig::FromDict(&dict)); | 
|  |  | 
|  | DCHECK(config); | 
|  | content::BackgroundTracingManager::ReceiveCallback receive_callback = | 
|  | base::Bind(&ChromeTracingDelegateBrowserTest::OnUpload, | 
|  | base::Unretained(this)); | 
|  |  | 
|  | return content::BackgroundTracingManager::GetInstance()->SetActiveScenario( | 
|  | std::move(config), receive_callback, data_filtering); | 
|  | } | 
|  |  | 
|  | void TriggerPreemptiveScenario( | 
|  | const base::Closure& on_started_finalization_callback) { | 
|  | on_started_finalization_callback_ = on_started_finalization_callback; | 
|  | trigger_handle_ = | 
|  | content::BackgroundTracingManager::GetInstance()->RegisterTriggerType( | 
|  | "test"); | 
|  |  | 
|  | content::BackgroundTracingManager::StartedFinalizingCallback | 
|  | started_finalizing_callback = | 
|  | base::Bind(&ChromeTracingDelegateBrowserTest::OnStartedFinalizing, | 
|  | base::Unretained(this)); | 
|  | content::BackgroundTracingManager::GetInstance()->TriggerNamedEvent( | 
|  | trigger_handle_, started_finalizing_callback); | 
|  | } | 
|  |  | 
|  | int get_receive_count() const { return receive_count_; } | 
|  | bool get_started_finalizations() const { | 
|  | return started_finalizations_count_; | 
|  | } | 
|  | bool get_last_started_finalization_success() const { | 
|  | return last_on_started_finalizing_success_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | void OnUpload(const scoped_refptr<base::RefCountedString>& file_contents, | 
|  | scoped_ptr<const base::DictionaryValue> metadata, | 
|  | base::Callback<void()> done_callback) { | 
|  | receive_count_ += 1; | 
|  |  | 
|  | content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | 
|  | base::Bind(done_callback)); | 
|  | content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | 
|  | base::Bind(on_upload_callback_)); | 
|  | } | 
|  |  | 
|  | void OnStartedFinalizing(bool success) { | 
|  | started_finalizations_count_++; | 
|  | last_on_started_finalizing_success_ = success; | 
|  |  | 
|  | if (!on_started_finalization_callback_.is_null()) { | 
|  | content::BrowserThread::PostTask( | 
|  | content::BrowserThread::UI, FROM_HERE, | 
|  | base::Bind(on_started_finalization_callback_)); | 
|  | } | 
|  | } | 
|  |  | 
|  | base::Closure on_upload_callback_; | 
|  | base::Closure on_started_finalization_callback_; | 
|  | int receive_count_; | 
|  | int started_finalizations_count_; | 
|  | content::BackgroundTracingManager::TriggerHandle trigger_handle_; | 
|  | bool last_on_started_finalizing_success_; | 
|  | }; | 
|  |  | 
|  | IN_PROC_BROWSER_TEST_F(ChromeTracingDelegateBrowserTest, | 
|  | BackgroundTracingTimeThrottled) { | 
|  | base::RunLoop wait_for_upload; | 
|  |  | 
|  | EXPECT_TRUE(StartPreemptiveScenario( | 
|  | wait_for_upload.QuitClosure(), | 
|  | content::BackgroundTracingManager::NO_DATA_FILTERING)); | 
|  |  | 
|  | TriggerPreemptiveScenario(base::Closure()); | 
|  |  | 
|  | wait_for_upload.Run(); | 
|  |  | 
|  | EXPECT_TRUE(get_receive_count() == 1); | 
|  |  | 
|  | PrefService* local_state = g_browser_process->local_state(); | 
|  | DCHECK(local_state); | 
|  | const base::Time last_upload_time = base::Time::FromInternalValue( | 
|  | local_state->GetInt64(prefs::kBackgroundTracingLastUpload)); | 
|  | EXPECT_FALSE(last_upload_time.is_null()); | 
|  |  | 
|  | // We should not be able to start a new reactive scenario immediately after | 
|  | // a previous one gets uploaded. | 
|  | EXPECT_FALSE(StartPreemptiveScenario( | 
|  | base::Closure(), content::BackgroundTracingManager::NO_DATA_FILTERING)); | 
|  | } | 
|  |  | 
|  | IN_PROC_BROWSER_TEST_F(ChromeTracingDelegateBrowserTest, | 
|  | BackgroundTracingThrottleTimeElapsed) { | 
|  | base::RunLoop wait_for_upload; | 
|  |  | 
|  | EXPECT_TRUE(StartPreemptiveScenario( | 
|  | wait_for_upload.QuitClosure(), | 
|  | content::BackgroundTracingManager::NO_DATA_FILTERING)); | 
|  |  | 
|  | TriggerPreemptiveScenario(base::Closure()); | 
|  |  | 
|  | wait_for_upload.Run(); | 
|  |  | 
|  | EXPECT_TRUE(get_receive_count() == 1); | 
|  |  | 
|  | PrefService* local_state = g_browser_process->local_state(); | 
|  | DCHECK(local_state); | 
|  | const base::Time last_upload_time = base::Time::FromInternalValue( | 
|  | local_state->GetInt64(prefs::kBackgroundTracingLastUpload)); | 
|  | EXPECT_FALSE(last_upload_time.is_null()); | 
|  |  | 
|  | // We move the last upload time to eight days in the past, | 
|  | // and at that point should be able to start a scenario again. | 
|  | base::Time new_upload_time = last_upload_time - base::TimeDelta::FromDays(8); | 
|  | local_state->SetInt64(prefs::kBackgroundTracingLastUpload, | 
|  | new_upload_time.ToInternalValue()); | 
|  | EXPECT_TRUE(StartPreemptiveScenario( | 
|  | base::Closure(), content::BackgroundTracingManager::NO_DATA_FILTERING)); | 
|  | } | 
|  |  | 
|  | // If we need a PII-stripped trace, any existing OTR session should block the | 
|  | // trace. | 
|  | IN_PROC_BROWSER_TEST_F(ChromeTracingDelegateBrowserTest, | 
|  | ExistingIncognitoSessionBlockingTraceStart) { | 
|  | EXPECT_TRUE(chrome::ExecuteCommand(browser(), IDC_NEW_INCOGNITO_WINDOW)); | 
|  | EXPECT_TRUE(BrowserList::IsOffTheRecordSessionActive()); | 
|  | EXPECT_FALSE(StartPreemptiveScenario( | 
|  | base::Closure(), content::BackgroundTracingManager::ANONYMIZE_DATA)); | 
|  | } | 
|  |  | 
|  | // If we need a PII-stripped trace, any new OTR session during tracing should | 
|  | // block the finalization of the trace. | 
|  | IN_PROC_BROWSER_TEST_F(ChromeTracingDelegateBrowserTest, | 
|  | NewIncognitoSessionBlockingTraceFinalization) { | 
|  | EXPECT_TRUE(StartPreemptiveScenario( | 
|  | base::Closure(), content::BackgroundTracingManager::ANONYMIZE_DATA)); | 
|  |  | 
|  | EXPECT_TRUE(chrome::ExecuteCommand(browser(), IDC_NEW_INCOGNITO_WINDOW)); | 
|  | EXPECT_TRUE(BrowserList::IsOffTheRecordSessionActive()); | 
|  |  | 
|  | base::RunLoop wait_for_finalization_start; | 
|  | TriggerPreemptiveScenario(wait_for_finalization_start.QuitClosure()); | 
|  | wait_for_finalization_start.Run(); | 
|  |  | 
|  | EXPECT_TRUE(get_started_finalizations() == 1); | 
|  | EXPECT_FALSE(get_last_started_finalization_success()); | 
|  | } | 
|  |  | 
|  | class ChromeTracingDelegateBrowserTestOnStartup | 
|  | : public ChromeTracingDelegateBrowserTest { | 
|  | protected: | 
|  | ChromeTracingDelegateBrowserTestOnStartup() {} | 
|  |  | 
|  | static void FieldTrialConfigTextFilter(std::string* config_text) { | 
|  | ASSERT_TRUE(config_text); | 
|  | // We need to replace the config JSON with the full one here, as we can't | 
|  | // pass JSON through the fieldtrial switch parsing. | 
|  | if (*config_text == "default_config_for_testing") { | 
|  | *config_text = | 
|  | "{\"mode\":\"PREEMPTIVE_TRACING_MODE\", \"category\": " | 
|  | "\"BENCHMARK\",\"configs\": [{\"rule\": " | 
|  | "\"MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED\",\"trigger_name\":" | 
|  | "\"test\"}]}"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void SetUpCommandLine(base::CommandLine* command_line) override { | 
|  | base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( | 
|  | switches::kForceFieldTrials, "BackgroundTracing/TestGroup/"); | 
|  | base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( | 
|  | switches::kForceFieldTrialParams, | 
|  | "BackgroundTracing.TestGroup:config/default_config_for_testing"); | 
|  |  | 
|  | tracing::SetConfigTextFilterForTesting(&FieldTrialConfigTextFilter); | 
|  | } | 
|  | }; | 
|  |  | 
|  | #if !defined(OS_CHROMEOS) | 
|  | IN_PROC_BROWSER_TEST_F(ChromeTracingDelegateBrowserTestOnStartup, | 
|  | PRE_ScenarioSetFromFieldtrial) { | 
|  | // At this point the metrics pref is not set. | 
|  | EXPECT_FALSE( | 
|  | content::BackgroundTracingManager::GetInstance()->HasActiveScenario()); | 
|  | } | 
|  | #endif // OS_CHROMEOS | 
|  |  | 
|  | IN_PROC_BROWSER_TEST_F(ChromeTracingDelegateBrowserTestOnStartup, | 
|  | ScenarioSetFromFieldtrial) { | 
|  | // We should reach this point without crashing. | 
|  | EXPECT_TRUE( | 
|  | content::BackgroundTracingManager::GetInstance()->HasActiveScenario()); | 
|  | } | 
|  |  | 
|  | #if !defined(OS_CHROMEOS) | 
|  | IN_PROC_BROWSER_TEST_F(ChromeTracingDelegateBrowserTestOnStartup, | 
|  | PRE_PRE_StartupTracingThrottle) { | 
|  | // At this point the metrics pref is not set. | 
|  | EXPECT_FALSE( | 
|  | content::BackgroundTracingManager::GetInstance()->HasActiveScenario()); | 
|  | } | 
|  | #endif // OS_CHROMEOS | 
|  |  | 
|  | IN_PROC_BROWSER_TEST_F(ChromeTracingDelegateBrowserTestOnStartup, | 
|  | PRE_StartupTracingThrottle) { | 
|  | EXPECT_TRUE( | 
|  | content::BackgroundTracingManager::GetInstance()->HasActiveScenario()); | 
|  |  | 
|  | // Simulate a trace upload. | 
|  | PrefService* local_state = g_browser_process->local_state(); | 
|  | DCHECK(local_state); | 
|  | local_state->SetInt64(prefs::kBackgroundTracingLastUpload, | 
|  | base::Time::Now().ToInternalValue()); | 
|  | } | 
|  |  | 
|  | IN_PROC_BROWSER_TEST_F(ChromeTracingDelegateBrowserTestOnStartup, | 
|  | StartupTracingThrottle) { | 
|  | // The startup scenario should *not* be started, since not enough | 
|  | // time has elapsed since the last upload (set in the PRE_ above). | 
|  | EXPECT_FALSE( | 
|  | content::BackgroundTracingManager::GetInstance()->HasActiveScenario()); | 
|  | } | 
|  |  | 
|  | }  // namespace |