1 // Copyright 2018 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/perf/metric_collector.h"
6
7 #include <stdint.h>
8
9 #include <memory>
10 #include <string>
11 #include <utility>
12 #include <vector>
13
14 #include "base/macros.h"
15 #include "base/memory/scoped_refptr.h"
16 #include "base/task/post_task.h"
17 #include "base/test/bind.h"
18 #include "base/test/metrics/histogram_tester.h"
19 #include "content/public/browser/browser_task_traits.h"
20 #include "content/public/test/browser_task_environment.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 #include "third_party/metrics_proto/sampled_profile.pb.h"
23 #include "third_party/protobuf/src/google/protobuf/io/coded_stream.h"
24 #include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
25 #include "third_party/protobuf/src/google/protobuf/wire_format_lite.h"
26
27 namespace metrics {
28
29 namespace internal {
30
31 namespace {
32
33 // Returns an example PerfDataProto. The contents don't have to make sense. They
34 // just need to constitute a semantically valid protobuf.
35 // |proto| is an output parameter that will contain the created protobuf.
GetExamplePerfDataProto()36 PerfDataProto GetExamplePerfDataProto() {
37 PerfDataProto proto;
38 proto.set_timestamp_sec(1435604013); // Time since epoch in seconds.
39
40 PerfDataProto_PerfFileAttr* file_attr = proto.add_file_attrs();
41 file_attr->add_ids(61);
42 file_attr->add_ids(62);
43 file_attr->add_ids(63);
44
45 PerfDataProto_PerfEventAttr* attr = file_attr->mutable_attr();
46 attr->set_type(1);
47 attr->set_size(2);
48 attr->set_config(3);
49 attr->set_sample_period(4);
50 attr->set_sample_freq(5);
51
52 PerfDataProto_PerfEventStats* stats = proto.mutable_stats();
53 stats->set_num_events_read(100);
54 stats->set_num_sample_events(200);
55 stats->set_num_mmap_events(300);
56 stats->set_num_fork_events(400);
57 stats->set_num_exit_events(500);
58
59 return proto;
60 }
61
62 // Creates a serialized data stream containing a string with a field tag number.
SerializeStringFieldWithTag(int field,const std::string & value)63 std::string SerializeStringFieldWithTag(int field, const std::string& value) {
64 std::string result;
65 google::protobuf::io::StringOutputStream string_stream(&result);
66 google::protobuf::io::CodedOutputStream output(&string_stream);
67
68 using google::protobuf::internal::WireFormatLite;
69 WireFormatLite::WriteTag(field, WireFormatLite::WIRETYPE_LENGTH_DELIMITED,
70 &output);
71 output.WriteVarint32(value.size());
72 output.WriteString(value);
73
74 return result;
75 }
76
77 // Allows access to some private methods for testing.
78 class TestMetricCollector : public MetricCollector {
79 public:
TestMetricCollector()80 TestMetricCollector() : TestMetricCollector(CollectionParams()) {}
TestMetricCollector(const CollectionParams & collection_params)81 explicit TestMetricCollector(const CollectionParams& collection_params)
82 : MetricCollector("Test", collection_params) {}
83
ToolName() const84 const char* ToolName() const override { return "Test"; }
GetWeakPtr()85 base::WeakPtr<MetricCollector> GetWeakPtr() override {
86 return weak_factory_.GetWeakPtr();
87 }
88
CollectProfile(std::unique_ptr<SampledProfile> sampled_profile)89 void CollectProfile(
90 std::unique_ptr<SampledProfile> sampled_profile) override {
91 PerfDataProto perf_data_proto = GetExamplePerfDataProto();
92 SaveSerializedPerfProto(std::move(sampled_profile),
93 perf_data_proto.SerializeAsString());
94 }
95
96 using MetricCollector::collection_params;
97 using MetricCollector::CollectionAttemptStatus;
98 using MetricCollector::CurrentTimerDelay;
99 using MetricCollector::Init;
100 using MetricCollector::IsRunning;
101 using MetricCollector::login_time;
102 using MetricCollector::RecordUserLogin;
103 using MetricCollector::SaveSerializedPerfProto;
104 using MetricCollector::ScheduleIntervalCollection;
105 using MetricCollector::ScheduleSessionRestoreCollection;
106 using MetricCollector::ScheduleSuspendDoneCollection;
107 using MetricCollector::set_profile_done_callback;
108 using MetricCollector::StopTimer;
109
110 private:
111 base::WeakPtrFactory<TestMetricCollector> weak_factory_{this};
112
113 DISALLOW_COPY_AND_ASSIGN(TestMetricCollector);
114 };
115
116 const base::TimeDelta kPeriodicCollectionInterval =
117 base::TimeDelta::FromHours(1);
118 const base::TimeDelta kMaxCollectionDelay = base::TimeDelta::FromSeconds(1);
119
120 } // namespace
121
122 class MetricCollectorTest : public testing::Test {
123 public:
MetricCollectorTest()124 MetricCollectorTest()
125 : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
126 perf_data_proto_(GetExamplePerfDataProto()) {}
127
SaveProfile(std::unique_ptr<SampledProfile> sampled_profile)128 void SaveProfile(std::unique_ptr<SampledProfile> sampled_profile) {
129 cached_profile_data_.resize(cached_profile_data_.size() + 1);
130 cached_profile_data_.back().Swap(sampled_profile.get());
131 }
132
SetUp()133 void SetUp() override {
134 CollectionParams test_params;
135 // Set the sampling factors for the triggers to 1, so we always trigger
136 // collection, and set the collection delays to well known quantities, so
137 // we can fast forward the time.
138 test_params.resume_from_suspend.sampling_factor = 1;
139 test_params.resume_from_suspend.max_collection_delay = kMaxCollectionDelay;
140 test_params.restore_session.sampling_factor = 1;
141 test_params.restore_session.max_collection_delay = kMaxCollectionDelay;
142 test_params.periodic_interval = kPeriodicCollectionInterval;
143
144 metric_collector_ = std::make_unique<TestMetricCollector>(test_params);
145 metric_collector_->set_profile_done_callback(base::BindRepeating(
146 &MetricCollectorTest::SaveProfile, base::Unretained(this)));
147 metric_collector_->Init();
148 // MetricCollector requires the user to be logged in.
149 metric_collector_->RecordUserLogin(base::TimeTicks::Now());
150 }
151
TearDown()152 void TearDown() override {
153 metric_collector_.reset();
154 cached_profile_data_.clear();
155 }
156
157 protected:
158 // task_environment_ must be the first member (or at least before
159 // any member that cares about tasks) to be initialized first and destroyed
160 // last.
161 content::BrowserTaskEnvironment task_environment_;
162
163 std::vector<SampledProfile> cached_profile_data_;
164
165 std::unique_ptr<TestMetricCollector> metric_collector_;
166
167 // Store sample perf data protobuf for testing.
168 PerfDataProto perf_data_proto_;
169
170 DISALLOW_COPY_AND_ASSIGN(MetricCollectorTest);
171 };
172
TEST_F(MetricCollectorTest,CheckSetup)173 TEST_F(MetricCollectorTest, CheckSetup) {
174 EXPECT_GT(perf_data_proto_.ByteSize(), 0);
175
176 // Timer is active after user logs in.
177 EXPECT_TRUE(metric_collector_->IsRunning());
178 EXPECT_FALSE(metric_collector_->login_time().is_null());
179 }
180
TEST_F(MetricCollectorTest,EmptyProtosAreNotSaved)181 TEST_F(MetricCollectorTest, EmptyProtosAreNotSaved) {
182 auto sampled_profile = std::make_unique<SampledProfile>();
183 sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
184 base::HistogramTester histogram_tester;
185
186 metric_collector_->SaveSerializedPerfProto(std::move(sampled_profile),
187 std::string());
188 task_environment_.RunUntilIdle();
189
190 EXPECT_TRUE(cached_profile_data_.empty());
191 histogram_tester.ExpectUniqueSample(
192 "ChromeOS.CWP.CollectTest",
193 TestMetricCollector::CollectionAttemptStatus::ILLEGAL_DATA_RETURNED, 1);
194 }
195
TEST_F(MetricCollectorTest,ProtosWithNoSamplesAreNotSaved)196 TEST_F(MetricCollectorTest, ProtosWithNoSamplesAreNotSaved) {
197 auto sampled_profile = std::make_unique<SampledProfile>();
198 sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
199 base::HistogramTester histogram_tester;
200
201 PerfDataProto proto = GetExamplePerfDataProto();
202 PerfDataProto_PerfEventStats* stats = proto.mutable_stats();
203 stats->set_num_sample_events(0);
204
205 metric_collector_->SaveSerializedPerfProto(std::move(sampled_profile),
206 proto.SerializeAsString());
207 task_environment_.RunUntilIdle();
208
209 EXPECT_TRUE(cached_profile_data_.empty());
210 histogram_tester.ExpectUniqueSample(
211 "ChromeOS.CWP.CollectTest",
212 TestMetricCollector::CollectionAttemptStatus::SESSION_HAS_ZERO_SAMPLES,
213 1);
214 }
215
TEST_F(MetricCollectorTest,PerfDataProto)216 TEST_F(MetricCollectorTest, PerfDataProto) {
217 auto sampled_profile = std::make_unique<SampledProfile>();
218 sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
219 base::HistogramTester histogram_tester;
220
221 metric_collector_->SaveSerializedPerfProto(
222 std::move(sampled_profile), perf_data_proto_.SerializeAsString());
223 task_environment_.RunUntilIdle();
224 ASSERT_EQ(1U, cached_profile_data_.size());
225 histogram_tester.ExpectUniqueSample(
226 "ChromeOS.CWP.CollectTest",
227 TestMetricCollector::CollectionAttemptStatus::SUCCESS, 1);
228
229 const SampledProfile& profile = cached_profile_data_[0];
230 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
231 EXPECT_TRUE(profile.has_ms_after_boot());
232 EXPECT_TRUE(profile.has_ms_after_login());
233
234 ASSERT_TRUE(profile.has_perf_data());
235 EXPECT_EQ(perf_data_proto_.SerializeAsString(),
236 profile.perf_data().SerializeAsString());
237 }
238
TEST_F(MetricCollectorTest,PerfDataProto_UnknownFieldsDiscarded)239 TEST_F(MetricCollectorTest, PerfDataProto_UnknownFieldsDiscarded) {
240 // First add some unknown fields to MMapEvent, CommEvent, PerfBuildID, and
241 // StringAndMd5sumPrefix. The known field values don't have to make sense for
242 // perf data. They are just padding to avoid having an otherwise empty proto.
243 // The unknown field string contents don't have to make sense as serialized
244 // data as the test is to discard them.
245
246 // MMapEvent
247 PerfDataProto_PerfEvent* event1 = perf_data_proto_.add_events();
248 event1->mutable_header()->set_type(1);
249 event1->mutable_mmap_event()->set_pid(1234);
250 event1->mutable_mmap_event()->set_filename_md5_prefix(0xdeadbeef);
251 // Missing field |MMapEvent::filename| has tag=6.
252 *event1->mutable_mmap_event()->mutable_unknown_fields() =
253 SerializeStringFieldWithTag(6, "/opt/google/chrome/chrome");
254
255 // CommEvent
256 PerfDataProto_PerfEvent* event2 = perf_data_proto_.add_events();
257 event2->mutable_header()->set_type(2);
258 event2->mutable_comm_event()->set_pid(5678);
259 event2->mutable_comm_event()->set_comm_md5_prefix(0x900df00d);
260 // Missing field |CommEvent::comm| has tag=3.
261 *event2->mutable_comm_event()->mutable_unknown_fields() =
262 SerializeStringFieldWithTag(3, "chrome");
263
264 // PerfBuildID
265 PerfDataProto_PerfBuildID* build_id = perf_data_proto_.add_build_ids();
266 build_id->set_misc(3);
267 build_id->set_pid(1337);
268 build_id->set_filename_md5_prefix(0x9876543210);
269 // Missing field |PerfBuildID::filename| has tag=4.
270 *build_id->mutable_unknown_fields() =
271 SerializeStringFieldWithTag(4, "/opt/google/chrome/chrome");
272
273 // StringAndMd5sumPrefix
274 PerfDataProto_StringMetadata* metadata =
275 perf_data_proto_.mutable_string_metadata();
276 metadata->mutable_perf_command_line_whole()->set_value_md5_prefix(
277 0x123456789);
278 // Missing field |StringAndMd5sumPrefix::value| has tag=1.
279 *metadata->mutable_perf_command_line_whole()->mutable_unknown_fields() =
280 SerializeStringFieldWithTag(1, "perf record -a -- sleep 1");
281
282 // Serialize to string and make sure it can be deserialized.
283 std::string perf_data_string = perf_data_proto_.SerializeAsString();
284 PerfDataProto temp_proto;
285 EXPECT_TRUE(temp_proto.ParseFromString(perf_data_string));
286
287 // Now pass it to |metric_collector_|.
288 auto sampled_profile = std::make_unique<SampledProfile>();
289 sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
290
291 // Perf data protos are saved from the collector task runner.
292 metric_collector_->SaveSerializedPerfProto(std::move(sampled_profile),
293 perf_data_string);
294 task_environment_.RunUntilIdle();
295
296 ASSERT_EQ(1U, cached_profile_data_.size());
297
298 const SampledProfile& profile = cached_profile_data_[0];
299 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
300 EXPECT_TRUE(profile.has_perf_data());
301
302 // The serialized form should be different because the unknown fields have
303 // have been removed.
304 EXPECT_NE(perf_data_string, profile.perf_data().SerializeAsString());
305
306 // Check contents of stored protobuf.
307 const PerfDataProto& stored_proto = profile.perf_data();
308 ASSERT_EQ(2, stored_proto.events_size());
309
310 // MMapEvent
311 const PerfDataProto_PerfEvent& stored_event1 = stored_proto.events(0);
312 EXPECT_EQ(1U, stored_event1.header().type());
313 EXPECT_EQ(1234U, stored_event1.mmap_event().pid());
314 EXPECT_EQ(0xdeadbeef, stored_event1.mmap_event().filename_md5_prefix());
315 EXPECT_EQ(0U, stored_event1.mmap_event().unknown_fields().size());
316
317 // CommEvent
318 const PerfDataProto_PerfEvent& stored_event2 = stored_proto.events(1);
319 EXPECT_EQ(2U, stored_event2.header().type());
320 EXPECT_EQ(5678U, stored_event2.comm_event().pid());
321 EXPECT_EQ(0x900df00d, stored_event2.comm_event().comm_md5_prefix());
322 EXPECT_EQ(0U, stored_event2.comm_event().unknown_fields().size());
323
324 // PerfBuildID
325 ASSERT_EQ(1, stored_proto.build_ids_size());
326 const PerfDataProto_PerfBuildID& stored_build_id = stored_proto.build_ids(0);
327 EXPECT_EQ(3U, stored_build_id.misc());
328 EXPECT_EQ(1337U, stored_build_id.pid());
329 EXPECT_EQ(0x9876543210U, stored_build_id.filename_md5_prefix());
330 EXPECT_EQ(0U, stored_build_id.unknown_fields().size());
331
332 // StringAndMd5sumPrefix
333 const PerfDataProto_StringMetadata& stored_metadata =
334 stored_proto.string_metadata();
335 EXPECT_EQ(0x123456789U,
336 stored_metadata.perf_command_line_whole().value_md5_prefix());
337 EXPECT_EQ(0U,
338 stored_metadata.perf_command_line_whole().unknown_fields().size());
339 }
340
341 // Change |sampled_profile| between calls to SaveSerializedPerfProto().
TEST_F(MetricCollectorTest,MultipleCalls)342 TEST_F(MetricCollectorTest, MultipleCalls) {
343 auto sampled_profile = std::make_unique<SampledProfile>();
344 sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
345
346 // Perf data protos are saved from the collector task runner.
347 metric_collector_->SaveSerializedPerfProto(
348 std::move(sampled_profile), perf_data_proto_.SerializeAsString());
349 task_environment_.RunUntilIdle();
350
351 sampled_profile = std::make_unique<SampledProfile>();
352 sampled_profile->set_trigger_event(SampledProfile::RESTORE_SESSION);
353 sampled_profile->set_ms_after_restore(3000);
354 metric_collector_->SaveSerializedPerfProto(
355 std::move(sampled_profile), perf_data_proto_.SerializeAsString());
356 task_environment_.RunUntilIdle();
357
358 sampled_profile = std::make_unique<SampledProfile>();
359 sampled_profile->set_trigger_event(SampledProfile::RESUME_FROM_SUSPEND);
360 sampled_profile->set_suspend_duration_ms(60000);
361 sampled_profile->set_ms_after_resume(1500);
362 metric_collector_->SaveSerializedPerfProto(
363 std::move(sampled_profile), perf_data_proto_.SerializeAsString());
364 task_environment_.RunUntilIdle();
365
366 ASSERT_EQ(3U, cached_profile_data_.size());
367
368 {
369 const SampledProfile& profile = cached_profile_data_[0];
370 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
371 EXPECT_TRUE(profile.has_ms_after_boot());
372 EXPECT_TRUE(profile.has_ms_after_login());
373 ASSERT_TRUE(profile.has_perf_data());
374 EXPECT_EQ(perf_data_proto_.SerializeAsString(),
375 profile.perf_data().SerializeAsString());
376 }
377
378 {
379 const SampledProfile& profile = cached_profile_data_[1];
380 EXPECT_EQ(SampledProfile::RESTORE_SESSION, profile.trigger_event());
381 EXPECT_TRUE(profile.has_ms_after_boot());
382 EXPECT_TRUE(profile.has_ms_after_login());
383 EXPECT_EQ(3000, profile.ms_after_restore());
384 ASSERT_TRUE(profile.has_perf_data());
385 EXPECT_EQ(perf_data_proto_.SerializeAsString(),
386 profile.perf_data().SerializeAsString());
387 }
388
389 {
390 const SampledProfile& profile = cached_profile_data_[2];
391 EXPECT_EQ(SampledProfile::RESUME_FROM_SUSPEND, profile.trigger_event());
392 EXPECT_TRUE(profile.has_ms_after_boot());
393 EXPECT_TRUE(profile.has_ms_after_login());
394 EXPECT_EQ(60000, profile.suspend_duration_ms());
395 EXPECT_EQ(1500, profile.ms_after_resume());
396 ASSERT_TRUE(profile.has_perf_data());
397 EXPECT_EQ(perf_data_proto_.SerializeAsString(),
398 profile.perf_data().SerializeAsString());
399 }
400 }
401
TEST_F(MetricCollectorTest,StopTimer)402 TEST_F(MetricCollectorTest, StopTimer) {
403 auto sampled_profile = std::make_unique<SampledProfile>();
404 sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
405
406 metric_collector_->CollectProfile(std::move(sampled_profile));
407 task_environment_.RunUntilIdle();
408
409 EXPECT_TRUE(metric_collector_->IsRunning());
410 EXPECT_FALSE(metric_collector_->login_time().is_null());
411
412 // Timer is stopped by StopTimer(), but login time and cached profiles stay.
413 metric_collector_->StopTimer();
414 EXPECT_FALSE(metric_collector_->IsRunning());
415 EXPECT_FALSE(metric_collector_->login_time().is_null());
416
417 EXPECT_FALSE(cached_profile_data_.empty());
418 }
419
TEST_F(MetricCollectorTest,ScheduleSuspendDoneCollection)420 TEST_F(MetricCollectorTest, ScheduleSuspendDoneCollection) {
421 const auto kSuspendDuration = base::TimeDelta::FromMinutes(3);
422
423 metric_collector_->ScheduleSuspendDoneCollection(kSuspendDuration);
424
425 // The timer should be running at this point.
426 EXPECT_TRUE(metric_collector_->IsRunning());
427
428 // Fast forward the time by the max collection delay.
429 task_environment_.FastForwardBy(kMaxCollectionDelay);
430
431 // Check that the SuspendDone trigger produced one profile.
432 ASSERT_EQ(1U, cached_profile_data_.size());
433
434 const SampledProfile& profile = cached_profile_data_[0];
435 EXPECT_EQ(SampledProfile::RESUME_FROM_SUSPEND, profile.trigger_event());
436 EXPECT_EQ(kSuspendDuration.InMilliseconds(), profile.suspend_duration_ms());
437 EXPECT_TRUE(profile.has_ms_after_resume());
438 EXPECT_TRUE(profile.has_ms_after_login());
439 EXPECT_TRUE(profile.has_ms_after_boot());
440
441 // A profile collection rearms the timer for a new perodic collection.
442 // Check that the timer is running.
443 EXPECT_TRUE(metric_collector_->IsRunning());
444 cached_profile_data_.clear();
445
446 // Currently, any collection from another trigger event pushes the periodic
447 // collection interval forward by kPeriodicCollectionInterval. Since we had
448 // a SuspendDone collection, we should not see any new profiles during the
449 // next periodic collection interval, but see one in the following interval.
450 task_environment_.FastForwardBy(kPeriodicCollectionInterval -
451 kMaxCollectionDelay);
452 EXPECT_TRUE(cached_profile_data_.empty());
453
454 task_environment_.FastForwardBy(kPeriodicCollectionInterval);
455
456 ASSERT_EQ(1U, cached_profile_data_.size());
457 const SampledProfile& profile2 = cached_profile_data_[0];
458 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile2.trigger_event());
459 }
460
TEST_F(MetricCollectorTest,ScheduleSessionRestoreCollection)461 TEST_F(MetricCollectorTest, ScheduleSessionRestoreCollection) {
462 const int kRestoredTabs = 7;
463
464 metric_collector_->ScheduleSessionRestoreCollection(kRestoredTabs);
465
466 // The timer should be running at this point.
467 EXPECT_TRUE(metric_collector_->IsRunning());
468
469 // Fast forward the time by the max collection delay.
470 task_environment_.FastForwardBy(kMaxCollectionDelay);
471
472 ASSERT_EQ(1U, cached_profile_data_.size());
473
474 const SampledProfile& profile = cached_profile_data_[0];
475 EXPECT_EQ(SampledProfile::RESTORE_SESSION, profile.trigger_event());
476 EXPECT_EQ(kRestoredTabs, profile.num_tabs_restored());
477 EXPECT_FALSE(profile.has_ms_after_resume());
478 EXPECT_TRUE(profile.has_ms_after_login());
479 EXPECT_TRUE(profile.has_ms_after_boot());
480
481 // Timer is rearmed for periodic collection after each collection.
482 // Check that the timer is running.
483 EXPECT_TRUE(metric_collector_->IsRunning());
484 cached_profile_data_.clear();
485
486 // A second SessionRestoreDone call is throttled.
487 metric_collector_->ScheduleSessionRestoreCollection(1);
488
489 // Fast forward the time by the max collection delay.
490 task_environment_.FastForwardBy(kMaxCollectionDelay);
491 // This should find no new session restore profiles.
492 EXPECT_TRUE(cached_profile_data_.empty());
493
494 // Currently, any collection from another trigger event pushes the periodic
495 // collection interval forward by kPeriodicCollectionInterval. Since we had
496 // a SessionRestore collection, we should not see any new profiles during the
497 // current periodic collection interval, but see one in the next interval.
498 task_environment_.FastForwardBy(kPeriodicCollectionInterval -
499 kMaxCollectionDelay * 2);
500 EXPECT_TRUE(cached_profile_data_.empty());
501
502 // Advance clock another collection interval. We should find a profile.
503 task_environment_.FastForwardBy(kPeriodicCollectionInterval);
504 ASSERT_EQ(1U, cached_profile_data_.size());
505 const SampledProfile& profile2 = cached_profile_data_[0];
506 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile2.trigger_event());
507
508 // Advance the clock another periodic collection interval. This run should
509 // include a new periodic collection, but no session restore.
510 cached_profile_data_.clear();
511 task_environment_.FastForwardBy(kPeriodicCollectionInterval);
512 ASSERT_EQ(1U, cached_profile_data_.size());
513 const SampledProfile& profile3 = cached_profile_data_[0];
514 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile3.trigger_event());
515 }
516
TEST_F(MetricCollectorTest,ScheduleIntervalCollection)517 TEST_F(MetricCollectorTest, ScheduleIntervalCollection) {
518 // Timer is active after login and a periodic collection is scheduled.
519 EXPECT_TRUE(metric_collector_->IsRunning());
520
521 // Advance the clock by a periodic collection interval. We must have a
522 // periodic collection profile.
523 task_environment_.FastForwardBy(kPeriodicCollectionInterval);
524
525 ASSERT_EQ(1U, cached_profile_data_.size());
526
527 const SampledProfile& profile = cached_profile_data_[0];
528 EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
529 EXPECT_FALSE(profile.has_suspend_duration_ms());
530 EXPECT_FALSE(profile.has_ms_after_resume());
531 EXPECT_TRUE(profile.has_ms_after_login());
532 EXPECT_TRUE(profile.has_ms_after_boot());
533
534 ASSERT_TRUE(profile.has_perf_data());
535 EXPECT_EQ(perf_data_proto_.SerializeAsString(),
536 profile.perf_data().SerializeAsString());
537
538 // Make sure timer is rearmed after each collection.
539 EXPECT_TRUE(metric_collector_->IsRunning());
540 }
541
542 // Setting the sampling factors to zero should disable the triggers.
543 // Otherwise, it could cause a div-by-zero crash.
TEST_F(MetricCollectorTest,ZeroSamplingFactorDisablesTrigger)544 TEST_F(MetricCollectorTest, ZeroSamplingFactorDisablesTrigger) {
545 // Define params with zero sampling factors.
546 CollectionParams test_params;
547 test_params.resume_from_suspend.sampling_factor = 0;
548 test_params.restore_session.sampling_factor = 0;
549
550 metric_collector_ = std::make_unique<TestMetricCollector>(test_params);
551 metric_collector_->Init();
552 metric_collector_->RecordUserLogin(base::TimeTicks::Now());
553
554 // Cancel the background collection.
555 metric_collector_->StopTimer();
556
557 EXPECT_FALSE(metric_collector_->IsRunning())
558 << "Sanity: timer should not be running.";
559
560 // Calling ScheduleSuspendDoneCollection or ScheduleSessionRestoreCollection
561 // should not start the timer that triggers collection.
562 metric_collector_->ScheduleSuspendDoneCollection(
563 base::TimeDelta::FromMinutes(10));
564 EXPECT_FALSE(metric_collector_->IsRunning());
565
566 metric_collector_->ScheduleSessionRestoreCollection(100);
567 EXPECT_FALSE(metric_collector_->IsRunning());
568 }
569
TEST_F(MetricCollectorTest,ZeroPeriodicIntervalDisablesCollection)570 TEST_F(MetricCollectorTest, ZeroPeriodicIntervalDisablesCollection) {
571 // Define params with zero periodic interval.
572 CollectionParams test_params;
573 test_params.periodic_interval = base::TimeDelta::FromMilliseconds(0);
574
575 metric_collector_ = std::make_unique<TestMetricCollector>(test_params);
576 metric_collector_->Init();
577 metric_collector_->RecordUserLogin(base::TimeTicks::Now());
578
579 EXPECT_FALSE(metric_collector_->IsRunning())
580 << "Sanity: timer should not be running.";
581
582 // Advance the clock by 10 hours. We should have no profile and timer is not
583 // running.
584 task_environment_.FastForwardBy(base::TimeDelta::FromHours(10));
585
586 EXPECT_FALSE(metric_collector_->IsRunning())
587 << "Sanity: timer should not be running.";
588
589 ASSERT_TRUE(cached_profile_data_.empty());
590 }
591
592 } // namespace internal
593
594 } // namespace metrics
595