// Copyright 2014 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 "components/metrics/metrics_log_manager.h"
#include <string>
#include <utility>
#include <vector>
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/testing_pref_service.h"
#include "components/metrics/metrics_log.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/metrics/test_metrics_service_client.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace metrics {
namespace {
// Dummy serializer that just stores logs in memory.
class TestLogPrefService : public TestingPrefServiceSimple {
public:
TestLogPrefService() {
registry()->RegisterListPref(prefs::kMetricsInitialLogs);
registry()->RegisterListPref(prefs::kMetricsInitialLogsOld);
registry()->RegisterListPref(prefs::kMetricsOngoingLogs);
registry()->RegisterListPref(prefs::kMetricsOngoingLogsOld);
}
// Returns the number of logs of the given type.
size_t TypeCount(MetricsLog::LogType log_type) {
int list_length = 0;
if (log_type == MetricsLog::INITIAL_STABILITY_LOG)
list_length = GetList(prefs::kMetricsInitialLogs)->GetSize();
else
list_length = GetList(prefs::kMetricsOngoingLogs)->GetSize();
return list_length / 2;
}
};
} // namespace
TEST(MetricsLogManagerTest, StandardFlow) {
TestMetricsServiceClient client;
TestLogPrefService pref_service;
MetricsLogManager log_manager(&pref_service, 0);
// Make sure a new manager has a clean slate.
EXPECT_EQ(NULL, log_manager.current_log());
EXPECT_FALSE(log_manager.has_staged_log());
EXPECT_FALSE(log_manager.has_unsent_logs());
// Check that the normal flow works.
MetricsLog* initial_log = new MetricsLog(
"id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service);
log_manager.BeginLoggingWithLog(make_scoped_ptr(initial_log));
EXPECT_EQ(initial_log, log_manager.current_log());
EXPECT_FALSE(log_manager.has_staged_log());
log_manager.FinishCurrentLog();
EXPECT_EQ(NULL, log_manager.current_log());
EXPECT_TRUE(log_manager.has_unsent_logs());
EXPECT_FALSE(log_manager.has_staged_log());
MetricsLog* second_log =
new MetricsLog("id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service);
log_manager.BeginLoggingWithLog(make_scoped_ptr(second_log));
EXPECT_EQ(second_log, log_manager.current_log());
log_manager.StageNextLogForUpload();
EXPECT_TRUE(log_manager.has_staged_log());
EXPECT_FALSE(log_manager.staged_log().empty());
log_manager.DiscardStagedLog();
EXPECT_EQ(second_log, log_manager.current_log());
EXPECT_FALSE(log_manager.has_staged_log());
EXPECT_FALSE(log_manager.has_unsent_logs());
EXPECT_FALSE(log_manager.has_unsent_logs());
}
TEST(MetricsLogManagerTest, AbandonedLog) {
TestMetricsServiceClient client;
TestLogPrefService pref_service;
MetricsLogManager log_manager(&pref_service, 0);
MetricsLog* dummy_log = new MetricsLog(
"id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service);
log_manager.BeginLoggingWithLog(make_scoped_ptr(dummy_log));
EXPECT_EQ(dummy_log, log_manager.current_log());
log_manager.DiscardCurrentLog();
EXPECT_EQ(NULL, log_manager.current_log());
EXPECT_FALSE(log_manager.has_staged_log());
}
TEST(MetricsLogManagerTest, InterjectedLog) {
TestMetricsServiceClient client;
TestLogPrefService pref_service;
MetricsLogManager log_manager(&pref_service, 0);
MetricsLog* ongoing_log =
new MetricsLog("id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service);
MetricsLog* temp_log = new MetricsLog(
"id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service);
log_manager.BeginLoggingWithLog(make_scoped_ptr(ongoing_log));
EXPECT_EQ(ongoing_log, log_manager.current_log());
log_manager.PauseCurrentLog();
EXPECT_EQ(NULL, log_manager.current_log());
log_manager.BeginLoggingWithLog(make_scoped_ptr(temp_log));
EXPECT_EQ(temp_log, log_manager.current_log());
log_manager.FinishCurrentLog();
EXPECT_EQ(NULL, log_manager.current_log());
log_manager.ResumePausedLog();
EXPECT_EQ(ongoing_log, log_manager.current_log());
EXPECT_FALSE(log_manager.has_staged_log());
log_manager.StageNextLogForUpload();
log_manager.DiscardStagedLog();
EXPECT_FALSE(log_manager.has_unsent_logs());
}
TEST(MetricsLogManagerTest, InterjectedLogPreservesType) {
TestMetricsServiceClient client;
TestLogPrefService pref_service;
MetricsLogManager log_manager(&pref_service, 0);
log_manager.LoadPersistedUnsentLogs();
log_manager.BeginLoggingWithLog(make_scoped_ptr(new MetricsLog(
"id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)));
log_manager.PauseCurrentLog();
log_manager.BeginLoggingWithLog(make_scoped_ptr(new MetricsLog(
"id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service)));
log_manager.FinishCurrentLog();
log_manager.ResumePausedLog();
log_manager.StageNextLogForUpload();
log_manager.DiscardStagedLog();
// Verify that the remaining log (which is the original ongoing log) still
// has the right type.
log_manager.FinishCurrentLog();
log_manager.PersistUnsentLogs();
EXPECT_EQ(0U, pref_service.TypeCount(MetricsLog::INITIAL_STABILITY_LOG));
EXPECT_EQ(1U, pref_service.TypeCount(MetricsLog::ONGOING_LOG));
}
TEST(MetricsLogManagerTest, StoreAndLoad) {
TestMetricsServiceClient client;
TestLogPrefService pref_service;
// Set up some in-progress logging in a scoped log manager simulating the
// leadup to quitting, then persist as would be done on quit.
{
MetricsLogManager log_manager(&pref_service, 0);
log_manager.LoadPersistedUnsentLogs();
// Simulate a log having already been unsent from a previous session.
{
std::string log("proto");
PersistedLogs ongoing_logs(&pref_service, prefs::kMetricsOngoingLogs,
prefs::kMetricsOngoingLogsOld, 1, 1, 0);
ongoing_logs.StoreLog(log);
ongoing_logs.SerializeLogs();
}
EXPECT_EQ(1U, pref_service.TypeCount(MetricsLog::ONGOING_LOG));
EXPECT_FALSE(log_manager.has_unsent_logs());
log_manager.LoadPersistedUnsentLogs();
EXPECT_TRUE(log_manager.has_unsent_logs());
log_manager.BeginLoggingWithLog(make_scoped_ptr(new MetricsLog(
"id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service)));
log_manager.FinishCurrentLog();
log_manager.BeginLoggingWithLog(make_scoped_ptr(new MetricsLog(
"id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)));
log_manager.StageNextLogForUpload();
log_manager.StoreStagedLogAsUnsent(PersistedLogs::NORMAL_STORE);
log_manager.FinishCurrentLog();
// Nothing should be written out until PersistUnsentLogs is called.
EXPECT_EQ(0U, pref_service.TypeCount(MetricsLog::INITIAL_STABILITY_LOG));
EXPECT_EQ(1U, pref_service.TypeCount(MetricsLog::ONGOING_LOG));
log_manager.PersistUnsentLogs();
EXPECT_EQ(1U, pref_service.TypeCount(MetricsLog::INITIAL_STABILITY_LOG));
EXPECT_EQ(2U, pref_service.TypeCount(MetricsLog::ONGOING_LOG));
}
// Now simulate the relaunch, ensure that the log manager restores
// everything correctly, and verify that once the are handled they are not
// re-persisted.
{
MetricsLogManager log_manager(&pref_service, 0);
log_manager.LoadPersistedUnsentLogs();
EXPECT_TRUE(log_manager.has_unsent_logs());
log_manager.StageNextLogForUpload();
log_manager.DiscardStagedLog();
// The initial log should be sent first; update the persisted storage to
// verify.
log_manager.PersistUnsentLogs();
EXPECT_EQ(0U, pref_service.TypeCount(MetricsLog::INITIAL_STABILITY_LOG));
EXPECT_EQ(2U, pref_service.TypeCount(MetricsLog::ONGOING_LOG));
// Handle the first ongoing log.
log_manager.StageNextLogForUpload();
log_manager.DiscardStagedLog();
EXPECT_TRUE(log_manager.has_unsent_logs());
// Handle the last log.
log_manager.StageNextLogForUpload();
log_manager.DiscardStagedLog();
EXPECT_FALSE(log_manager.has_unsent_logs());
// Nothing should have changed "on disk" since PersistUnsentLogs hasn't been
// called again.
EXPECT_EQ(2U, pref_service.TypeCount(MetricsLog::ONGOING_LOG));
// Persist, and make sure nothing is left.
log_manager.PersistUnsentLogs();
EXPECT_EQ(0U, pref_service.TypeCount(MetricsLog::INITIAL_STABILITY_LOG));
EXPECT_EQ(0U, pref_service.TypeCount(MetricsLog::ONGOING_LOG));
}
}
TEST(MetricsLogManagerTest, StoreStagedLogTypes) {
TestMetricsServiceClient client;
// Ensure that types are preserved when storing staged logs.
{
TestLogPrefService pref_service;
MetricsLogManager log_manager(&pref_service, 0);
log_manager.LoadPersistedUnsentLogs();
log_manager.BeginLoggingWithLog(make_scoped_ptr(new MetricsLog(
"id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)));
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
log_manager.StoreStagedLogAsUnsent(PersistedLogs::NORMAL_STORE);
log_manager.PersistUnsentLogs();
EXPECT_EQ(0U, pref_service.TypeCount(MetricsLog::INITIAL_STABILITY_LOG));
EXPECT_EQ(1U, pref_service.TypeCount(MetricsLog::ONGOING_LOG));
}
{
TestLogPrefService pref_service;
MetricsLogManager log_manager(&pref_service, 0);
log_manager.LoadPersistedUnsentLogs();
log_manager.BeginLoggingWithLog(make_scoped_ptr(new MetricsLog(
"id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service)));
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
log_manager.StoreStagedLogAsUnsent(PersistedLogs::NORMAL_STORE);
log_manager.PersistUnsentLogs();
EXPECT_EQ(1U, pref_service.TypeCount(MetricsLog::INITIAL_STABILITY_LOG));
EXPECT_EQ(0U, pref_service.TypeCount(MetricsLog::ONGOING_LOG));
}
}
TEST(MetricsLogManagerTest, LargeLogDiscarding) {
TestMetricsServiceClient client;
TestLogPrefService pref_service;
// Set the size threshold very low, to verify that it's honored.
MetricsLogManager log_manager(&pref_service, 1);
log_manager.LoadPersistedUnsentLogs();
log_manager.BeginLoggingWithLog(make_scoped_ptr(new MetricsLog(
"id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service)));
log_manager.FinishCurrentLog();
log_manager.BeginLoggingWithLog(make_scoped_ptr(new MetricsLog(
"id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)));
log_manager.FinishCurrentLog();
// Only the ongoing log should be written out, due to the threshold.
log_manager.PersistUnsentLogs();
EXPECT_EQ(1U, pref_service.TypeCount(MetricsLog::INITIAL_STABILITY_LOG));
EXPECT_EQ(0U, pref_service.TypeCount(MetricsLog::ONGOING_LOG));
}
TEST(MetricsLogManagerTest, ProvisionalStoreStandardFlow) {
TestMetricsServiceClient client;
// Ensure that provisional store works, and discards the correct log.
{
TestLogPrefService pref_service;
MetricsLogManager log_manager(&pref_service, 0);
log_manager.LoadPersistedUnsentLogs();
log_manager.BeginLoggingWithLog(make_scoped_ptr(new MetricsLog(
"id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service)));
log_manager.FinishCurrentLog();
log_manager.BeginLoggingWithLog(make_scoped_ptr(new MetricsLog(
"id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)));
log_manager.StageNextLogForUpload();
log_manager.StoreStagedLogAsUnsent(PersistedLogs::PROVISIONAL_STORE);
log_manager.FinishCurrentLog();
log_manager.DiscardLastProvisionalStore();
log_manager.PersistUnsentLogs();
EXPECT_EQ(0U, pref_service.TypeCount(MetricsLog::INITIAL_STABILITY_LOG));
EXPECT_EQ(1U, pref_service.TypeCount(MetricsLog::ONGOING_LOG));
}
}
TEST(MetricsLogManagerTest, ProvisionalStoreNoop) {
TestMetricsServiceClient client;
// Ensure that trying to drop a sent log is a no-op, even if another log has
// since been staged.
{
TestLogPrefService pref_service;
MetricsLogManager log_manager(&pref_service, 0);
log_manager.LoadPersistedUnsentLogs();
log_manager.BeginLoggingWithLog(make_scoped_ptr(new MetricsLog(
"id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)));
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
log_manager.StoreStagedLogAsUnsent(PersistedLogs::PROVISIONAL_STORE);
log_manager.StageNextLogForUpload();
log_manager.DiscardStagedLog();
log_manager.BeginLoggingWithLog(make_scoped_ptr(new MetricsLog(
"id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)));
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
log_manager.StoreStagedLogAsUnsent(PersistedLogs::NORMAL_STORE);
log_manager.DiscardLastProvisionalStore();
log_manager.PersistUnsentLogs();
EXPECT_EQ(1U, pref_service.TypeCount(MetricsLog::ONGOING_LOG));
}
// Ensure that trying to drop more than once is a no-op
{
TestLogPrefService pref_service;
MetricsLogManager log_manager(&pref_service, 0);
log_manager.LoadPersistedUnsentLogs();
log_manager.BeginLoggingWithLog(make_scoped_ptr(new MetricsLog(
"id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)));
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
log_manager.StoreStagedLogAsUnsent(PersistedLogs::NORMAL_STORE);
log_manager.BeginLoggingWithLog(make_scoped_ptr(new MetricsLog(
"id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)));
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
log_manager.StoreStagedLogAsUnsent(PersistedLogs::PROVISIONAL_STORE);
log_manager.DiscardLastProvisionalStore();
log_manager.DiscardLastProvisionalStore();
log_manager.PersistUnsentLogs();
EXPECT_EQ(1U, pref_service.TypeCount(MetricsLog::ONGOING_LOG));
}
}
} // namespace metrics