1 // Copyright 2014 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.
5 #include "components/metrics/metrics_service.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/metrics/statistics_recorder.h"
13 #include "base/prefs/testing_pref_service.h"
14 #include "base/threading/platform_thread.h"
15 #include "components/metrics/client_info.h"
16 #include "components/metrics/compression_utils.h"
17 #include "components/metrics/metrics_hashes.h"
18 #include "components/metrics/metrics_log.h"
19 #include "components/metrics/metrics_pref_names.h"
20 #include "components/metrics/metrics_state_manager.h"
21 #include "components/metrics/test_metrics_service_client.h"
22 #include "components/variations/metrics_util.h"
23 #include "testing/gtest/include/gtest/gtest.h"
29 void StoreNoClientInfoBackup(const ClientInfo
& /* client_info */) {
32 scoped_ptr
<ClientInfo
> ReturnNoBackup() {
33 return scoped_ptr
<ClientInfo
>();
36 class TestMetricsProvider
: public MetricsProvider
{
38 explicit TestMetricsProvider(bool has_stability_metrics
) :
39 has_stability_metrics_(has_stability_metrics
),
40 provide_stability_metrics_called_(false) {
43 bool HasStabilityMetrics() override
{ return has_stability_metrics_
; }
44 void ProvideStabilityMetrics(
45 SystemProfileProto
* system_profile_proto
) override
{
46 UMA_STABILITY_HISTOGRAM_ENUMERATION("TestMetricsProvider.Metric", 1, 2);
47 provide_stability_metrics_called_
= true;
50 bool provide_stability_metrics_called() const {
51 return provide_stability_metrics_called_
;
55 bool has_stability_metrics_
;
56 bool provide_stability_metrics_called_
;
58 DISALLOW_COPY_AND_ASSIGN(TestMetricsProvider
);
61 class TestMetricsService
: public MetricsService
{
63 TestMetricsService(MetricsStateManager
* state_manager
,
64 MetricsServiceClient
* client
,
65 PrefService
* local_state
)
66 : MetricsService(state_manager
, client
, local_state
) {}
67 ~TestMetricsService() override
{}
69 using MetricsService::log_manager
;
72 DISALLOW_COPY_AND_ASSIGN(TestMetricsService
);
75 class TestMetricsLog
: public MetricsLog
{
77 TestMetricsLog(const std::string
& client_id
,
79 MetricsServiceClient
* client
,
80 PrefService
* local_state
)
81 : MetricsLog(client_id
,
83 MetricsLog::ONGOING_LOG
,
87 ~TestMetricsLog() override
{}
90 DISALLOW_COPY_AND_ASSIGN(TestMetricsLog
);
93 class MetricsServiceTest
: public testing::Test
{
95 MetricsServiceTest() : is_metrics_reporting_enabled_(false) {
96 MetricsService::RegisterPrefs(testing_local_state_
.registry());
97 metrics_state_manager_
= MetricsStateManager::Create(
99 base::Bind(&MetricsServiceTest::is_metrics_reporting_enabled
,
100 base::Unretained(this)),
101 base::Bind(&StoreNoClientInfoBackup
),
102 base::Bind(&ReturnNoBackup
));
105 ~MetricsServiceTest() override
{
106 MetricsService::SetExecutionPhase(MetricsService::UNINITIALIZED_PHASE
,
110 MetricsStateManager
* GetMetricsStateManager() {
111 return metrics_state_manager_
.get();
114 PrefService
* GetLocalState() { return &testing_local_state_
; }
116 // Sets metrics reporting as enabled for testing.
117 void EnableMetricsReporting() {
118 is_metrics_reporting_enabled_
= true;
121 // Waits until base::TimeTicks::Now() no longer equals |value|. This should
122 // take between 1-15ms per the documented resolution of base::TimeTicks.
123 void WaitUntilTimeChanges(const base::TimeTicks
& value
) {
124 while (base::TimeTicks::Now() == value
) {
125 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
129 // Returns true if there is a synthetic trial in the given vector that matches
130 // the given trial name and trial group; returns false otherwise.
131 bool HasSyntheticTrial(
132 const std::vector
<variations::ActiveGroupId
>& synthetic_trials
,
133 const std::string
& trial_name
,
134 const std::string
& trial_group
) {
135 uint32 trial_name_hash
= HashName(trial_name
);
136 uint32 trial_group_hash
= HashName(trial_group
);
137 for (const variations::ActiveGroupId
& trial
: synthetic_trials
) {
138 if (trial
.name
== trial_name_hash
&& trial
.group
== trial_group_hash
)
144 // Finds a histogram with the specified |name_hash| in |histograms|.
145 const base::HistogramBase
* FindHistogram(
146 const base::StatisticsRecorder::Histograms
& histograms
,
148 for (const base::HistogramBase
* histogram
: histograms
) {
149 if (name_hash
== HashMetricName(histogram
->histogram_name()))
155 // Checks whether |uma_log| contains any histograms that are not flagged
156 // with kUmaStabilityHistogramFlag. Stability logs should only contain such
158 void CheckForNonStabilityHistograms(
159 const ChromeUserMetricsExtension
& uma_log
) {
160 const int kStabilityFlags
= base::HistogramBase::kUmaStabilityHistogramFlag
;
161 base::StatisticsRecorder::Histograms histograms
;
162 base::StatisticsRecorder::GetHistograms(&histograms
);
163 for (int i
= 0; i
< uma_log
.histogram_event_size(); ++i
) {
164 const uint64 hash
= uma_log
.histogram_event(i
).name_hash();
166 const base::HistogramBase
* histogram
= FindHistogram(histograms
, hash
);
167 EXPECT_TRUE(histogram
) << hash
;
169 EXPECT_EQ(kStabilityFlags
, histogram
->flags() & kStabilityFlags
) << hash
;
174 bool is_metrics_reporting_enabled() const {
175 return is_metrics_reporting_enabled_
;
178 bool is_metrics_reporting_enabled_
;
179 TestingPrefServiceSimple testing_local_state_
;
180 scoped_ptr
<MetricsStateManager
> metrics_state_manager_
;
181 base::MessageLoop message_loop
;
183 DISALLOW_COPY_AND_ASSIGN(MetricsServiceTest
);
188 TEST_F(MetricsServiceTest
, InitialStabilityLogAfterCleanShutDown
) {
189 EnableMetricsReporting();
190 GetLocalState()->SetBoolean(prefs::kStabilityExitedCleanly
, true);
192 TestMetricsServiceClient client
;
193 TestMetricsService
service(
194 GetMetricsStateManager(), &client
, GetLocalState());
196 TestMetricsProvider
* test_provider
= new TestMetricsProvider(false);
197 service
.RegisterMetricsProvider(scoped_ptr
<MetricsProvider
>(test_provider
));
199 service
.InitializeMetricsRecordingState();
200 // No initial stability log should be generated.
201 EXPECT_FALSE(service
.log_manager()->has_unsent_logs());
202 EXPECT_FALSE(service
.log_manager()->has_staged_log());
204 // The test provider should not have been called upon to provide stability
206 EXPECT_FALSE(test_provider
->provide_stability_metrics_called());
209 TEST_F(MetricsServiceTest
, InitialStabilityLogAtProviderRequest
) {
210 EnableMetricsReporting();
212 // Save an existing system profile to prefs, to correspond to what would be
213 // saved from a previous session.
214 TestMetricsServiceClient client
;
215 TestMetricsLog
log("client", 1, &client
, GetLocalState());
216 log
.RecordEnvironment(std::vector
<MetricsProvider
*>(),
217 std::vector
<variations::ActiveGroupId
>(),
220 // Record stability build time and version from previous session, so that
221 // stability metrics (including exited cleanly flag) won't be cleared.
222 GetLocalState()->SetInt64(prefs::kStabilityStatsBuildTime
,
223 MetricsLog::GetBuildTime());
224 GetLocalState()->SetString(prefs::kStabilityStatsVersion
,
225 client
.GetVersionString());
227 // Set the clean exit flag, as that will otherwise cause a stabilty
228 // log to be produced, irrespective provider requests.
229 GetLocalState()->SetBoolean(prefs::kStabilityExitedCleanly
, true);
231 TestMetricsService
service(
232 GetMetricsStateManager(), &client
, GetLocalState());
233 // Add a metrics provider that requests a stability log.
234 TestMetricsProvider
* test_provider
= new TestMetricsProvider(true);
235 service
.RegisterMetricsProvider(
236 scoped_ptr
<MetricsProvider
>(test_provider
));
238 service
.InitializeMetricsRecordingState();
240 // The initial stability log should be generated and persisted in unsent logs.
241 MetricsLogManager
* log_manager
= service
.log_manager();
242 EXPECT_TRUE(log_manager
->has_unsent_logs());
243 EXPECT_FALSE(log_manager
->has_staged_log());
245 // The test provider should have been called upon to provide stability
247 EXPECT_TRUE(test_provider
->provide_stability_metrics_called());
249 // Stage the log and retrieve it.
250 log_manager
->StageNextLogForUpload();
251 EXPECT_TRUE(log_manager
->has_staged_log());
253 std::string uncompressed_log
;
254 EXPECT_TRUE(GzipUncompress(log_manager
->staged_log(), &uncompressed_log
));
256 ChromeUserMetricsExtension uma_log
;
257 EXPECT_TRUE(uma_log
.ParseFromString(uncompressed_log
));
259 EXPECT_TRUE(uma_log
.has_client_id());
260 EXPECT_TRUE(uma_log
.has_session_id());
261 EXPECT_TRUE(uma_log
.has_system_profile());
262 EXPECT_EQ(0, uma_log
.user_action_event_size());
263 EXPECT_EQ(0, uma_log
.omnibox_event_size());
264 EXPECT_EQ(0, uma_log
.profiler_event_size());
265 EXPECT_EQ(0, uma_log
.perf_data_size());
266 CheckForNonStabilityHistograms(uma_log
);
268 // As there wasn't an unclean shutdown, this log has zero crash count.
269 EXPECT_EQ(0, uma_log
.system_profile().stability().crash_count());
272 TEST_F(MetricsServiceTest
, InitialStabilityLogAfterCrash
) {
273 EnableMetricsReporting();
274 GetLocalState()->ClearPref(prefs::kStabilityExitedCleanly
);
276 // Set up prefs to simulate restarting after a crash.
278 // Save an existing system profile to prefs, to correspond to what would be
279 // saved from a previous session.
280 TestMetricsServiceClient client
;
281 TestMetricsLog
log("client", 1, &client
, GetLocalState());
282 log
.RecordEnvironment(std::vector
<MetricsProvider
*>(),
283 std::vector
<variations::ActiveGroupId
>(),
286 // Record stability build time and version from previous session, so that
287 // stability metrics (including exited cleanly flag) won't be cleared.
288 GetLocalState()->SetInt64(prefs::kStabilityStatsBuildTime
,
289 MetricsLog::GetBuildTime());
290 GetLocalState()->SetString(prefs::kStabilityStatsVersion
,
291 client
.GetVersionString());
293 GetLocalState()->SetBoolean(prefs::kStabilityExitedCleanly
, false);
295 TestMetricsService
service(
296 GetMetricsStateManager(), &client
, GetLocalState());
297 service
.InitializeMetricsRecordingState();
299 // The initial stability log should be generated and persisted in unsent logs.
300 MetricsLogManager
* log_manager
= service
.log_manager();
301 EXPECT_TRUE(log_manager
->has_unsent_logs());
302 EXPECT_FALSE(log_manager
->has_staged_log());
304 // Stage the log and retrieve it.
305 log_manager
->StageNextLogForUpload();
306 EXPECT_TRUE(log_manager
->has_staged_log());
308 std::string uncompressed_log
;
309 EXPECT_TRUE(GzipUncompress(log_manager
->staged_log(), &uncompressed_log
));
311 ChromeUserMetricsExtension uma_log
;
312 EXPECT_TRUE(uma_log
.ParseFromString(uncompressed_log
));
314 EXPECT_TRUE(uma_log
.has_client_id());
315 EXPECT_TRUE(uma_log
.has_session_id());
316 EXPECT_TRUE(uma_log
.has_system_profile());
317 EXPECT_EQ(0, uma_log
.user_action_event_size());
318 EXPECT_EQ(0, uma_log
.omnibox_event_size());
319 EXPECT_EQ(0, uma_log
.profiler_event_size());
320 EXPECT_EQ(0, uma_log
.perf_data_size());
321 CheckForNonStabilityHistograms(uma_log
);
323 EXPECT_EQ(1, uma_log
.system_profile().stability().crash_count());
326 TEST_F(MetricsServiceTest
, RegisterSyntheticTrial
) {
327 TestMetricsServiceClient client
;
328 MetricsService
service(GetMetricsStateManager(), &client
, GetLocalState());
330 // Add two synthetic trials and confirm that they show up in the list.
331 SyntheticTrialGroup
trial1(HashName("TestTrial1"), HashName("Group1"));
332 service
.RegisterSyntheticFieldTrial(trial1
);
334 SyntheticTrialGroup
trial2(HashName("TestTrial2"), HashName("Group2"));
335 service
.RegisterSyntheticFieldTrial(trial2
);
336 // Ensure that time has advanced by at least a tick before proceeding.
337 WaitUntilTimeChanges(base::TimeTicks::Now());
339 service
.log_manager_
.BeginLoggingWithLog(scoped_ptr
<MetricsLog
>(
340 new MetricsLog("clientID",
342 MetricsLog::INITIAL_STABILITY_LOG
,
345 // Save the time when the log was started (it's okay for this to be greater
346 // than the time recorded by the above call since it's used to ensure the
348 const base::TimeTicks begin_log_time
= base::TimeTicks::Now();
350 std::vector
<variations::ActiveGroupId
> synthetic_trials
;
351 service
.GetCurrentSyntheticFieldTrials(&synthetic_trials
);
352 EXPECT_EQ(2U, synthetic_trials
.size());
353 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials
, "TestTrial1", "Group1"));
354 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials
, "TestTrial2", "Group2"));
356 // Ensure that time has advanced by at least a tick before proceeding.
357 WaitUntilTimeChanges(begin_log_time
);
359 // Change the group for the first trial after the log started.
360 SyntheticTrialGroup
trial3(HashName("TestTrial1"), HashName("Group2"));
361 service
.RegisterSyntheticFieldTrial(trial3
);
362 service
.GetCurrentSyntheticFieldTrials(&synthetic_trials
);
363 EXPECT_EQ(1U, synthetic_trials
.size());
364 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials
, "TestTrial2", "Group2"));
366 // Add a new trial after the log started and confirm that it doesn't show up.
367 SyntheticTrialGroup
trial4(HashName("TestTrial3"), HashName("Group3"));
368 service
.RegisterSyntheticFieldTrial(trial4
);
369 service
.GetCurrentSyntheticFieldTrials(&synthetic_trials
);
370 EXPECT_EQ(1U, synthetic_trials
.size());
371 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials
, "TestTrial2", "Group2"));
373 // Ensure that time has advanced by at least a tick before proceeding.
374 WaitUntilTimeChanges(base::TimeTicks::Now());
376 // Start a new log and ensure all three trials appear in it.
377 service
.log_manager_
.FinishCurrentLog();
378 service
.log_manager_
.BeginLoggingWithLog(
379 scoped_ptr
<MetricsLog
>(new MetricsLog(
380 "clientID", 1, MetricsLog::ONGOING_LOG
, &client
, GetLocalState())));
381 service
.GetCurrentSyntheticFieldTrials(&synthetic_trials
);
382 EXPECT_EQ(3U, synthetic_trials
.size());
383 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials
, "TestTrial1", "Group2"));
384 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials
, "TestTrial2", "Group2"));
385 EXPECT_TRUE(HasSyntheticTrial(synthetic_trials
, "TestTrial3", "Group3"));
386 service
.log_manager_
.FinishCurrentLog();
389 } // namespace metrics