blob: dc58454c0feb476aac7b01783fb39c5e0371412e [file] [log] [blame]
// Copyright (c) 2012 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 <string>
#include <utility>
#include <vector>
#include "chrome/common/metrics/metrics_log_base.h"
#include "chrome/common/metrics/metrics_log_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
typedef MetricsLogManager::SerializedLog SerializedLog;
namespace {
class MetricsLogManagerTest : public testing::Test {
};
// Dummy serializer that just stores logs in memory.
class DummyLogSerializer : public MetricsLogManager::LogSerializer {
public:
virtual void SerializeLogs(const std::vector<SerializedLog>& logs,
MetricsLogManager::LogType log_type) {
persisted_logs_[log_type] = logs;
}
virtual void DeserializeLogs(MetricsLogManager::LogType log_type,
std::vector<SerializedLog>* logs) {
ASSERT_NE(static_cast<void*>(NULL), logs);
*logs = persisted_logs_[log_type];
}
// Returns the number of logs of the given type.
size_t TypeCount(MetricsLogManager::LogType log_type) {
return persisted_logs_[log_type].size();
}
// In-memory "persitent storage".
std::vector<SerializedLog> persisted_logs_[2];
};
} // namespace
TEST(MetricsLogManagerTest, StandardFlow) {
MetricsLogManager log_manager;
// 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.
MetricsLogBase* initial_log = new MetricsLogBase("id", 0, "version");
log_manager.BeginLoggingWithLog(initial_log, MetricsLogManager::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());
MetricsLogBase* second_log = new MetricsLogBase("id", 0, "version");
log_manager.BeginLoggingWithLog(second_log, MetricsLogManager::ONGOING_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_text().empty());
log_manager.DiscardStagedLogXml();
log_manager.DiscardStagedLogProto();
EXPECT_EQ(second_log, log_manager.current_log());
EXPECT_FALSE(log_manager.has_staged_log());
EXPECT_FALSE(log_manager.has_unsent_logs());
EXPECT_TRUE(log_manager.staged_log_text().empty());
EXPECT_FALSE(log_manager.has_unsent_logs());
}
TEST(MetricsLogManagerTest, AbandonedLog) {
MetricsLogManager log_manager;
MetricsLogBase* dummy_log = new MetricsLogBase("id", 0, "version");
log_manager.BeginLoggingWithLog(dummy_log, MetricsLogManager::INITIAL_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) {
MetricsLogManager log_manager;
MetricsLogBase* ongoing_log = new MetricsLogBase("id", 0, "version");
MetricsLogBase* temp_log = new MetricsLogBase("id", 0, "version");
log_manager.BeginLoggingWithLog(ongoing_log, MetricsLogManager::ONGOING_LOG);
EXPECT_EQ(ongoing_log, log_manager.current_log());
log_manager.PauseCurrentLog();
EXPECT_EQ(NULL, log_manager.current_log());
log_manager.BeginLoggingWithLog(temp_log, MetricsLogManager::INITIAL_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.DiscardStagedLogXml();
log_manager.DiscardStagedLogProto();
EXPECT_FALSE(log_manager.has_unsent_logs());
}
TEST(MetricsLogManagerTest, InterjectedLogPreservesType) {
MetricsLogManager log_manager;
MetricsLogBase* ongoing_log = new MetricsLogBase("id", 0, "version");
MetricsLogBase* temp_log = new MetricsLogBase("id", 0, "version");
log_manager.BeginLoggingWithLog(ongoing_log, MetricsLogManager::ONGOING_LOG);
log_manager.PauseCurrentLog();
log_manager.BeginLoggingWithLog(temp_log, MetricsLogManager::INITIAL_LOG);
log_manager.FinishCurrentLog();
log_manager.ResumePausedLog();
log_manager.StageNextLogForUpload();
log_manager.DiscardStagedLogXml();
log_manager.DiscardStagedLogProto();
// Verify that the remaining log (which is the original ongoing log) still
// has the right type.
DummyLogSerializer* serializer = new DummyLogSerializer;
log_manager.set_log_serializer(serializer);
log_manager.FinishCurrentLog();
log_manager.PersistUnsentLogs();
EXPECT_EQ(0U, serializer->TypeCount(MetricsLogManager::INITIAL_LOG));
EXPECT_EQ(1U, serializer->TypeCount(MetricsLogManager::ONGOING_LOG));
}
TEST(MetricsLogManagerTest, StoreAndLoad) {
std::vector<SerializedLog> initial_logs;
std::vector<SerializedLog> ongoing_logs;
// 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;
DummyLogSerializer* serializer = new DummyLogSerializer;
log_manager.set_log_serializer(serializer);
// Simulate a log having already been unsent from a previous session.
SerializedLog log = {"xml", "proto"};
serializer->persisted_logs_[MetricsLogManager::ONGOING_LOG].push_back(log);
EXPECT_FALSE(log_manager.has_unsent_logs());
log_manager.LoadPersistedUnsentLogs();
EXPECT_TRUE(log_manager.has_unsent_logs());
MetricsLogBase* log1 = new MetricsLogBase("id", 0, "version");
MetricsLogBase* log2 = new MetricsLogBase("id", 0, "version");
log_manager.BeginLoggingWithLog(log1, MetricsLogManager::INITIAL_LOG);
log_manager.FinishCurrentLog();
log_manager.BeginLoggingWithLog(log2, MetricsLogManager::ONGOING_LOG);
log_manager.StageNextLogForUpload();
log_manager.StoreStagedLogAsUnsent(MetricsLogManager::NORMAL_STORE);
log_manager.FinishCurrentLog();
// Nothing should be written out until PersistUnsentLogs is called.
EXPECT_EQ(0U, serializer->TypeCount(MetricsLogManager::INITIAL_LOG));
EXPECT_EQ(1U, serializer->TypeCount(MetricsLogManager::ONGOING_LOG));
log_manager.PersistUnsentLogs();
EXPECT_EQ(1U, serializer->TypeCount(MetricsLogManager::INITIAL_LOG));
EXPECT_EQ(2U, serializer->TypeCount(MetricsLogManager::ONGOING_LOG));
// Save the logs to transfer over to a new serializer (since log_manager
// owns |serializer|, so it's about to go away.
initial_logs = serializer->persisted_logs_[MetricsLogManager::INITIAL_LOG];
ongoing_logs = serializer->persisted_logs_[MetricsLogManager::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;
DummyLogSerializer* serializer = new DummyLogSerializer;
serializer->persisted_logs_[MetricsLogManager::INITIAL_LOG] = initial_logs;
serializer->persisted_logs_[MetricsLogManager::ONGOING_LOG] = ongoing_logs;
log_manager.set_log_serializer(serializer);
log_manager.LoadPersistedUnsentLogs();
EXPECT_TRUE(log_manager.has_unsent_logs());
log_manager.StageNextLogForUpload();
log_manager.DiscardStagedLogXml();
log_manager.DiscardStagedLogProto();
// The initial log should be sent first; update the persisted storage to
// verify.
log_manager.PersistUnsentLogs();
EXPECT_EQ(0U, serializer->TypeCount(MetricsLogManager::INITIAL_LOG));
EXPECT_EQ(2U, serializer->TypeCount(MetricsLogManager::ONGOING_LOG));
// Handle the first ongoing log.
log_manager.StageNextLogForUpload();
log_manager.DiscardStagedLogXml();
log_manager.DiscardStagedLogProto();
EXPECT_TRUE(log_manager.has_unsent_logs());
// Handle the last log.
log_manager.StageNextLogForUpload();
log_manager.DiscardStagedLogXml();
log_manager.DiscardStagedLogProto();
EXPECT_FALSE(log_manager.has_unsent_logs());
// Nothing should have changed "on disk" since PersistUnsentLogs hasn't been
// called again.
EXPECT_EQ(2U, serializer->TypeCount(MetricsLogManager::ONGOING_LOG));
// Persist, and make sure nothing is left.
log_manager.PersistUnsentLogs();
EXPECT_EQ(0U, serializer->TypeCount(MetricsLogManager::INITIAL_LOG));
EXPECT_EQ(0U, serializer->TypeCount(MetricsLogManager::ONGOING_LOG));
}
}
TEST(MetricsLogManagerTest, StoreStagedLogTypes) {
// Ensure that types are preserved when storing staged logs.
{
MetricsLogManager log_manager;
DummyLogSerializer* serializer = new DummyLogSerializer;
log_manager.set_log_serializer(serializer);
MetricsLogBase* log = new MetricsLogBase("id", 0, "version");
log_manager.BeginLoggingWithLog(log, MetricsLogManager::ONGOING_LOG);
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
log_manager.StoreStagedLogAsUnsent(MetricsLogManager::NORMAL_STORE);
log_manager.PersistUnsentLogs();
EXPECT_EQ(0U, serializer->TypeCount(MetricsLogManager::INITIAL_LOG));
EXPECT_EQ(1U, serializer->TypeCount(MetricsLogManager::ONGOING_LOG));
}
{
MetricsLogManager log_manager;
DummyLogSerializer* serializer = new DummyLogSerializer;
log_manager.set_log_serializer(serializer);
MetricsLogBase* log = new MetricsLogBase("id", 0, "version");
log_manager.BeginLoggingWithLog(log, MetricsLogManager::INITIAL_LOG);
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
log_manager.StoreStagedLogAsUnsent(MetricsLogManager::NORMAL_STORE);
log_manager.PersistUnsentLogs();
EXPECT_EQ(1U, serializer->TypeCount(MetricsLogManager::INITIAL_LOG));
EXPECT_EQ(0U, serializer->TypeCount(MetricsLogManager::ONGOING_LOG));
}
}
TEST(MetricsLogManagerTest, LargeLogDiscarding) {
MetricsLogManager log_manager;
DummyLogSerializer* serializer = new DummyLogSerializer;
log_manager.set_log_serializer(serializer);
// Set the size threshold very low, to verify that it's honored.
log_manager.set_max_ongoing_log_store_size(1);
MetricsLogBase* log1 = new MetricsLogBase("id", 0, "version");
MetricsLogBase* log2 = new MetricsLogBase("id", 0, "version");
log_manager.BeginLoggingWithLog(log1, MetricsLogManager::INITIAL_LOG);
log_manager.FinishCurrentLog();
log_manager.BeginLoggingWithLog(log2, MetricsLogManager::ONGOING_LOG);
log_manager.FinishCurrentLog();
// Only the ongoing log should be written out, due to the threshold.
log_manager.PersistUnsentLogs();
EXPECT_EQ(1U, serializer->TypeCount(MetricsLogManager::INITIAL_LOG));
EXPECT_EQ(0U, serializer->TypeCount(MetricsLogManager::ONGOING_LOG));
}
TEST(MetricsLogManagerTest, ProvisionalStoreStandardFlow) {
// Ensure that provisional store works, and discards the correct log.
{
MetricsLogManager log_manager;
MetricsLogBase* log1 = new MetricsLogBase("id", 0, "version");
MetricsLogBase* log2 = new MetricsLogBase("id", 0, "version");
log_manager.BeginLoggingWithLog(log1, MetricsLogManager::INITIAL_LOG);
log_manager.FinishCurrentLog();
log_manager.BeginLoggingWithLog(log2, MetricsLogManager::ONGOING_LOG);
log_manager.StageNextLogForUpload();
log_manager.StoreStagedLogAsUnsent(MetricsLogManager::PROVISIONAL_STORE);
log_manager.FinishCurrentLog();
log_manager.DiscardLastProvisionalStore();
DummyLogSerializer* serializer = new DummyLogSerializer;
log_manager.set_log_serializer(serializer);
log_manager.PersistUnsentLogs();
EXPECT_EQ(0U, serializer->TypeCount(MetricsLogManager::INITIAL_LOG));
EXPECT_EQ(1U, serializer->TypeCount(MetricsLogManager::ONGOING_LOG));
}
}
TEST(MetricsLogManagerTest, ProvisionalStoreNoop) {
// Ensure that trying to drop a sent log is a no-op, even if another log has
// since been staged.
{
MetricsLogManager log_manager;
MetricsLogBase* log1 = new MetricsLogBase("id", 0, "version");
MetricsLogBase* log2 = new MetricsLogBase("id", 0, "version");
log_manager.BeginLoggingWithLog(log1, MetricsLogManager::ONGOING_LOG);
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
log_manager.StoreStagedLogAsUnsent(MetricsLogManager::PROVISIONAL_STORE);
log_manager.StageNextLogForUpload();
log_manager.DiscardStagedLogXml();
log_manager.DiscardStagedLogProto();
log_manager.BeginLoggingWithLog(log2, MetricsLogManager::ONGOING_LOG);
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
log_manager.StoreStagedLogAsUnsent(MetricsLogManager::NORMAL_STORE);
log_manager.DiscardLastProvisionalStore();
DummyLogSerializer* serializer = new DummyLogSerializer;
log_manager.set_log_serializer(serializer);
log_manager.PersistUnsentLogs();
EXPECT_EQ(1U, serializer->TypeCount(MetricsLogManager::ONGOING_LOG));
}
// Ensure that trying to drop more than once is a no-op
{
MetricsLogManager log_manager;
MetricsLogBase* log1 = new MetricsLogBase("id", 0, "version");
MetricsLogBase* log2 = new MetricsLogBase("id", 0, "version");
log_manager.BeginLoggingWithLog(log1, MetricsLogManager::ONGOING_LOG);
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
log_manager.StoreStagedLogAsUnsent(MetricsLogManager::NORMAL_STORE);
log_manager.BeginLoggingWithLog(log2, MetricsLogManager::ONGOING_LOG);
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
log_manager.StoreStagedLogAsUnsent(MetricsLogManager::PROVISIONAL_STORE);
log_manager.DiscardLastProvisionalStore();
log_manager.DiscardLastProvisionalStore();
DummyLogSerializer* serializer = new DummyLogSerializer;
log_manager.set_log_serializer(serializer);
log_manager.PersistUnsentLogs();
EXPECT_EQ(1U, serializer->TypeCount(MetricsLogManager::ONGOING_LOG));
}
}
// Test that discarding just the XML log, then the protobuf log, works.
TEST(MetricsLogManagerTest, DiscardXmlLogFirst) {
MetricsLogManager log_manager;
EXPECT_FALSE(log_manager.has_staged_log());
EXPECT_FALSE(log_manager.has_staged_log_xml());
EXPECT_FALSE(log_manager.has_staged_log_proto());
MetricsLogBase* initial_log = new MetricsLogBase("id", 0, "version");
log_manager.BeginLoggingWithLog(initial_log, MetricsLogManager::INITIAL_LOG);
log_manager.FinishCurrentLog();
EXPECT_FALSE(log_manager.has_staged_log());
EXPECT_FALSE(log_manager.has_staged_log_xml());
EXPECT_FALSE(log_manager.has_staged_log_proto());
log_manager.StageNextLogForUpload();
EXPECT_TRUE(log_manager.has_staged_log());
EXPECT_TRUE(log_manager.has_staged_log_xml());
EXPECT_TRUE(log_manager.has_staged_log_proto());
EXPECT_FALSE(log_manager.staged_log_text().empty());
log_manager.DiscardStagedLogXml();
EXPECT_TRUE(log_manager.has_staged_log());
EXPECT_FALSE(log_manager.has_staged_log_xml());
EXPECT_TRUE(log_manager.has_staged_log_proto());
EXPECT_FALSE(log_manager.staged_log_text().empty());
log_manager.DiscardStagedLogProto();
EXPECT_FALSE(log_manager.has_staged_log());
EXPECT_FALSE(log_manager.has_staged_log_xml());
EXPECT_FALSE(log_manager.has_staged_log_proto());
EXPECT_TRUE(log_manager.staged_log_text().empty());
}
// Test that discarding just the protobuf log, then the XML log, works.
TEST(MetricsLogManagerTest, DiscardProtoLogFirst) {
MetricsLogManager log_manager;
EXPECT_FALSE(log_manager.has_staged_log());
EXPECT_FALSE(log_manager.has_staged_log_xml());
EXPECT_FALSE(log_manager.has_staged_log_proto());
MetricsLogBase* initial_log = new MetricsLogBase("id", 0, "version");
log_manager.BeginLoggingWithLog(initial_log, MetricsLogManager::INITIAL_LOG);
log_manager.FinishCurrentLog();
EXPECT_FALSE(log_manager.has_staged_log());
EXPECT_FALSE(log_manager.has_staged_log_xml());
EXPECT_FALSE(log_manager.has_staged_log_proto());
log_manager.StageNextLogForUpload();
EXPECT_TRUE(log_manager.has_staged_log());
EXPECT_TRUE(log_manager.has_staged_log_xml());
EXPECT_TRUE(log_manager.has_staged_log_proto());
EXPECT_FALSE(log_manager.staged_log_text().empty());
log_manager.DiscardStagedLogProto();
EXPECT_TRUE(log_manager.has_staged_log());
EXPECT_TRUE(log_manager.has_staged_log_xml());
EXPECT_FALSE(log_manager.has_staged_log_proto());
EXPECT_FALSE(log_manager.staged_log_text().empty());
log_manager.DiscardStagedLogXml();
EXPECT_FALSE(log_manager.has_staged_log());
EXPECT_FALSE(log_manager.has_staged_log_xml());
EXPECT_FALSE(log_manager.has_staged_log_proto());
EXPECT_TRUE(log_manager.staged_log_text().empty());
}