1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/metrics/metrics_service.h" 6 7 #include <ctype.h> 8 #include <string> 9 10 #include "base/command_line.h" 11 #include "base/message_loop/message_loop.h" 12 #include "chrome/common/chrome_switches.h" 13 #include "chrome/common/pref_names.h" 14 #include "chrome/test/base/scoped_testing_local_state.h" 15 #include "chrome/test/base/testing_browser_process.h" 16 #include "components/variations/metrics_util.h" 17 #include "content/public/common/process_type.h" 18 #include "content/public/common/webplugininfo.h" 19 #include "content/public/test/test_browser_thread.h" 20 #include "testing/gtest/include/gtest/gtest.h" 21 #include "ui/gfx/size.h" 22 23 #if defined(OS_CHROMEOS) 24 #include "chromeos/dbus/fake_bluetooth_adapter_client.h" 25 #include "chromeos/dbus/fake_bluetooth_device_client.h" 26 #include "chromeos/dbus/fake_bluetooth_input_client.h" 27 #include "chromeos/dbus/fake_dbus_thread_manager.h" 28 #endif // OS_CHROMEOS 29 30 namespace { 31 32 class TestMetricsService : public MetricsService { 33 public: 34 TestMetricsService() {} 35 virtual ~TestMetricsService() {} 36 37 MetricsLogManager* log_manager() { 38 return &log_manager_; 39 } 40 41 private: 42 DISALLOW_COPY_AND_ASSIGN(TestMetricsService); 43 }; 44 45 class TestMetricsLog : public MetricsLog { 46 public: 47 TestMetricsLog(const std::string& client_id, int session_id) 48 : MetricsLog(client_id, session_id) {} 49 virtual ~TestMetricsLog() {} 50 51 private: 52 virtual gfx::Size GetScreenSize() const OVERRIDE { 53 return gfx::Size(1024, 768); 54 } 55 56 virtual float GetScreenDeviceScaleFactor() const OVERRIDE { 57 return 1.0f; 58 } 59 60 virtual int GetScreenCount() const OVERRIDE { 61 return 1; 62 } 63 64 DISALLOW_COPY_AND_ASSIGN(TestMetricsLog); 65 }; 66 67 class MetricsServiceTest : public testing::Test { 68 public: 69 MetricsServiceTest() 70 : ui_thread_(content::BrowserThread::UI, &message_loop_), 71 testing_local_state_(TestingBrowserProcess::GetGlobal()) { 72 #if defined(OS_CHROMEOS) 73 chromeos::FakeDBusThreadManager* fake_dbus_thread_manager = 74 new chromeos::FakeDBusThreadManager; 75 fake_dbus_thread_manager->SetBluetoothAdapterClient( 76 scoped_ptr<chromeos::BluetoothAdapterClient>( 77 new chromeos::FakeBluetoothAdapterClient)); 78 fake_dbus_thread_manager->SetBluetoothDeviceClient( 79 scoped_ptr<chromeos::BluetoothDeviceClient>( 80 new chromeos::FakeBluetoothDeviceClient)); 81 fake_dbus_thread_manager->SetBluetoothInputClient( 82 scoped_ptr<chromeos::BluetoothInputClient>( 83 new chromeos::FakeBluetoothInputClient)); 84 chromeos::DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager); 85 #endif // OS_CHROMEOS 86 } 87 88 virtual ~MetricsServiceTest() { 89 #if defined(OS_CHROMEOS) 90 chromeos::DBusThreadManager::Shutdown(); 91 #endif // OS_CHROMEOS 92 MetricsService::SetExecutionPhase(MetricsService::UNINITIALIZED_PHASE); 93 } 94 95 PrefService* GetLocalState() { 96 return testing_local_state_.Get(); 97 } 98 99 // Returns true if there is a synthetic trial in the given vector that matches 100 // the given trial name and trial group; returns false otherwise. 101 bool HasSyntheticTrial( 102 const std::vector<chrome_variations::ActiveGroupId>& synthetic_trials, 103 const std::string& trial_name, 104 const std::string& trial_group) { 105 uint32 trial_name_hash = metrics::HashName(trial_name); 106 uint32 trial_group_hash = metrics::HashName(trial_group); 107 for (std::vector<chrome_variations::ActiveGroupId>::const_iterator it = 108 synthetic_trials.begin(); 109 it != synthetic_trials.end(); ++it) { 110 if ((*it).name == trial_name_hash && (*it).group == trial_group_hash) 111 return true; 112 } 113 return false; 114 } 115 116 private: 117 base::MessageLoopForUI message_loop_; 118 content::TestBrowserThread ui_thread_; 119 ScopedTestingLocalState testing_local_state_; 120 121 DISALLOW_COPY_AND_ASSIGN(MetricsServiceTest); 122 }; 123 124 } // namespace 125 126 // Ensure the ClientId is formatted as expected. 127 TEST_F(MetricsServiceTest, ClientIdCorrectlyFormatted) { 128 std::string clientid = MetricsService::GenerateClientID(); 129 EXPECT_EQ(36U, clientid.length()); 130 131 for (size_t i = 0; i < clientid.length(); ++i) { 132 char current = clientid[i]; 133 if (i == 8 || i == 13 || i == 18 || i == 23) 134 EXPECT_EQ('-', current); 135 else 136 EXPECT_TRUE(isxdigit(current)); 137 } 138 } 139 140 TEST_F(MetricsServiceTest, IsPluginProcess) { 141 EXPECT_TRUE( 142 MetricsService::IsPluginProcess(content::PROCESS_TYPE_PLUGIN)); 143 EXPECT_TRUE( 144 MetricsService::IsPluginProcess(content::PROCESS_TYPE_PPAPI_PLUGIN)); 145 EXPECT_FALSE( 146 MetricsService::IsPluginProcess(content::PROCESS_TYPE_GPU)); 147 } 148 149 TEST_F(MetricsServiceTest, LowEntropySource0NotReset) { 150 MetricsService service; 151 152 // Get the low entropy source once, to initialize it. 153 service.GetLowEntropySource(); 154 155 // Now, set it to 0 and ensure it doesn't get reset. 156 service.low_entropy_source_ = 0; 157 EXPECT_EQ(0, service.GetLowEntropySource()); 158 // Call it another time, just to make sure. 159 EXPECT_EQ(0, service.GetLowEntropySource()); 160 } 161 162 TEST_F(MetricsServiceTest, PermutedEntropyCacheClearedWhenLowEntropyReset) { 163 const PrefService::Preference* low_entropy_pref = 164 GetLocalState()->FindPreference(prefs::kMetricsLowEntropySource); 165 const char* kCachePrefName = prefs::kMetricsPermutedEntropyCache; 166 int low_entropy_value = -1; 167 168 // First, generate an initial low entropy source value. 169 { 170 EXPECT_TRUE(low_entropy_pref->IsDefaultValue()); 171 172 MetricsService::SetExecutionPhase(MetricsService::UNINITIALIZED_PHASE); 173 MetricsService service; 174 service.GetLowEntropySource(); 175 176 EXPECT_FALSE(low_entropy_pref->IsDefaultValue()); 177 EXPECT_TRUE(low_entropy_pref->GetValue()->GetAsInteger(&low_entropy_value)); 178 } 179 180 // Now, set a dummy value in the permuted entropy cache pref and verify that 181 // another call to GetLowEntropySource() doesn't clobber it when 182 // --reset-variation-state wasn't specified. 183 { 184 GetLocalState()->SetString(kCachePrefName, "test"); 185 186 MetricsService::SetExecutionPhase(MetricsService::UNINITIALIZED_PHASE); 187 MetricsService service; 188 service.GetLowEntropySource(); 189 190 EXPECT_EQ("test", GetLocalState()->GetString(kCachePrefName)); 191 EXPECT_EQ(low_entropy_value, 192 GetLocalState()->GetInteger(prefs::kMetricsLowEntropySource)); 193 } 194 195 // Verify that the cache does get reset if --reset-variations-state is passed. 196 { 197 CommandLine::ForCurrentProcess()->AppendSwitch( 198 switches::kResetVariationState); 199 200 MetricsService::SetExecutionPhase(MetricsService::UNINITIALIZED_PHASE); 201 MetricsService service; 202 service.GetLowEntropySource(); 203 204 EXPECT_TRUE(GetLocalState()->GetString(kCachePrefName).empty()); 205 } 206 } 207 208 TEST_F(MetricsServiceTest, InitialStabilityLogAfterCleanShutDown) { 209 base::FieldTrialList field_trial_list(NULL); 210 base::FieldTrialList::CreateFieldTrial("UMAStability", "SeparateLog"); 211 212 GetLocalState()->SetBoolean(prefs::kStabilityExitedCleanly, true); 213 214 TestMetricsService service; 215 service.InitializeMetricsRecordingState(MetricsService::REPORTING_ENABLED); 216 // No initial stability log should be generated. 217 EXPECT_FALSE(service.log_manager()->has_unsent_logs()); 218 EXPECT_FALSE(service.log_manager()->has_staged_log()); 219 } 220 221 TEST_F(MetricsServiceTest, InitialStabilityLogAfterCrash) { 222 base::FieldTrialList field_trial_list(NULL); 223 base::FieldTrialList::CreateFieldTrial("UMAStability", "SeparateLog"); 224 225 GetLocalState()->ClearPref(prefs::kStabilityExitedCleanly); 226 227 // Set up prefs to simulate restarting after a crash. 228 229 // Save an existing system profile to prefs, to correspond to what would be 230 // saved from a previous session. 231 TestMetricsLog log("client", 1); 232 log.RecordEnvironment(std::vector<content::WebPluginInfo>(), 233 GoogleUpdateMetrics(), 234 std::vector<chrome_variations::ActiveGroupId>()); 235 236 // Record stability build time and version from previous session, so that 237 // stability metrics (including exited cleanly flag) won't be cleared. 238 GetLocalState()->SetInt64(prefs::kStabilityStatsBuildTime, 239 MetricsLog::GetBuildTime()); 240 GetLocalState()->SetString(prefs::kStabilityStatsVersion, 241 MetricsLog::GetVersionString()); 242 243 GetLocalState()->SetBoolean(prefs::kStabilityExitedCleanly, false); 244 245 TestMetricsService service; 246 service.InitializeMetricsRecordingState(MetricsService::REPORTING_ENABLED); 247 248 // The initial stability log should be generated and persisted in unsent logs. 249 MetricsLogManager* log_manager = service.log_manager(); 250 EXPECT_TRUE(log_manager->has_unsent_logs()); 251 EXPECT_FALSE(log_manager->has_staged_log()); 252 253 // Stage the log and retrieve it. 254 log_manager->StageNextLogForUpload(); 255 EXPECT_TRUE(log_manager->has_staged_log()); 256 257 metrics::ChromeUserMetricsExtension uma_log; 258 EXPECT_TRUE(uma_log.ParseFromString(log_manager->staged_log_text())); 259 260 EXPECT_TRUE(uma_log.has_client_id()); 261 EXPECT_TRUE(uma_log.has_session_id()); 262 EXPECT_TRUE(uma_log.has_system_profile()); 263 EXPECT_EQ(0, uma_log.user_action_event_size()); 264 EXPECT_EQ(0, uma_log.omnibox_event_size()); 265 EXPECT_EQ(0, uma_log.histogram_event_size()); 266 EXPECT_EQ(0, uma_log.profiler_event_size()); 267 EXPECT_EQ(0, uma_log.perf_data_size()); 268 269 EXPECT_EQ(1, uma_log.system_profile().stability().crash_count()); 270 } 271 272 // Crashes on at least Mac and Linux. http://crbug.com/320433 273 TEST_F(MetricsServiceTest, DISABLED_RegisterSyntheticTrial) { 274 MetricsService service; 275 276 // Add two synthetic trials and confirm that they show up in the list. 277 SyntheticTrialGroup trial1(metrics::HashName("TestTrial1"), 278 metrics::HashName("Group1"), 279 base::TimeTicks::Now()); 280 service.RegisterSyntheticFieldTrial(trial1); 281 282 SyntheticTrialGroup trial2(metrics::HashName("TestTrial2"), 283 metrics::HashName("Group2"), 284 base::TimeTicks::Now()); 285 service.RegisterSyntheticFieldTrial(trial2); 286 287 service.log_manager_.BeginLoggingWithLog(new MetricsLog("clientID", 1), 288 MetricsLog::INITIAL_LOG); 289 290 std::vector<chrome_variations::ActiveGroupId> synthetic_trials; 291 service.GetCurrentSyntheticFieldTrials(&synthetic_trials); 292 EXPECT_EQ(2U, synthetic_trials.size()); 293 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "Group1")); 294 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2")); 295 296 // Change the group for the first trial after the log started. 297 SyntheticTrialGroup trial3(metrics::HashName("TestTrial1"), 298 metrics::HashName("Group2"), 299 base::TimeTicks::Now()); 300 service.RegisterSyntheticFieldTrial(trial3); 301 service.GetCurrentSyntheticFieldTrials(&synthetic_trials); 302 EXPECT_EQ(1U, synthetic_trials.size()); 303 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2")); 304 305 // Add a new trial after the log started and confirm that it doesn't show up. 306 SyntheticTrialGroup trial4(metrics::HashName("TestTrial3"), 307 metrics::HashName("Group3"), 308 base::TimeTicks::Now()); 309 service.RegisterSyntheticFieldTrial(trial4); 310 service.GetCurrentSyntheticFieldTrials(&synthetic_trials); 311 EXPECT_EQ(1U, synthetic_trials.size()); 312 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2")); 313 314 // Start a new log. 315 service.log_manager_.FinishCurrentLog(); 316 service.log_manager_.BeginLoggingWithLog(new MetricsLog("clientID", 1), 317 MetricsLog::ONGOING_LOG); 318 service.GetCurrentSyntheticFieldTrials(&synthetic_trials); 319 EXPECT_EQ(3U, synthetic_trials.size()); 320 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial1", "Group2")); 321 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial2", "Group2")); 322 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials, "TestTrial3", "Group3")); 323 service.log_manager_.FinishCurrentLog(); 324 } 325