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