blob: e52f18e7cf9b073f8c2f57f4447671fa3ac9c7be [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/metrics/structured/structured_metrics_provider.h"
#include <cstdint>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/threading/scoped_blocking_call.h"
#include "components/metrics/structured/event.h"
#include "components/metrics/structured/recorder.h"
#include "components/metrics/structured/storage.pb.h"
#include "components/metrics/structured/structured_events.h"
#include "components/metrics/structured/structured_metrics_features.h"
#include "components/metrics/structured/structured_metrics_recorder.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
namespace metrics::structured {
namespace {
constexpr char kHwid[] = "hwid";
constexpr size_t kUserCount = 3;
class TestRecorder : public StructuredMetricsClient::RecordingDelegate {
public:
TestRecorder() = default;
TestRecorder(const TestRecorder& recorder) = delete;
TestRecorder& operator=(const TestRecorder& recorder) = delete;
~TestRecorder() override = default;
void RecordEvent(Event&& event) override {
Recorder::GetInstance()->RecordEvent(std::move(event));
}
bool IsReadyToRecord() const override { return true; }
};
class TestSystemProfileProvider : public metrics::MetricsProvider {
public:
TestSystemProfileProvider() = default;
TestSystemProfileProvider(const TestSystemProfileProvider& recorder) = delete;
TestSystemProfileProvider& operator=(
const TestSystemProfileProvider& recorder) = delete;
~TestSystemProfileProvider() override = default;
void ProvideSystemProfileMetrics(
metrics::SystemProfileProto* proto) override {
proto->set_multi_profile_user_count(kUserCount);
proto->mutable_hardware()->set_full_hardware_class(kHwid);
}
};
} // namespace
class StructuredMetricsProviderTest : public testing::Test {
protected:
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
Recorder::GetInstance()->SetUiTaskRunner(
task_environment_.GetMainThreadTaskRunner());
StructuredMetricsClient::Get()->SetDelegate(&recorder_);
// Move the mock date forward from day 0, because KeyData assumes that day 0
// is a bug.
task_environment_.AdvanceClock(base::Days(1000));
}
base::FilePath TempDirPath() { return temp_dir_.GetPath(); }
base::FilePath ProfileKeyFilePath() {
return temp_dir_.GetPath().Append("structured_metrics").Append("keys");
}
base::FilePath DeviceKeyFilePath() {
return temp_dir_.GetPath()
.Append("structured_metrics")
.Append("device_keys");
}
void Wait() { task_environment_.RunUntilIdle(); }
// Simulates the three external events that the structure metrics system cares
// about: the metrics service initializing and enabling its providers, and a
// user logging in.
void Init() {
system_profile_provider_ = std::make_unique<TestSystemProfileProvider>();
// Create a system profile, normally done by ChromeMetricsServiceClient.
auto structured_metrics_recorder =
std::unique_ptr<StructuredMetricsRecorder>(
new StructuredMetricsRecorder(DeviceKeyFilePath(),
/*write_delay=*/base::Seconds(0),
system_profile_provider_.get()));
// Create the provider, normally done by the ChromeMetricsServiceClient.
provider_ = std::unique_ptr<StructuredMetricsProvider>(
new StructuredMetricsProvider(
/*min_independent_metrics_interval=*/base::Seconds(0),
std::move(structured_metrics_recorder)));
// Enable recording, normally done after the metrics service has checked
// consent allows recording.
provider_->OnRecordingEnabled();
}
void OnRecordingEnabled() { provider_->OnRecordingEnabled(); }
void OnProfileAdded(const base::FilePath& path) {
provider_->recorder().OnProfileAdded(path);
}
StructuredDataProto GetSessionData() {
ChromeUserMetricsExtension uma_proto;
provider_->ProvideCurrentSessionData(&uma_proto);
Wait();
return uma_proto.structured_data();
}
StructuredDataProto GetIndependentMetrics() {
ChromeUserMetricsExtension uma_proto;
if (provider_->HasIndependentMetrics()) {
provider_->ProvideIndependentMetrics(
base::BindOnce([](bool success) { CHECK(success); }), &uma_proto,
nullptr);
Wait();
return uma_proto.structured_data();
}
auto p = StructuredDataProto();
return p;
}
void ExpectNoErrors() {
histogram_tester_.ExpectTotalCount("UMA.StructuredMetrics.InternalError",
0);
}
bool RecorderInitialized() {
return provider_->recorder().is_init_state(
StructuredMetricsRecorder::InitState::kInitialized);
}
protected:
std::unique_ptr<TestSystemProfileProvider> system_profile_provider_;
std::unique_ptr<StructuredMetricsProvider> provider_;
// Feature list should be constructed before task environment.
base::test::ScopedFeatureList scoped_feature_list_;
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::MainThreadType::UI,
base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED,
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
base::HistogramTester histogram_tester_;
base::ScopedTempDir temp_dir_;
private:
TestRecorder recorder_;
};
// Ensure that disabling independent upload of non-client_id metrics via feature
// flag instead uploads them in the main UMA upload.
TEST_F(StructuredMetricsProviderTest, DisableIndependentUploads) {
scoped_feature_list_.InitAndEnableFeatureWithParameters(
kStructuredMetrics, {{"enable_independent_metrics_upload", "false"}});
Init();
// Add a profile, normally done by the ChromeMetricsServiceClient after a
// user logs in.
OnProfileAdded(TempDirPath());
Wait();
OnRecordingEnabled();
events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1).Record();
events::v2::test_project_three::TestEventFour().SetTestMetricFour(1).Record();
EXPECT_EQ(GetIndependentMetrics().events_size(), 0);
EXPECT_EQ(GetSessionData().events_size(), 2);
ExpectNoErrors();
}
TEST_F(StructuredMetricsProviderTest, NoIndependentUploadsBeforeInitialized) {
Init();
// Verify the recorder is not initialized.
EXPECT_FALSE(RecorderInitialized());
events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1).Record();
events::v2::test_project_three::TestEventFour().SetTestMetricFour(1).Record();
EXPECT_EQ(GetIndependentMetrics().events_size(), 0);
EXPECT_EQ(GetSessionData().events_size(), 0);
ExpectNoErrors();
}
} // namespace metrics::structured