blob: 6f902ac43185c15f0d30c43d4adcff2c9e061a70 [file] [log] [blame]
// Copyright 2018 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 "chrome/browser/metrics/perf/metric_collector.h"
#include <stdint.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/macros.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/metrics_proto/sampled_profile.pb.h"
#include "third_party/protobuf/src/google/protobuf/io/coded_stream.h"
#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
#include "third_party/protobuf/src/google/protobuf/wire_format_lite_inl.h"
namespace metrics {
namespace {
// Returns an example PerfDataProto. The contents don't have to make sense. They
// just need to constitute a semantically valid protobuf.
// |proto| is an output parameter that will contain the created protobuf.
PerfDataProto GetExamplePerfDataProto() {
PerfDataProto proto;
proto.set_timestamp_sec(1435604013); // Time since epoch in seconds.
PerfDataProto_PerfFileAttr* file_attr = proto.add_file_attrs();
file_attr->add_ids(61);
file_attr->add_ids(62);
file_attr->add_ids(63);
PerfDataProto_PerfEventAttr* attr = file_attr->mutable_attr();
attr->set_type(1);
attr->set_size(2);
attr->set_config(3);
attr->set_sample_period(4);
attr->set_sample_freq(5);
PerfDataProto_PerfEventStats* stats = proto.mutable_stats();
stats->set_num_events_read(100);
stats->set_num_sample_events(200);
stats->set_num_mmap_events(300);
stats->set_num_fork_events(400);
stats->set_num_exit_events(500);
return proto;
}
// Returns an example PerfStatProto. The contents don't have to make sense. They
// just need to constitute a semantically valid protobuf.
// |result| is an output parameter that will contain the created protobuf.
PerfStatProto GetExamplePerfStatProto() {
PerfStatProto proto;
proto.set_command_line(
"perf stat -a -e cycles -e instructions -e branches -- sleep 2");
PerfStatProto_PerfStatLine* line1 = proto.add_line();
line1->set_time_ms(1000);
line1->set_count(2000);
line1->set_event_name("cycles");
PerfStatProto_PerfStatLine* line2 = proto.add_line();
line2->set_time_ms(2000);
line2->set_count(5678);
line2->set_event_name("instructions");
PerfStatProto_PerfStatLine* line3 = proto.add_line();
line3->set_time_ms(3000);
line3->set_count(9999);
line3->set_event_name("branches");
return proto;
}
// Creates a serialized data stream containing a string with a field tag number.
std::string SerializeStringFieldWithTag(int field, const std::string& value) {
std::string result;
google::protobuf::io::StringOutputStream string_stream(&result);
google::protobuf::io::CodedOutputStream output(&string_stream);
using google::protobuf::internal::WireFormatLite;
WireFormatLite::WriteTag(field, WireFormatLite::WIRETYPE_LENGTH_DELIMITED,
&output);
output.WriteVarint32(value.size());
output.WriteString(value);
return result;
}
// Allows access to some private methods for testing.
class TestMetricCollector : public MetricCollector {
public:
TestMetricCollector() : MetricCollector("UMA.CWP.TestData") {}
explicit TestMetricCollector(const CollectionParams& collection_params)
: MetricCollector("UMA.CWP.TestData", collection_params) {}
void CollectProfile(
std::unique_ptr<SampledProfile> sampled_profile) override {
PerfDataProto perf_data_proto = GetExamplePerfDataProto();
SaveSerializedPerfProto(std::move(sampled_profile),
PerfProtoType::PERF_TYPE_DATA,
perf_data_proto.SerializeAsString());
}
using MetricCollector::collection_params;
using MetricCollector::login_time;
using MetricCollector::PerfProtoType;
using MetricCollector::SaveSerializedPerfProto;
using MetricCollector::ScheduleIntervalCollection;
using MetricCollector::timer;
private:
std::vector<SampledProfile> stored_profiles_;
DISALLOW_COPY_AND_ASSIGN(TestMetricCollector);
};
} // namespace
class MetricCollectorTest : public testing::Test {
public:
MetricCollectorTest()
: task_runner_(base::MakeRefCounted<base::TestSimpleTaskRunner>()),
task_runner_handle_(task_runner_),
perf_data_proto_(GetExamplePerfDataProto()),
perf_stat_proto_(GetExamplePerfStatProto()) {}
void SetUp() override {
CollectionParams test_params;
// Set the sampling factors for the triggers to 1, so we always trigger
// collection.
test_params.resume_from_suspend.sampling_factor = 1;
test_params.restore_session.sampling_factor = 1;
metric_collector_ = std::make_unique<TestMetricCollector>(test_params);
metric_collector_->Init();
// MetricCollector requires the user to be logged in.
metric_collector_->OnUserLoggedIn();
}
void TearDown() override { metric_collector_.reset(); }
protected:
std::unique_ptr<TestMetricCollector> metric_collector_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
base::ThreadTaskRunnerHandle task_runner_handle_;
// Store sample perf data/stat protobufs for testing.
PerfDataProto perf_data_proto_;
PerfStatProto perf_stat_proto_;
DISALLOW_COPY_AND_ASSIGN(MetricCollectorTest);
};
TEST_F(MetricCollectorTest, CheckSetup) {
EXPECT_GT(perf_data_proto_.ByteSize(), 0);
EXPECT_GT(perf_stat_proto_.ByteSize(), 0);
// Timer is active after user logs in.
EXPECT_TRUE(metric_collector_->timer().IsRunning());
EXPECT_FALSE(metric_collector_->login_time().is_null());
// There are no cached profiles at start.
std::vector<SampledProfile> stored_profiles;
EXPECT_FALSE(metric_collector_->GetSampledProfiles(&stored_profiles));
EXPECT_TRUE(stored_profiles.empty());
}
TEST_F(MetricCollectorTest, EnabledOnLogin) {
metric_collector_->OnUserLoggedIn();
EXPECT_TRUE(metric_collector_->timer().IsRunning());
EXPECT_FALSE(metric_collector_->login_time().is_null());
}
TEST_F(MetricCollectorTest, EmptyProtosAreNotSaved) {
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
metric_collector_->SaveSerializedPerfProto(
std::move(sampled_profile),
TestMetricCollector::PerfProtoType::PERF_TYPE_DATA, std::string());
std::vector<SampledProfile> stored_profiles;
EXPECT_FALSE(metric_collector_->GetSampledProfiles(&stored_profiles));
}
TEST_F(MetricCollectorTest, PerfDataProto) {
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
metric_collector_->SaveSerializedPerfProto(
std::move(sampled_profile),
TestMetricCollector::PerfProtoType::PERF_TYPE_DATA,
perf_data_proto_.SerializeAsString());
std::vector<SampledProfile> stored_profiles;
EXPECT_TRUE(metric_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(1U, stored_profiles.size());
const SampledProfile& profile = stored_profiles[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
EXPECT_TRUE(profile.has_ms_after_boot());
EXPECT_TRUE(profile.has_ms_after_login());
ASSERT_TRUE(profile.has_perf_data());
EXPECT_FALSE(profile.has_perf_stat());
EXPECT_EQ(perf_data_proto_.SerializeAsString(),
profile.perf_data().SerializeAsString());
}
TEST_F(MetricCollectorTest, PerfDataProto_UnknownFieldsDiscarded) {
// First add some unknown fields to MMapEvent, CommEvent, PerfBuildID, and
// StringAndMd5sumPrefix. The known field values don't have to make sense for
// perf data. They are just padding to avoid having an otherwise empty proto.
// The unknown field string contents don't have to make sense as serialized
// data as the test is to discard them.
// MMapEvent
PerfDataProto_PerfEvent* event1 = perf_data_proto_.add_events();
event1->mutable_header()->set_type(1);
event1->mutable_mmap_event()->set_pid(1234);
event1->mutable_mmap_event()->set_filename_md5_prefix(0xdeadbeef);
// Missing field |MMapEvent::filename| has tag=6.
*event1->mutable_mmap_event()->mutable_unknown_fields() =
SerializeStringFieldWithTag(6, "/opt/google/chrome/chrome");
// CommEvent
PerfDataProto_PerfEvent* event2 = perf_data_proto_.add_events();
event2->mutable_header()->set_type(2);
event2->mutable_comm_event()->set_pid(5678);
event2->mutable_comm_event()->set_comm_md5_prefix(0x900df00d);
// Missing field |CommEvent::comm| has tag=3.
*event2->mutable_comm_event()->mutable_unknown_fields() =
SerializeStringFieldWithTag(3, "chrome");
// PerfBuildID
PerfDataProto_PerfBuildID* build_id = perf_data_proto_.add_build_ids();
build_id->set_misc(3);
build_id->set_pid(1337);
build_id->set_filename_md5_prefix(0x9876543210);
// Missing field |PerfBuildID::filename| has tag=4.
*build_id->mutable_unknown_fields() =
SerializeStringFieldWithTag(4, "/opt/google/chrome/chrome");
// StringAndMd5sumPrefix
PerfDataProto_StringMetadata* metadata =
perf_data_proto_.mutable_string_metadata();
metadata->mutable_perf_command_line_whole()->set_value_md5_prefix(
0x123456789);
// Missing field |StringAndMd5sumPrefix::value| has tag=1.
*metadata->mutable_perf_command_line_whole()->mutable_unknown_fields() =
SerializeStringFieldWithTag(1, "perf record -a -- sleep 1");
// Serialize to string and make sure it can be deserialized.
std::string perf_data_string = perf_data_proto_.SerializeAsString();
PerfDataProto temp_proto;
EXPECT_TRUE(temp_proto.ParseFromString(perf_data_string));
// Now pass it to |metric_collector_|.
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
metric_collector_->SaveSerializedPerfProto(
std::move(sampled_profile),
TestMetricCollector::PerfProtoType::PERF_TYPE_DATA, perf_data_string);
std::vector<SampledProfile> stored_profiles;
EXPECT_TRUE(metric_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(1U, stored_profiles.size());
const SampledProfile& profile = stored_profiles[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
EXPECT_TRUE(profile.has_perf_data());
// The serialized form should be different because the unknown fields have
// have been removed.
EXPECT_NE(perf_data_string, profile.perf_data().SerializeAsString());
// Check contents of stored protobuf.
const PerfDataProto& stored_proto = profile.perf_data();
ASSERT_EQ(2, stored_proto.events_size());
// MMapEvent
const PerfDataProto_PerfEvent& stored_event1 = stored_proto.events(0);
EXPECT_EQ(1U, stored_event1.header().type());
EXPECT_EQ(1234U, stored_event1.mmap_event().pid());
EXPECT_EQ(0xdeadbeef, stored_event1.mmap_event().filename_md5_prefix());
EXPECT_EQ(0U, stored_event1.mmap_event().unknown_fields().size());
// CommEvent
const PerfDataProto_PerfEvent& stored_event2 = stored_proto.events(1);
EXPECT_EQ(2U, stored_event2.header().type());
EXPECT_EQ(5678U, stored_event2.comm_event().pid());
EXPECT_EQ(0x900df00d, stored_event2.comm_event().comm_md5_prefix());
EXPECT_EQ(0U, stored_event2.comm_event().unknown_fields().size());
// PerfBuildID
ASSERT_EQ(1, stored_proto.build_ids_size());
const PerfDataProto_PerfBuildID& stored_build_id = stored_proto.build_ids(0);
EXPECT_EQ(3U, stored_build_id.misc());
EXPECT_EQ(1337U, stored_build_id.pid());
EXPECT_EQ(0x9876543210U, stored_build_id.filename_md5_prefix());
EXPECT_EQ(0U, stored_build_id.unknown_fields().size());
// StringAndMd5sumPrefix
const PerfDataProto_StringMetadata& stored_metadata =
stored_proto.string_metadata();
EXPECT_EQ(0x123456789U,
stored_metadata.perf_command_line_whole().value_md5_prefix());
EXPECT_EQ(0U,
stored_metadata.perf_command_line_whole().unknown_fields().size());
}
TEST_F(MetricCollectorTest, PerfStatProto) {
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
metric_collector_->SaveSerializedPerfProto(
std::move(sampled_profile),
TestMetricCollector::PerfProtoType::PERF_TYPE_STAT,
perf_stat_proto_.SerializeAsString());
std::vector<SampledProfile> stored_profiles;
EXPECT_TRUE(metric_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(1U, stored_profiles.size());
const SampledProfile& profile = stored_profiles[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
EXPECT_TRUE(profile.has_ms_after_boot());
EXPECT_TRUE(profile.has_ms_after_login());
EXPECT_FALSE(profile.has_perf_data());
ASSERT_TRUE(profile.has_perf_stat());
EXPECT_EQ(perf_stat_proto_.SerializeAsString(),
profile.perf_stat().SerializeAsString());
}
// Change |sampled_profile| between calls to SaveSerializedPerfProto().
TEST_F(MetricCollectorTest, MultipleCalls) {
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
metric_collector_->SaveSerializedPerfProto(
std::move(sampled_profile),
TestMetricCollector::PerfProtoType::PERF_TYPE_DATA,
perf_data_proto_.SerializeAsString());
sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::RESTORE_SESSION);
sampled_profile->set_ms_after_restore(3000);
metric_collector_->SaveSerializedPerfProto(
std::move(sampled_profile),
TestMetricCollector::PerfProtoType::PERF_TYPE_STAT,
perf_stat_proto_.SerializeAsString());
sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::RESUME_FROM_SUSPEND);
sampled_profile->set_suspend_duration_ms(60000);
sampled_profile->set_ms_after_resume(1500);
metric_collector_->SaveSerializedPerfProto(
std::move(sampled_profile),
TestMetricCollector::PerfProtoType::PERF_TYPE_DATA,
perf_data_proto_.SerializeAsString());
sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
metric_collector_->SaveSerializedPerfProto(
std::move(sampled_profile),
TestMetricCollector::PerfProtoType::PERF_TYPE_STAT,
perf_stat_proto_.SerializeAsString());
std::vector<SampledProfile> stored_profiles;
EXPECT_TRUE(metric_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(4U, stored_profiles.size());
{
const SampledProfile& profile = stored_profiles[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
EXPECT_TRUE(profile.has_ms_after_boot());
EXPECT_TRUE(profile.has_ms_after_login());
ASSERT_TRUE(profile.has_perf_data());
EXPECT_FALSE(profile.has_perf_stat());
EXPECT_EQ(perf_data_proto_.SerializeAsString(),
profile.perf_data().SerializeAsString());
}
{
const SampledProfile& profile = stored_profiles[1];
EXPECT_EQ(SampledProfile::RESTORE_SESSION, profile.trigger_event());
EXPECT_TRUE(profile.has_ms_after_boot());
EXPECT_TRUE(profile.has_ms_after_login());
EXPECT_EQ(3000, profile.ms_after_restore());
EXPECT_FALSE(profile.has_perf_data());
ASSERT_TRUE(profile.has_perf_stat());
EXPECT_EQ(perf_stat_proto_.SerializeAsString(),
profile.perf_stat().SerializeAsString());
}
{
const SampledProfile& profile = stored_profiles[2];
EXPECT_EQ(SampledProfile::RESUME_FROM_SUSPEND, profile.trigger_event());
EXPECT_TRUE(profile.has_ms_after_boot());
EXPECT_TRUE(profile.has_ms_after_login());
EXPECT_EQ(60000, profile.suspend_duration_ms());
EXPECT_EQ(1500, profile.ms_after_resume());
ASSERT_TRUE(profile.has_perf_data());
EXPECT_FALSE(profile.has_perf_stat());
EXPECT_EQ(perf_data_proto_.SerializeAsString(),
profile.perf_data().SerializeAsString());
}
{
const SampledProfile& profile = stored_profiles[3];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
EXPECT_TRUE(profile.has_ms_after_boot());
EXPECT_TRUE(profile.has_ms_after_login());
EXPECT_FALSE(profile.has_perf_data());
ASSERT_TRUE(profile.has_perf_stat());
EXPECT_EQ(perf_stat_proto_.SerializeAsString(),
profile.perf_stat().SerializeAsString());
}
}
TEST_F(MetricCollectorTest, Deactivate) {
auto sampled_profile = std::make_unique<SampledProfile>();
sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION);
metric_collector_->CollectProfile(std::move(sampled_profile));
metric_collector_->OnUserLoggedIn();
EXPECT_TRUE(metric_collector_->timer().IsRunning());
EXPECT_FALSE(metric_collector_->login_time().is_null());
// Timer is stopped by Deactivate(), but login time and cached profiles stay.
metric_collector_->Deactivate();
EXPECT_FALSE(metric_collector_->timer().IsRunning());
EXPECT_FALSE(metric_collector_->login_time().is_null());
std::vector<SampledProfile> stored_profiles;
EXPECT_TRUE(metric_collector_->GetSampledProfiles(&stored_profiles));
}
TEST_F(MetricCollectorTest, SuspendDone) {
const auto kSuspendDuration = base::TimeDelta::FromMinutes(3);
metric_collector_->SuspendDone(kSuspendDuration);
// Timer is active after the SuspendDone call.
EXPECT_TRUE(metric_collector_->timer().IsRunning());
// Run all pending tasks. This will run all the tasks already queued, but not
// any new tasks created while executing the existing pending tasks. This is
// important, because our collectors always queue a new periodic collection
// task after each collection, regardless of trigger. Thus, RunUntilIdle
// would never terminate.
task_runner_->RunPendingTasks();
std::vector<SampledProfile> stored_profiles;
EXPECT_TRUE(metric_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(1U, stored_profiles.size());
// Timer is rearmed for periodic collection after each collection.
EXPECT_TRUE(metric_collector_->timer().IsRunning());
const SampledProfile& profile = stored_profiles[0];
EXPECT_EQ(SampledProfile::RESUME_FROM_SUSPEND, profile.trigger_event());
EXPECT_EQ(kSuspendDuration.InMilliseconds(), profile.suspend_duration_ms());
EXPECT_TRUE(profile.has_ms_after_resume());
EXPECT_TRUE(profile.has_ms_after_login());
EXPECT_TRUE(profile.has_ms_after_boot());
// Run all new pending tasks. This will run a periodic collection that was
// scheduled by the previous collection event.
task_runner_->RunPendingTasks();
stored_profiles.clear();
EXPECT_TRUE(metric_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(1U, stored_profiles.size());
const SampledProfile& profile2 = stored_profiles[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile2.trigger_event());
}
TEST_F(MetricCollectorTest, OnSessionRestoreDone) {
const int kRestoredTabs = 7;
metric_collector_->OnSessionRestoreDone(kRestoredTabs);
// Timer is active after the OnSessionRestoreDone call.
EXPECT_TRUE(metric_collector_->timer().IsRunning());
// Run all pending tasks.
task_runner_->RunPendingTasks();
std::vector<SampledProfile> stored_profiles;
EXPECT_TRUE(metric_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(1U, stored_profiles.size());
// Timer is rearmed for periodic collection after each collection.
EXPECT_TRUE(metric_collector_->timer().IsRunning());
const SampledProfile& profile = stored_profiles[0];
EXPECT_EQ(SampledProfile::RESTORE_SESSION, profile.trigger_event());
EXPECT_EQ(kRestoredTabs, profile.num_tabs_restored());
EXPECT_FALSE(profile.has_ms_after_resume());
EXPECT_TRUE(profile.has_ms_after_login());
EXPECT_TRUE(profile.has_ms_after_boot());
// A second SessionRestoreDone call is throttled.
metric_collector_->OnSessionRestoreDone(1);
// Run all new pending tasks. This will run a periodic collection, but not a
// second session restore.
task_runner_->RunPendingTasks();
stored_profiles.clear();
EXPECT_TRUE(metric_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(1U, stored_profiles.size());
const SampledProfile& profile2 = stored_profiles[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile2.trigger_event());
// Rerun any new pending tasks. This run should include a new periodic
// collection, but no session restore.
task_runner_->RunPendingTasks();
stored_profiles.clear();
EXPECT_TRUE(metric_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(1U, stored_profiles.size());
const SampledProfile& profile3 = stored_profiles[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile3.trigger_event());
}
TEST_F(MetricCollectorTest, ScheduleIntervalCollection) {
// Timer is active after login and a periodic collection is scheduled.
EXPECT_TRUE(metric_collector_->timer().IsRunning());
// Run all pending tasks.
task_runner_->RunPendingTasks();
std::vector<SampledProfile> stored_profiles;
EXPECT_TRUE(metric_collector_->GetSampledProfiles(&stored_profiles));
ASSERT_EQ(1U, stored_profiles.size());
// Timer is rearmed after each collection.
EXPECT_TRUE(metric_collector_->timer().IsRunning());
const SampledProfile& profile = stored_profiles[0];
EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event());
EXPECT_FALSE(profile.has_suspend_duration_ms());
EXPECT_FALSE(profile.has_ms_after_resume());
EXPECT_TRUE(profile.has_ms_after_login());
EXPECT_TRUE(profile.has_ms_after_boot());
ASSERT_TRUE(profile.has_perf_data());
EXPECT_FALSE(profile.has_perf_stat());
EXPECT_EQ(perf_data_proto_.SerializeAsString(),
profile.perf_data().SerializeAsString());
}
// Setting the sampling factors to zero should disable the triggers.
// Otherwise, it could cause a div-by-zero crash.
TEST_F(MetricCollectorTest, ZeroSamplingFactorDisablesTrigger) {
// Define params with zero sampling factors.
CollectionParams test_params;
test_params.resume_from_suspend.sampling_factor = 0;
test_params.restore_session.sampling_factor = 0;
metric_collector_ = std::make_unique<TestMetricCollector>(test_params);
metric_collector_->Init();
// Cancel the background collection.
metric_collector_->Deactivate();
EXPECT_FALSE(metric_collector_->timer().IsRunning())
<< "Sanity: timer should not be running.";
// Calling SuspendDone or OnSessionRestoreDone should not start the timer
// that triggers collection.
metric_collector_->SuspendDone(base::TimeDelta::FromMinutes(10));
EXPECT_FALSE(metric_collector_->timer().IsRunning());
metric_collector_->OnSessionRestoreDone(100);
EXPECT_FALSE(metric_collector_->timer().IsRunning());
}
} // namespace metrics