summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorasvitkine@chromium.org <asvitkine@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-11 16:31:24 +0000
committerasvitkine@chromium.org <asvitkine@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-06-11 16:31:24 +0000
commit9706e1b7596203edee3e1f0aaff56e8493ff9f20 (patch)
tree0ea75e500862c1d5a09240ec43cd28aa7ab15c7e
parent92218694a696e560c7127e11690ca38e53213a4d (diff)
downloadchromium_src-9706e1b7596203edee3e1f0aaff56e8493ff9f20.zip
chromium_src-9706e1b7596203edee3e1f0aaff56e8493ff9f20.tar.gz
chromium_src-9706e1b7596203edee3e1f0aaff56e8493ff9f20.tar.bz2
Make MetricsService save compressed logs to local state.
Previously, it would persist logs in uncompressed form. This CL changes the code to compress logs before they're saved. A new pair of prefs is introduced for storing these logs, while reading from the old pref is still maintained to not lose old logs. Additionally, this makes the metrics log discard limit (currently 50k) be checked against the compressed size rather than the uncompressed size. Simplifies the format used to store logs, now simply storing the compressed log bytes and corresponding hash for each log. The size and checksum fields are removed. (Size was redundant while the checksum is not no longer necessary now that we store the log hash.) Deletes some tests that inspected the actual pref values are removed, in favor of tests that check that serializing and de-serializing works as expected. Finally, also introduces GzipUncompress() that is needed for tests that inspect log manager logs. BUG=382076 TBR=agl@chromium.org Review URL: https://codereview.chromium.org/318203004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@276429 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/metrics/metrics_service_unittest.cc7
-rw-r--r--components/components_tests.gyp2
-rw-r--r--components/metrics.gypi8
-rw-r--r--components/metrics/DEPS1
-rw-r--r--components/metrics/compression_utils.cc156
-rw-r--r--components/metrics/compression_utils.h20
-rw-r--r--components/metrics/compression_utils_unittest.cc (renamed from components/metrics/net/compression_utils_unittest.cc)42
-rw-r--r--components/metrics/metrics_log_manager.cc18
-rw-r--r--components/metrics/metrics_log_manager.h12
-rw-r--r--components/metrics/metrics_log_manager_unittest.cc29
-rw-r--r--components/metrics/metrics_log_uploader.h7
-rw-r--r--components/metrics/metrics_pref_names.cc4
-rw-r--r--components/metrics/metrics_pref_names.h2
-rw-r--r--components/metrics/metrics_service.cc2
-rw-r--r--components/metrics/net/DEPS1
-rw-r--r--components/metrics/net/compression_utils.cc96
-rw-r--r--components/metrics/net/compression_utils.h17
-rw-r--r--components/metrics/net/net_metrics_log_uploader.cc17
-rw-r--r--components/metrics/net/net_metrics_log_uploader.h2
-rw-r--r--components/metrics/persisted_logs.cc134
-rw-r--r--components/metrics/persisted_logs.h54
-rw-r--r--components/metrics/persisted_logs_unittest.cc298
22 files changed, 501 insertions, 428 deletions
diff --git a/chrome/browser/metrics/metrics_service_unittest.cc b/chrome/browser/metrics/metrics_service_unittest.cc
index b75951d..ceb3328 100644
--- a/chrome/browser/metrics/metrics_service_unittest.cc
+++ b/chrome/browser/metrics/metrics_service_unittest.cc
@@ -13,6 +13,7 @@
#include "chrome/common/pref_names.h"
#include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h"
+#include "components/metrics/compression_utils.h"
#include "components/metrics/metrics_log.h"
#include "components/metrics/metrics_service_observer.h"
#include "components/metrics/metrics_state_manager.h"
@@ -209,8 +210,12 @@ TEST_F(MetricsServiceTest, InitialStabilityLogAfterCrash) {
log_manager->StageNextLogForUpload();
EXPECT_TRUE(log_manager->has_staged_log());
+ std::string uncompressed_log;
+ EXPECT_TRUE(metrics::GzipUncompress(log_manager->staged_log(),
+ &uncompressed_log));
+
metrics::ChromeUserMetricsExtension uma_log;
- EXPECT_TRUE(uma_log.ParseFromString(log_manager->staged_log()));
+ EXPECT_TRUE(uma_log.ParseFromString(uncompressed_log));
EXPECT_TRUE(uma_log.has_client_id());
EXPECT_TRUE(uma_log.has_session_id());
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index 23de5ed..4dc8c92 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -111,13 +111,13 @@
'keyed_service/content/browser_context_dependency_manager_unittest.cc',
'keyed_service/core/dependency_graph_unittest.cc',
'language_usage_metrics/language_usage_metrics_unittest.cc',
+ 'metrics/compression_utils_unittest.cc',
'metrics/machine_id_provider_win_unittest.cc',
'metrics/metrics_hashes_unittest.cc',
'metrics/metrics_log_manager_unittest.cc',
'metrics/metrics_log_unittest.cc',
'metrics/metrics_reporting_scheduler_unittest.cc',
'metrics/metrics_state_manager_unittest.cc',
- 'metrics/net/compression_utils_unittest.cc',
'metrics/persisted_logs_unittest.cc',
'navigation_interception/intercept_navigation_resource_throttle_unittest.cc',
'network_time/network_time_tracker_unittest.cc',
diff --git a/components/metrics.gypi b/components/metrics.gypi
index 24fd27c..decc23f8 100644
--- a/components/metrics.gypi
+++ b/components/metrics.gypi
@@ -12,11 +12,13 @@
],
'dependencies': [
'../base/base.gyp:base',
+ '../third_party/zlib/zlib.gyp:zlib',
'component_metrics_proto',
'variations',
],
'sources': [
- 'metrics/metrics_provider.h',
+ 'metrics/compression_utils.cc',
+ 'metrics/compression_utils.h',
'metrics/cloned_install_detector.cc',
'metrics/cloned_install_detector.h',
'metrics/machine_id_provider.h',
@@ -32,6 +34,7 @@
'metrics/metrics_log_manager.h',
'metrics/metrics_pref_names.cc',
'metrics/metrics_pref_names.h',
+ 'metrics/metrics_provider.h',
'metrics/metrics_reporting_scheduler.cc',
'metrics/metrics_reporting_scheduler.h',
'metrics/metrics_service.cc',
@@ -67,12 +70,9 @@
],
'dependencies': [
'../net/net.gyp:net',
- '../third_party/zlib/zlib.gyp:zlib',
'metrics',
],
'sources': [
- 'metrics/net/compression_utils.cc',
- 'metrics/net/compression_utils.h',
'metrics/net/net_metrics_log_uploader.cc',
'metrics/net/net_metrics_log_uploader.h',
],
diff --git a/components/metrics/DEPS b/components/metrics/DEPS
index 6f42ac7..dfb4de1 100644
--- a/components/metrics/DEPS
+++ b/components/metrics/DEPS
@@ -1,4 +1,5 @@
include_rules = [
"+components/variations",
"-net",
+ "+third_party/zlib",
]
diff --git a/components/metrics/compression_utils.cc b/components/metrics/compression_utils.cc
new file mode 100644
index 0000000..d90a67a
--- /dev/null
+++ b/components/metrics/compression_utils.cc
@@ -0,0 +1,156 @@
+// 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/compression_utils.h"
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/sys_byteorder.h"
+#include "third_party/zlib/zlib.h"
+
+namespace {
+
+// The difference in bytes between a zlib header and a gzip header.
+const size_t kGzipZlibHeaderDifferenceBytes = 16;
+
+// Pass an integer greater than the following get a gzip header instead of a
+// zlib header when calling deflateInit2() and inflateInit2().
+const int kWindowBitsToGetGzipHeader = 16;
+
+// This describes the amount of memory zlib uses to compress data. It can go
+// from 1 to 9, with 8 being the default. For details, see:
+// http://www.zlib.net/manual.html (search for memLevel).
+const int kZlibMemoryLevel = 8;
+
+// This code is taken almost verbatim from third_party/zlib/compress.c. The only
+// difference is deflateInit2() is called which sets the window bits to be > 16.
+// That causes a gzip header to be emitted rather than a zlib header.
+int GzipCompressHelper(Bytef* dest,
+ uLongf* dest_length,
+ const Bytef* source,
+ uLong source_length) {
+ z_stream stream;
+
+ stream.next_in = bit_cast<Bytef*>(source);
+ stream.avail_in = static_cast<uInt>(source_length);
+ stream.next_out = dest;
+ stream.avail_out = static_cast<uInt>(*dest_length);
+ if (static_cast<uLong>(stream.avail_out) != *dest_length)
+ return Z_BUF_ERROR;
+
+ stream.zalloc = static_cast<alloc_func>(0);
+ stream.zfree = static_cast<free_func>(0);
+ stream.opaque = static_cast<voidpf>(0);
+
+ gz_header gzip_header;
+ memset(&gzip_header, 0, sizeof(gzip_header));
+ int err = deflateInit2(&stream,
+ Z_DEFAULT_COMPRESSION,
+ Z_DEFLATED,
+ MAX_WBITS + kWindowBitsToGetGzipHeader,
+ kZlibMemoryLevel,
+ Z_DEFAULT_STRATEGY);
+ if (err != Z_OK)
+ return err;
+
+ err = deflateSetHeader(&stream, &gzip_header);
+ if (err != Z_OK)
+ return err;
+
+ err = deflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ deflateEnd(&stream);
+ return err == Z_OK ? Z_BUF_ERROR : err;
+ }
+ *dest_length = stream.total_out;
+
+ err = deflateEnd(&stream);
+ return err;
+}
+
+// This code is taken almost verbatim from third_party/zlib/uncompr.c. The only
+// difference is inflateInit2() is called which sets the window bits to be > 16.
+// That causes a gzip header to be parsed rather than a zlib header.
+int GzipUncompressHelper(Bytef* dest,
+ uLongf* dest_length,
+ const Bytef* source,
+ uLong source_length) {
+ z_stream stream;
+
+ stream.next_in = bit_cast<Bytef*>(source);
+ stream.avail_in = static_cast<uInt>(source_length);
+ if (static_cast<uLong>(stream.avail_in) != source_length)
+ return Z_BUF_ERROR;
+
+ stream.next_out = dest;
+ stream.avail_out = static_cast<uInt>(*dest_length);
+ if (static_cast<uLong>(stream.avail_out) != *dest_length)
+ return Z_BUF_ERROR;
+
+ stream.zalloc = static_cast<alloc_func>(0);
+ stream.zfree = static_cast<free_func>(0);
+
+ int err = inflateInit2(&stream, MAX_WBITS + kWindowBitsToGetGzipHeader);
+ if (err != Z_OK)
+ return err;
+
+ err = inflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ inflateEnd(&stream);
+ if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
+ return Z_DATA_ERROR;
+ return err;
+ }
+ *dest_length = stream.total_out;
+
+ err = inflateEnd(&stream);
+ return err;
+}
+
+// Returns the uncompressed size from GZIP-compressed |compressed_data|.
+uint32 GetUncompressedSize(const std::string& compressed_data) {
+ // The uncompressed size is stored in the last 4 bytes of |input| in LE.
+ uint32 size;
+ if (compressed_data.length() < sizeof(size))
+ return 0;
+ memcpy(&size, &compressed_data[compressed_data.length() - sizeof(size)],
+ sizeof(size));
+ return base::ByteSwapToLE32(size);
+}
+
+} // namespace
+
+namespace metrics {
+
+bool GzipCompress(const std::string& input, std::string* output) {
+ const uLongf input_size = static_cast<uLongf>(input.size());
+ std::vector<Bytef> compressed_data(kGzipZlibHeaderDifferenceBytes +
+ compressBound(input_size));
+
+ uLongf compressed_size = static_cast<uLongf>(compressed_data.size());
+ if (GzipCompressHelper(&compressed_data.front(),
+ &compressed_size,
+ bit_cast<const Bytef*>(input.data()),
+ input_size) != Z_OK) {
+ return false;
+ }
+
+ compressed_data.resize(compressed_size);
+ output->assign(compressed_data.begin(), compressed_data.end());
+ DCHECK_EQ(input.size(), GetUncompressedSize(*output));
+ return true;
+}
+
+bool GzipUncompress(const std::string& input, std::string* output) {
+ output->resize(GetUncompressedSize(input));
+ uLongf uncompressed_size = static_cast<uLongf>(output->length());
+ return GzipUncompressHelper(bit_cast<Bytef*>(output->data()),
+ &uncompressed_size,
+ bit_cast<const Bytef*>(input.data()),
+ static_cast<uLongf>(input.length())) == Z_OK;
+}
+
+} // namespace metrics
diff --git a/components/metrics/compression_utils.h b/components/metrics/compression_utils.h
new file mode 100644
index 0000000..32b21e5
--- /dev/null
+++ b/components/metrics/compression_utils.h
@@ -0,0 +1,20 @@
+// 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.
+
+#ifndef COMPONENTS_METRICS_COMPRESSION_UTILS_H_
+#define COMPONENTS_METRICS_COMPRESSION_UTILS_H_
+
+#include <string>
+
+namespace metrics {
+
+// Compresses the data in |input| using gzip, storing the result in |output|.
+bool GzipCompress(const std::string& input, std::string* output);
+
+// Uncompresses the data in |input| using gzip, storing the result in |output|.
+bool GzipUncompress(const std::string& input, std::string* output);
+
+} // namespace metrics
+
+#endif // COMPONENTS_METRICS_COMPRESSION_UTILS_H_
diff --git a/components/metrics/net/compression_utils_unittest.cc b/components/metrics/compression_utils_unittest.cc
index 05305d4..797a7fb 100644
--- a/components/metrics/net/compression_utils_unittest.cc
+++ b/components/metrics/compression_utils_unittest.cc
@@ -2,14 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/metrics/net/compression_utils.h"
+#include "components/metrics/compression_utils.h"
#include <string>
-#include "base/base_paths.h"
#include "base/basictypes.h"
-#include "base/file_util.h"
-#include "base/path_service.h"
+#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace metrics {
@@ -32,11 +30,6 @@ const uint8 kCompressedData[] =
0x2f, 0xca, 0x49, 0x01, 0x00, 0x85, 0x11, 0x4a, 0x0d,
0x0b, 0x00, 0x00, 0x00};
-// Re-enable C4309.
-#if defined(OS_WIN)
-#pragma warning( default: 4309 )
-#endif
-
} // namespace
TEST(CompressionUtilsTest, GzipCompression) {
@@ -49,4 +42,35 @@ TEST(CompressionUtilsTest, GzipCompression) {
EXPECT_EQ(golden_compressed_data, compressed_data);
}
+TEST(CompressionUtilsTest, GzipUncompression) {
+ std::string compressed_data(reinterpret_cast<const char*>(kCompressedData),
+ arraysize(kCompressedData));
+
+ std::string uncompressed_data;
+ EXPECT_TRUE(GzipUncompress(compressed_data, &uncompressed_data));
+
+ std::string golden_data(reinterpret_cast<const char*>(kData),
+ arraysize(kData));
+ EXPECT_EQ(golden_data, uncompressed_data);
+}
+
+// Checks that compressing/decompressing input > 256 bytes works as expected.
+TEST(CompressionUtilsTest, LargeInput) {
+ const size_t kSize = 32 * 1024;
+
+ // Generate a data string of |kSize| for testing.
+ std::string data;
+ data.resize(kSize);
+ for (size_t i = 0; i < kSize; ++i)
+ data[i] = static_cast<char>(i & 0xFF);
+
+ std::string compressed_data;
+ EXPECT_TRUE(GzipCompress(data, &compressed_data));
+
+ std::string uncompressed_data;
+ EXPECT_TRUE(GzipUncompress(compressed_data, &uncompressed_data));
+
+ EXPECT_EQ(data, uncompressed_data);
+}
+
} // namespace metrics
diff --git a/components/metrics/metrics_log_manager.cc b/components/metrics/metrics_log_manager.cc
index d34b7b8..6b0525d 100644
--- a/components/metrics/metrics_log_manager.cc
+++ b/components/metrics/metrics_log_manager.cc
@@ -43,11 +43,13 @@ MetricsLogManager::MetricsLogManager(PrefService* local_state,
: unsent_logs_loaded_(false),
initial_log_queue_(local_state,
prefs::kMetricsInitialLogs,
+ prefs::kMetricsInitialLogsOld,
kInitialLogsPersistLimit,
kStorageByteLimitPerLogType,
0),
ongoing_log_queue_(local_state,
prefs::kMetricsOngoingLogs,
+ prefs::kMetricsOngoingLogsOld,
kOngoingLogsPersistLimit,
kStorageByteLimitPerLogType,
max_ongoing_log_size) {}
@@ -62,10 +64,10 @@ void MetricsLogManager::BeginLoggingWithLog(scoped_ptr<MetricsLog> log) {
void MetricsLogManager::FinishCurrentLog() {
DCHECK(current_log_.get());
current_log_->CloseLog();
- std::string log_text;
- current_log_->GetEncodedLog(&log_text);
- if (!log_text.empty())
- StoreLog(&log_text, current_log_->log_type());
+ std::string log_data;
+ current_log_->GetEncodedLog(&log_data);
+ if (!log_data.empty())
+ StoreLog(log_data, current_log_->log_type());
current_log_.reset();
}
@@ -101,20 +103,20 @@ void MetricsLogManager::ResumePausedLog() {
current_log_.reset(paused_log_.release());
}
-void MetricsLogManager::StoreLog(std::string* log,
+void MetricsLogManager::StoreLog(const std::string& log_data,
MetricsLog::LogType log_type) {
switch (log_type) {
case MetricsLog::INITIAL_STABILITY_LOG:
- initial_log_queue_.StoreLog(log);
+ initial_log_queue_.StoreLog(log_data);
break;
case MetricsLog::ONGOING_LOG:
- ongoing_log_queue_.StoreLog(log);
+ ongoing_log_queue_.StoreLog(log_data);
break;
}
}
void MetricsLogManager::StoreStagedLogAsUnsent(
- metrics::PersistedLogs::StoreType store_type) {
+ PersistedLogs::StoreType store_type) {
DCHECK(has_staged_log());
if (initial_log_queue_.has_staged_log())
initial_log_queue_.StoreStagedLogAsUnsent(store_type);
diff --git a/components/metrics/metrics_log_manager.h b/components/metrics/metrics_log_manager.h
index 95c3df6..0bdc886 100644
--- a/components/metrics/metrics_log_manager.h
+++ b/components/metrics/metrics_log_manager.h
@@ -91,7 +91,7 @@ class MetricsLogManager {
// This is intended to be used when logs are being saved while an upload is in
// progress, in case the upload later succeeds.
// This can only be called if has_staged_log() is true.
- void StoreStagedLogAsUnsent(metrics::PersistedLogs::StoreType store_type);
+ void StoreStagedLogAsUnsent(PersistedLogs::StoreType store_type);
// Discards the last log stored with StoreStagedLogAsUnsent with |store_type|
// set to PROVISIONAL_STORE, as long as it hasn't already been re-staged. If
@@ -105,10 +105,8 @@ class MetricsLogManager {
void LoadPersistedUnsentLogs();
private:
- // Saves |log| as the given type.
- // NOTE: This clears the contents of |log| (to avoid an expensive copy),
- // so the log should be discarded after this call.
- void StoreLog(std::string* log, MetricsLog::LogType log_type);
+ // Saves |log_data| as the given type.
+ void StoreLog(const std::string& log_data, MetricsLog::LogType log_type);
// Tracks whether unsent logs (if any) have been loaded from the serializer.
bool unsent_logs_loaded_;
@@ -120,8 +118,8 @@ class MetricsLogManager {
scoped_ptr<MetricsLog> paused_log_;
// Logs that have not yet been sent.
- metrics::PersistedLogs initial_log_queue_;
- metrics::PersistedLogs ongoing_log_queue_;
+ PersistedLogs initial_log_queue_;
+ PersistedLogs ongoing_log_queue_;
DISALLOW_COPY_AND_ASSIGN(MetricsLogManager);
};
diff --git a/components/metrics/metrics_log_manager_unittest.cc b/components/metrics/metrics_log_manager_unittest.cc
index 06ffb428..b43a514 100644
--- a/components/metrics/metrics_log_manager_unittest.cc
+++ b/components/metrics/metrics_log_manager_unittest.cc
@@ -24,7 +24,9 @@ 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.
@@ -34,7 +36,7 @@ class TestLogPrefService : public TestingPrefServiceSimple {
list_length = GetList(prefs::kMetricsInitialLogs)->GetSize();
else
list_length = GetList(prefs::kMetricsOngoingLogs)->GetSize();
- return list_length ? list_length - 2 : 0;
+ return list_length / 2;
}
};
@@ -160,9 +162,9 @@ TEST(MetricsLogManagerTest, StoreAndLoad) {
// Simulate a log having already been unsent from a previous session.
{
std::string log("proto");
- metrics::PersistedLogs ongoing_logs(
- &pref_service, prefs::kMetricsOngoingLogs, 1, 1, 0);
- ongoing_logs.StoreLog(&log);
+ 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));
@@ -176,7 +178,7 @@ TEST(MetricsLogManagerTest, StoreAndLoad) {
log_manager.BeginLoggingWithLog(make_scoped_ptr(new MetricsLog(
"id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)));
log_manager.StageNextLogForUpload();
- log_manager.StoreStagedLogAsUnsent(metrics::PersistedLogs::NORMAL_STORE);
+ log_manager.StoreStagedLogAsUnsent(PersistedLogs::NORMAL_STORE);
log_manager.FinishCurrentLog();
// Nothing should be written out until PersistUnsentLogs is called.
@@ -236,7 +238,7 @@ TEST(MetricsLogManagerTest, StoreStagedLogTypes) {
"id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)));
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
- log_manager.StoreStagedLogAsUnsent(metrics::PersistedLogs::NORMAL_STORE);
+ log_manager.StoreStagedLogAsUnsent(PersistedLogs::NORMAL_STORE);
log_manager.PersistUnsentLogs();
EXPECT_EQ(0U, pref_service.TypeCount(MetricsLog::INITIAL_STABILITY_LOG));
@@ -252,7 +254,7 @@ TEST(MetricsLogManagerTest, StoreStagedLogTypes) {
"id", 0, MetricsLog::INITIAL_STABILITY_LOG, &client, &pref_service)));
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
- log_manager.StoreStagedLogAsUnsent(metrics::PersistedLogs::NORMAL_STORE);
+ log_manager.StoreStagedLogAsUnsent(PersistedLogs::NORMAL_STORE);
log_manager.PersistUnsentLogs();
EXPECT_EQ(1U, pref_service.TypeCount(MetricsLog::INITIAL_STABILITY_LOG));
@@ -295,8 +297,7 @@ TEST(MetricsLogManagerTest, ProvisionalStoreStandardFlow) {
log_manager.BeginLoggingWithLog(make_scoped_ptr(new MetricsLog(
"id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)));
log_manager.StageNextLogForUpload();
- log_manager.StoreStagedLogAsUnsent(
- metrics::PersistedLogs::PROVISIONAL_STORE);
+ log_manager.StoreStagedLogAsUnsent(PersistedLogs::PROVISIONAL_STORE);
log_manager.FinishCurrentLog();
log_manager.DiscardLastProvisionalStore();
@@ -320,15 +321,14 @@ TEST(MetricsLogManagerTest, ProvisionalStoreNoop) {
"id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)));
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
- log_manager.StoreStagedLogAsUnsent(
- metrics::PersistedLogs::PROVISIONAL_STORE);
+ 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(metrics::PersistedLogs::NORMAL_STORE);
+ log_manager.StoreStagedLogAsUnsent(PersistedLogs::NORMAL_STORE);
log_manager.DiscardLastProvisionalStore();
log_manager.PersistUnsentLogs();
@@ -345,13 +345,12 @@ TEST(MetricsLogManagerTest, ProvisionalStoreNoop) {
"id", 0, MetricsLog::ONGOING_LOG, &client, &pref_service)));
log_manager.FinishCurrentLog();
log_manager.StageNextLogForUpload();
- log_manager.StoreStagedLogAsUnsent(metrics::PersistedLogs::NORMAL_STORE);
+ 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(
- metrics::PersistedLogs::PROVISIONAL_STORE);
+ log_manager.StoreStagedLogAsUnsent(PersistedLogs::PROVISIONAL_STORE);
log_manager.DiscardLastProvisionalStore();
log_manager.DiscardLastProvisionalStore();
diff --git a/components/metrics/metrics_log_uploader.h b/components/metrics/metrics_log_uploader.h
index 0deecc8..5e7101d 100644
--- a/components/metrics/metrics_log_uploader.h
+++ b/components/metrics/metrics_log_uploader.h
@@ -25,9 +25,10 @@ class MetricsLogUploader {
const base::Callback<void(int)>& on_upload_complete);
virtual ~MetricsLogUploader();
- // Uploads a log with the specified |log_data| and |log_hash|. |log_hash| is
- // expected to be the hex-encoded SHA1 hash of |log_data|.
- virtual bool UploadLog(const std::string& log_data,
+ // Uploads a log with the specified |compressed_log_data| and |log_hash|.
+ // |log_hash| is expected to be the hex-encoded SHA1 hash of the log data
+ // before compression.
+ virtual bool UploadLog(const std::string& compressed_log_data,
const std::string& log_hash) = 0;
protected:
diff --git a/components/metrics/metrics_pref_names.cc b/components/metrics/metrics_pref_names.cc
index ed437a0..8b96e1a 100644
--- a/components/metrics/metrics_pref_names.cc
+++ b/components/metrics/metrics_pref_names.cc
@@ -16,6 +16,8 @@ const char kMetricsClientID[] = "user_experience_metrics.client_id2";
// first minute of a browser session. These logs include things like crash count
// info, etc.
const char kMetricsInitialLogs[] =
+ "user_experience_metrics.initial_logs_list";
+const char kMetricsInitialLogsOld[] =
"user_experience_metrics.initial_logs_as_protobufs";
// The metrics entropy source.
@@ -41,6 +43,8 @@ const char kMetricsOldLowEntropySource[] =
// logs typically include histograms and memory reports, as well as ongoing
// user activities.
const char kMetricsOngoingLogs[] =
+ "user_experience_metrics.ongoing_logs_list";
+const char kMetricsOngoingLogsOld[] =
"user_experience_metrics.ongoing_logs_as_protobufs";
// Boolean that indicates a cloned install has been detected and the metrics
diff --git a/components/metrics/metrics_pref_names.h b/components/metrics/metrics_pref_names.h
index 1862721..26352a8 100644
--- a/components/metrics/metrics_pref_names.h
+++ b/components/metrics/metrics_pref_names.h
@@ -12,11 +12,13 @@ namespace prefs {
// component. Document each in the .cc file.
extern const char kMetricsClientID[];
extern const char kMetricsInitialLogs[];
+extern const char kMetricsInitialLogsOld[];
extern const char kMetricsLowEntropySource[];
extern const char kMetricsMachineId[];
extern const char kMetricsOldClientID[];
extern const char kMetricsOldLowEntropySource[];
extern const char kMetricsOngoingLogs[];
+extern const char kMetricsOngoingLogsOld[];
extern const char kMetricsResetIds[];
extern const char kMetricsReportingEnabledTimestamp[];
extern const char kMetricsSessionID[];
diff --git a/components/metrics/metrics_service.cc b/components/metrics/metrics_service.cc
index ca57925..2d97d28 100644
--- a/components/metrics/metrics_service.cc
+++ b/components/metrics/metrics_service.cc
@@ -300,6 +300,8 @@ void MetricsService::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterListPref(metrics::prefs::kMetricsInitialLogs);
registry->RegisterListPref(metrics::prefs::kMetricsOngoingLogs);
+ registry->RegisterListPref(metrics::prefs::kMetricsInitialLogsOld);
+ registry->RegisterListPref(metrics::prefs::kMetricsOngoingLogsOld);
registry->RegisterInt64Pref(metrics::prefs::kUninstallLaunchCount, 0);
registry->RegisterInt64Pref(metrics::prefs::kUninstallMetricsUptimeSec, 0);
diff --git a/components/metrics/net/DEPS b/components/metrics/net/DEPS
index 71ea427..ede766a 100644
--- a/components/metrics/net/DEPS
+++ b/components/metrics/net/DEPS
@@ -1,4 +1,3 @@
include_rules = [
"+net",
- "+third_party/zlib",
]
diff --git a/components/metrics/net/compression_utils.cc b/components/metrics/net/compression_utils.cc
deleted file mode 100644
index e8efe18..0000000
--- a/components/metrics/net/compression_utils.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-// 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/net/compression_utils.h"
-
-#include <vector>
-
-#include "base/basictypes.h"
-#include "third_party/zlib/zlib.h"
-
-namespace {
-
-// The difference in bytes between a zlib header and a gzip header.
-const size_t kGzipZlibHeaderDifferenceBytes = 16;
-
-// Pass an integer greater than the following get a gzip header instead of a
-// zlib header when calling deflateInit2_.
-const int kWindowBitsToGetGzipHeader = 16;
-
-// This describes the amount of memory zlib uses to compress data. It can go
-// from 1 to 9, with 8 being the default. For details, see:
-// http://www.zlib.net/manual.html (search for memLevel).
-const int kZlibMemoryLevel = 8;
-
-// This code is taken almost verbatim from third_party/zlib/compress.c. The only
-// difference is deflateInit2_ is called which sets the window bits to be > 16.
-// That causes a gzip header to be emitted rather than a zlib header.
-int GzipCompressHelper(Bytef* dest,
- uLongf* dest_length,
- const Bytef* source,
- uLong source_length) {
- z_stream stream;
-
- stream.next_in = bit_cast<Bytef*>(source);
- stream.avail_in = static_cast<uInt>(source_length);
- stream.next_out = dest;
- stream.avail_out = static_cast<uInt>(*dest_length);
- if (static_cast<uLong>(stream.avail_out) != *dest_length)
- return Z_BUF_ERROR;
-
- stream.zalloc = static_cast<alloc_func>(0);
- stream.zfree = static_cast<free_func>(0);
- stream.opaque = static_cast<voidpf>(0);
-
- gz_header gzip_header;
- memset(&gzip_header, 0, sizeof(gzip_header));
- int err = deflateInit2_(&stream,
- Z_DEFAULT_COMPRESSION,
- Z_DEFLATED,
- MAX_WBITS + kWindowBitsToGetGzipHeader,
- kZlibMemoryLevel,
- Z_DEFAULT_STRATEGY,
- ZLIB_VERSION,
- sizeof(z_stream));
- if (err != Z_OK)
- return err;
-
- err = deflateSetHeader(&stream, &gzip_header);
- if (err != Z_OK)
- return err;
-
- err = deflate(&stream, Z_FINISH);
- if (err != Z_STREAM_END) {
- deflateEnd(&stream);
- return err == Z_OK ? Z_BUF_ERROR : err;
- }
- *dest_length = stream.total_out;
-
- err = deflateEnd(&stream);
- return err;
-}
-
-} // namespace
-
-namespace metrics {
-
-bool GzipCompress(const std::string& input, std::string* output) {
- const uLongf input_size = static_cast<uLongf>(input.size());
- std::vector<Bytef> compressed_data(kGzipZlibHeaderDifferenceBytes +
- compressBound(input_size));
-
- uLongf compressed_size = static_cast<uLongf>(compressed_data.size());
- if (GzipCompressHelper(&compressed_data.front(),
- &compressed_size,
- bit_cast<const Bytef*>(input.data()),
- input_size) != Z_OK) {
- return false;
- }
-
- compressed_data.resize(compressed_size);
- output->assign(compressed_data.begin(), compressed_data.end());
- return true;
-}
-
-} // namespace metrics
diff --git a/components/metrics/net/compression_utils.h b/components/metrics/net/compression_utils.h
deleted file mode 100644
index ee875dd..0000000
--- a/components/metrics/net/compression_utils.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// 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.
-
-#ifndef COMPONENTS_METRICS_NET_COMPRESSION_UTILS_H_
-#define COMPONENTS_METRICS_NET_COMPRESSION_UTILS_H_
-
-#include <string>
-
-namespace metrics {
-
-// Compresses the text in |input| using gzip, storing the result in |output|.
-bool GzipCompress(const std::string& input, std::string* output);
-
-} // namespace metrics
-
-#endif // COMPONENTS_METRICS_NET_COMPRESSION_UTILS_H_
diff --git a/components/metrics/net/net_metrics_log_uploader.cc b/components/metrics/net/net_metrics_log_uploader.cc
index 3e5a88e..8c68ca0 100644
--- a/components/metrics/net/net_metrics_log_uploader.cc
+++ b/components/metrics/net/net_metrics_log_uploader.cc
@@ -5,7 +5,6 @@
#include "components/metrics/net/net_metrics_log_uploader.h"
#include "base/metrics/histogram.h"
-#include "components/metrics/net/compression_utils.h"
#include "net/base/load_flags.h"
#include "net/url_request/url_fetcher.h"
#include "url/gurl.h"
@@ -24,22 +23,8 @@ NetMetricsLogUploader::NetMetricsLogUploader(
NetMetricsLogUploader::~NetMetricsLogUploader() {
}
-bool NetMetricsLogUploader::UploadLog(const std::string& log_data,
+bool NetMetricsLogUploader::UploadLog(const std::string& compressed_log_data,
const std::string& log_hash) {
- std::string compressed_log_data;
- if (!GzipCompress(log_data, &compressed_log_data)) {
- NOTREACHED();
- return false;
- }
-
- UMA_HISTOGRAM_PERCENTAGE(
- "UMA.ProtoCompressionRatio",
- static_cast<int>(100 * compressed_log_data.size() / log_data.size()));
- UMA_HISTOGRAM_CUSTOM_COUNTS(
- "UMA.ProtoGzippedKBSaved",
- static_cast<int>((log_data.size() - compressed_log_data.size()) / 1024),
- 1, 2000, 50);
-
current_fetch_.reset(
net::URLFetcher::Create(GURL(server_url_), net::URLFetcher::POST, this));
current_fetch_->SetRequestContext(request_context_getter_);
diff --git a/components/metrics/net/net_metrics_log_uploader.h b/components/metrics/net/net_metrics_log_uploader.h
index 4753fd7..52ee753 100644
--- a/components/metrics/net/net_metrics_log_uploader.h
+++ b/components/metrics/net/net_metrics_log_uploader.h
@@ -33,7 +33,7 @@ class NetMetricsLogUploader : public MetricsLogUploader,
virtual ~NetMetricsLogUploader();
// MetricsLogUploader:
- virtual bool UploadLog(const std::string& log_data,
+ virtual bool UploadLog(const std::string& compressed_log_data,
const std::string& log_hash) OVERRIDE;
private:
diff --git a/components/metrics/persisted_logs.cc b/components/metrics/persisted_logs.cc
index 006f095..17d6aa7 100644
--- a/components/metrics/persisted_logs.cc
+++ b/components/metrics/persisted_logs.cc
@@ -13,15 +13,12 @@
#include "base/prefs/scoped_user_pref_update.h"
#include "base/sha1.h"
#include "base/timer/elapsed_timer.h"
+#include "components/metrics/compression_utils.h"
namespace metrics {
namespace {
-// We append (2) more elements to persisted lists: the size of the list and a
-// checksum of the elements.
-const size_t kChecksumEntryCount = 2;
-
PersistedLogs::LogReadStatus MakeRecallStatusHistogram(
PersistedLogs::LogReadStatus status) {
UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecallProtobufs",
@@ -29,28 +26,64 @@ PersistedLogs::LogReadStatus MakeRecallStatusHistogram(
return status;
}
+// Reads the value at |index| from |list_value| as a string and Base64-decodes
+// it into |result|. Returns true on success.
+bool ReadBase64String(const base::ListValue& list_value,
+ size_t index,
+ std::string* result) {
+ std::string base64_result;
+ if (!list_value.GetString(index, &base64_result))
+ return false;
+ return base::Base64Decode(base64_result, result);
+}
+
+// Base64-encodes |str| and appends the result to |list_value|.
+void AppendBase64String(const std::string& str, base::ListValue* list_value) {
+ std::string base64_str;
+ base::Base64Encode(str, &base64_str);
+ list_value->Append(base::Value::CreateStringValue(base64_str));
+}
+
} // namespace
-void PersistedLogs::LogHashPair::SwapLog(std::string* input) {
- log.swap(*input);
- if (!log.empty())
- hash = base::SHA1HashString(log);
- else
- hash.clear();
+void PersistedLogs::LogHashPair::Init(const std::string& log_data) {
+ DCHECK(!log_data.empty());
+
+ if (!GzipCompress(log_data, &compressed_log_data)) {
+ NOTREACHED();
+ return;
+ }
+
+ UMA_HISTOGRAM_PERCENTAGE(
+ "UMA.ProtoCompressionRatio",
+ static_cast<int>(100 * compressed_log_data.size() / log_data.size()));
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "UMA.ProtoGzippedKBSaved",
+ static_cast<int>((log_data.size() - compressed_log_data.size()) / 1024),
+ 1, 2000, 50);
+
+ hash = base::SHA1HashString(log_data);
+}
+
+void PersistedLogs::LogHashPair::Clear() {
+ compressed_log_data.clear();
+ hash.clear();
}
void PersistedLogs::LogHashPair::Swap(PersistedLogs::LogHashPair* input) {
- log.swap(input->log);
+ compressed_log_data.swap(input->compressed_log_data);
hash.swap(input->hash);
}
PersistedLogs::PersistedLogs(PrefService* local_state,
const char* pref_name,
+ const char* old_pref_name,
size_t min_log_count,
size_t min_log_bytes,
size_t max_log_size)
: local_state_(local_state),
pref_name_(pref_name),
+ old_pref_name_(old_pref_name),
min_log_count_(min_log_count),
min_log_bytes_(min_log_bytes),
max_log_size_(max_log_size),
@@ -67,7 +100,7 @@ void PersistedLogs::SerializeLogs() {
if (max_log_size_) {
for (std::vector<LogHashPair>::iterator it = list_.begin();
it != list_.end();) {
- size_t log_size = it->log.length();
+ size_t log_size = it->compressed_log_data.length();
if (log_size > max_log_size_) {
UMA_HISTOGRAM_COUNTS("UMA.Large Accumulated Log Not Persisted",
static_cast<int>(log_size));
@@ -77,18 +110,29 @@ void PersistedLogs::SerializeLogs() {
}
}
}
+
ListPrefUpdate update(local_state_, pref_name_);
WriteLogsToPrefList(update.Get());
+
+ // Clear the old pref now that we've written to the new one.
+ // TODO(asvitkine): Remove the old pref in M39.
+ local_state_->ClearPref(old_pref_name_);
}
PersistedLogs::LogReadStatus PersistedLogs::DeserializeLogs() {
- const base::ListValue* unsent_logs = local_state_->GetList(pref_name_);
+ // First, try reading from old pref. If it's empty, read from the new one.
+ // TODO(asvitkine): Remove the old pref in M39.
+ const base::ListValue* unsent_logs = local_state_->GetList(old_pref_name_);
+ if (!unsent_logs->empty())
+ return ReadLogsFromOldPrefList(*unsent_logs);
+
+ unsent_logs = local_state_->GetList(pref_name_);
return ReadLogsFromPrefList(*unsent_logs);
}
-void PersistedLogs::StoreLog(std::string* input) {
+void PersistedLogs::StoreLog(const std::string& log_data) {
list_.push_back(LogHashPair());
- list_.back().SwapLog(input);
+ list_.back().Init(log_data);
}
void PersistedLogs::StageLog() {
@@ -107,7 +151,7 @@ void PersistedLogs::StageLog() {
void PersistedLogs::DiscardStagedLog() {
DCHECK(has_staged_log());
- staged_log_.log.clear();
+ staged_log_.Clear();
}
void PersistedLogs::StoreStagedLogAsUnsent(StoreType store_type) {
@@ -127,7 +171,6 @@ void PersistedLogs::DiscardLastProvisionalStore() {
void PersistedLogs::WriteLogsToPrefList(base::ListValue* list_value) {
list_value->Clear();
-
// Leave the list completely empty if there are no storable values.
if (list_.empty())
return;
@@ -141,7 +184,7 @@ void PersistedLogs::WriteLogsToPrefList(base::ListValue* list_value) {
std::vector<LogHashPair>::const_reverse_iterator end = list_.rend();
for (std::vector<LogHashPair>::const_reverse_iterator it = list_.rbegin();
it != end; ++it) {
- size_t log_size = it->log.length();
+ const size_t log_size = it->compressed_log_data.length();
if (bytes_used >= min_log_bytes_ &&
(list_.size() - start) >= min_log_count_) {
break;
@@ -151,35 +194,44 @@ void PersistedLogs::WriteLogsToPrefList(base::ListValue* list_value) {
}
}
DCHECK_LT(start, list_.size());
- if (start >= list_.size())
- return;
- // Store size at the beginning of the list_value.
- list_value->Append(base::Value::CreateIntegerValue(list_.size() - start));
+ for (size_t i = start; i < list_.size(); ++i) {
+ AppendBase64String(list_[i].compressed_log_data, list_value);
+ AppendBase64String(list_[i].hash, list_value);
+ }
+}
+
+PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromPrefList(
+ const base::ListValue& list_value) {
+ if (list_value.empty())
+ return MakeRecallStatusHistogram(LIST_EMPTY);
- base::MD5Context ctx;
- base::MD5Init(&ctx);
- std::string encoded_log;
- for (std::vector<LogHashPair>::const_iterator it = list_.begin() + start;
- it != list_.end(); ++it) {
- // We encode the compressed log as Value::CreateStringValue() expects to
- // take a valid UTF8 string.
- base::Base64Encode(it->log, &encoded_log);
- base::MD5Update(&ctx, encoded_log);
- list_value->Append(base::Value::CreateStringValue(encoded_log));
+ // For each log, there's two entries in the list (the data and the hash).
+ DCHECK_EQ(0U, list_value.GetSize() % 2);
+ const size_t log_count = list_value.GetSize() / 2;
+
+ // Resize |list_| ahead of time, so that values can be decoded directly into
+ // the elements of the list.
+ DCHECK(list_.empty());
+ list_.resize(log_count);
+
+ for (size_t i = 0; i < log_count; ++i) {
+ if (!ReadBase64String(list_value, i * 2, &list_[i].compressed_log_data) ||
+ !ReadBase64String(list_value, i * 2 + 1, &list_[i].hash)) {
+ list_.clear();
+ return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION);
+ }
}
- // Append hash to the end of the list_value.
- base::MD5Digest digest;
- base::MD5Final(&digest, &ctx);
- list_value->Append(base::Value::CreateStringValue(
- base::MD5DigestToBase16(digest)));
- // Minimum of 3 elements (size, data, hash).
- DCHECK_GE(list_value->GetSize(), 3U);
+ return MakeRecallStatusHistogram(RECALL_SUCCESS);
}
-PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromPrefList(
+PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromOldPrefList(
const base::ListValue& list_value) {
+ // We append (2) more elements to persisted lists: the size of the list and a
+ // checksum of the elements.
+ const size_t kChecksumEntryCount = 2;
+
if (list_value.GetSize() == 0)
return MakeRecallStatusHistogram(LIST_EMPTY);
if (list_value.GetSize() <= kChecksumEntryCount)
@@ -223,7 +275,7 @@ PersistedLogs::LogReadStatus PersistedLogs::ReadLogsFromPrefList(
}
DCHECK_LT(local_index, list_.size());
- list_[local_index].SwapLog(&log_text);
+ list_[local_index].Init(log_text);
}
// Verify checksum.
diff --git a/components/metrics/persisted_logs.h b/components/metrics/persisted_logs.h
index c8457e3..3e68427 100644
--- a/components/metrics/persisted_logs.h
+++ b/components/metrics/persisted_logs.h
@@ -42,15 +42,19 @@ class PersistedLogs {
PROVISIONAL_STORE, // A store operation that can be easily reverted later.
};
- // Constructs a PersistedLogs that stores data in |local_state| under
- // the preference |pref_name|. Calling code is responsible for ensuring that
- // the lifetime of |local_state| is longer than the lifetime of PersistedLogs.
- // When saving logs to disk, we will store either the first |min_log_count|
- // logs, or at least |min_log_bytes| bytes of logs, whichever is more.
- // If the optional max_log_size parameter is non-zero, all logs larger than
+ // Constructs a PersistedLogs that stores data in |local_state| under the
+ // preference |pref_name| and also reads from legacy pref |old_pref_name|.
+ // Calling code is responsible for ensuring that the lifetime of |local_state|
+ // is longer than the lifetime of PersistedLogs.
+ //
+ // When saving logs to disk, stores either the first |min_log_count| logs, or
+ // at least |min_log_bytes| bytes of logs, whichever is greater.
+ //
+ // If the optional |max_log_size| parameter is non-zero, all logs larger than
// that limit will be dropped before logs are written to disk.
PersistedLogs(PrefService* local_state,
const char* pref_name,
+ const char* old_pref_name,
size_t min_log_count,
size_t min_log_bytes,
size_t max_log_size);
@@ -62,8 +66,8 @@ class PersistedLogs {
// Reads the list from the preference.
LogReadStatus DeserializeLogs();
- // Adds a log to the list. |input| will be swapped with an empty string.
- void StoreLog(std::string* input);
+ // Adds a log to the list.
+ void StoreLog(const std::string& log_data);
// Stages the most recent log. The staged_log will remain the same even if
// additional logs are added.
@@ -87,12 +91,14 @@ class PersistedLogs {
void DiscardLastProvisionalStore();
// True if a log has been staged.
- bool has_staged_log() const { return !staged_log_.log.empty(); };
+ bool has_staged_log() const {
+ return !staged_log_.compressed_log_data.empty();
+ }
// Returns the element in the front of the list.
const std::string& staged_log() const {
DCHECK(has_staged_log());
- return staged_log_.log;
+ return staged_log_.compressed_log_data;
}
// Returns the element in the front of the list.
@@ -114,14 +120,22 @@ class PersistedLogs {
// Reads the list from the ListValue.
LogReadStatus ReadLogsFromPrefList(const base::ListValue& list);
+ // Reads the list from the old pref's ListValue.
+ // TODO(asvitkine): Remove the old pref in M39.
+ LogReadStatus ReadLogsFromOldPrefList(const base::ListValue& list);
+
// A weak pointer to the PrefService object to read and write the preference
// from. Calling code should ensure this object continues to exist for the
// lifetime of the PersistedLogs object.
PrefService* local_state_;
- // The name of the preference this object stores logs in.
+ // The name of the preference to serialize logs to/from.
const char* pref_name_;
+ // The name of the preference to serialize logs from.
+ // TODO(asvitkine): Remove the old pref in M39.
+ const char* old_pref_name_;
+
// We will keep at least this |min_log_count_| logs or |min_log_bytes_| bytes
// of logs, whichever is greater, when writing to disk. These apply after
// skipping logs greater than |max_log_size_|.
@@ -132,14 +146,20 @@ class PersistedLogs {
const size_t max_log_size_;
struct LogHashPair {
- // Raw log text, typically a serialized protobuf.
- std::string log;
- // The SHA1 hash of log, stored to catch errors from memory corruption.
- std::string hash;
- // Swap the content of input into log and update the hash.
- void SwapLog(std::string* input);
+ // Initializes the members based on uncompressed |log_data|.
+ void Init(const std::string& log_data);
+
+ // Clears the struct members.
+ void Clear();
+
// Swap both log and hash from another LogHashPair.
void Swap(LogHashPair* input);
+
+ // Compressed log data - a serialized protobuf that's been gzipped.
+ std::string compressed_log_data;
+
+ // The SHA1 hash of log, stored to catch errors from memory corruption.
+ std::string hash;
};
// A list of all of the stored logs, stored with SHA1 hashes to check for
// corruption while they are stored in memory.
diff --git a/components/metrics/persisted_logs_unittest.cc b/components/metrics/persisted_logs_unittest.cc
index 97e9f41..27c230c 100644
--- a/components/metrics/persisted_logs_unittest.cc
+++ b/components/metrics/persisted_logs_unittest.cc
@@ -2,14 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "components/metrics/persisted_logs.h"
+
#include "base/base64.h"
-#include "base/md5.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/scoped_user_pref_update.h"
#include "base/prefs/testing_pref_service.h"
+#include "base/rand_util.h"
#include "base/sha1.h"
#include "base/values.h"
-#include "components/metrics/persisted_logs.h"
+#include "components/metrics/compression_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace metrics {
@@ -17,14 +19,37 @@ namespace metrics {
namespace {
const char kTestPrefName[] = "TestPref";
+const char kTestOldPrefName[] = "TestPrefOld";
const size_t kLogCountLimit = 3;
const size_t kLogByteLimit = 1000;
+// Compresses |log_data| and returns the result.
+std::string Compress(const std::string& log_data) {
+ std::string compressed_log_data;
+ EXPECT_TRUE(GzipCompress(log_data, &compressed_log_data));
+ return compressed_log_data;
+}
+
+// Generates and returns log data such that its size after compression is at
+// least |min_compressed_size|.
+std::string GenerateLogWithMinCompressedSize(size_t min_compressed_size) {
+ // Since the size check is done against a compressed log, generate enough
+ // data that compresses to larger than |log_size|.
+ std::string rand_bytes = base::RandBytesAsString(min_compressed_size);
+ while (Compress(rand_bytes).size() < min_compressed_size)
+ rand_bytes.append(base::RandBytesAsString(min_compressed_size));
+ std::string base64_data_for_logging;
+ base::Base64Encode(rand_bytes, &base64_data_for_logging);
+ SCOPED_TRACE(testing::Message() << "Using random data "
+ << base64_data_for_logging);
+ return rand_bytes;
+}
class PersistedLogsTest : public testing::Test {
public:
PersistedLogsTest() {
prefs_.registry()->RegisterListPref(kTestPrefName);
+ prefs_.registry()->RegisterListPref(kTestOldPrefName);
}
protected:
@@ -36,88 +61,69 @@ class PersistedLogsTest : public testing::Test {
class TestPersistedLogs : public PersistedLogs {
public:
- TestPersistedLogs(PrefService* service)
- : PersistedLogs(service, kTestPrefName, kLogCountLimit, kLogByteLimit, 0) {
- }
-
- // Make a copy of the string and store the copy.
- void StoreLogCopy(std::string tmp) {
- StoreLog(&tmp);
+ TestPersistedLogs(PrefService* service, size_t min_log_bytes)
+ : PersistedLogs(service, kTestPrefName, kTestOldPrefName, kLogCountLimit,
+ min_log_bytes, 0) {
}
// Stages and removes the next log, while testing it's value.
void ExpectNextLog(const std::string& expected_log) {
StageLog();
- EXPECT_EQ(staged_log(), expected_log);
+ EXPECT_EQ(staged_log(), Compress(expected_log));
DiscardStagedLog();
}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestPersistedLogs);
};
} // namespace
// Store and retrieve empty list_value.
TEST_F(PersistedLogsTest, EmptyLogList) {
- TestPersistedLogs persisted_logs(&prefs_);
+ TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
persisted_logs.SerializeLogs();
const base::ListValue* list_value = prefs_.GetList(kTestPrefName);
EXPECT_EQ(0U, list_value->GetSize());
- TestPersistedLogs result_persisted_logs(&prefs_);
+ TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
EXPECT_EQ(PersistedLogs::LIST_EMPTY, result_persisted_logs.DeserializeLogs());
EXPECT_EQ(0U, result_persisted_logs.size());
}
// Store and retrieve a single log value.
TEST_F(PersistedLogsTest, SingleElementLogList) {
- TestPersistedLogs persisted_logs(&prefs_);
+ TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
- persisted_logs.StoreLogCopy("Hello world!");
+ persisted_logs.StoreLog("Hello world!");
persisted_logs.SerializeLogs();
- const base::ListValue* list_value = prefs_.GetList(kTestPrefName);
- // |list_value| will now contain the following:
- // [1, Base64Encode("Hello world!"), MD5("Hello world!")].
- ASSERT_EQ(3U, list_value->GetSize());
-
- // Examine each element.
- base::ListValue::const_iterator it = list_value->begin();
- int size = 0;
- (*it)->GetAsInteger(&size);
- EXPECT_EQ(1, size);
-
- ++it;
- std::string str;
- (*it)->GetAsString(&str); // Base64 encoded "Hello world!" string.
- std::string encoded;
- base::Base64Encode("Hello world!", &encoded);
- EXPECT_TRUE(encoded == str);
-
- ++it;
- (*it)->GetAsString(&str); // MD5 for encoded "Hello world!" string.
- EXPECT_TRUE(base::MD5String(encoded) == str);
-
- ++it;
- EXPECT_TRUE(it == list_value->end()); // Reached end of list_value.
-
- TestPersistedLogs result_persisted_logs(&prefs_);
+ TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
result_persisted_logs.DeserializeLogs());
EXPECT_EQ(1U, result_persisted_logs.size());
+
+ // Verify that the result log matches the initial log.
+ persisted_logs.StageLog();
+ result_persisted_logs.StageLog();
+ EXPECT_EQ(persisted_logs.staged_log(), result_persisted_logs.staged_log());
+ EXPECT_EQ(persisted_logs.staged_log_hash(),
+ result_persisted_logs.staged_log_hash());
}
// Store a set of logs over the length limit, but smaller than the min number of
// bytes.
TEST_F(PersistedLogsTest, LongButTinyLogList) {
- TestPersistedLogs persisted_logs(&prefs_);
+ TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
size_t log_count = kLogCountLimit * 5;
for (size_t i = 0; i < log_count; ++i)
- persisted_logs.StoreLogCopy("x");
+ persisted_logs.StoreLog("x");
persisted_logs.SerializeLogs();
- TestPersistedLogs result_persisted_logs(&prefs_);
+ TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
result_persisted_logs.DeserializeLogs());
EXPECT_EQ(persisted_logs.size(), result_persisted_logs.size());
@@ -128,29 +134,33 @@ TEST_F(PersistedLogsTest, LongButTinyLogList) {
// Store a set of logs over the length limit, but that doesn't reach the minimum
// number of bytes until after passing the length limit.
TEST_F(PersistedLogsTest, LongButSmallLogList) {
- TestPersistedLogs persisted_logs(&prefs_);
-
- // Make log_count logs each slightly larger than
- // kLogByteLimit / (log_count - 2)
- // so that the minimum is reached before the oldest (first) two logs.
size_t log_count = kLogCountLimit * 5;
- size_t log_size = (kLogByteLimit / (log_count - 2)) + 2;
- persisted_logs.StoreLogCopy("one");
- persisted_logs.StoreLogCopy("two");
+ size_t log_size = 50;
+
std::string first_kept = "First to keep";
first_kept.resize(log_size, ' ');
- persisted_logs.StoreLogCopy(first_kept);
+
std::string blank_log = std::string(log_size, ' ');
- for (size_t i = persisted_logs.size(); i < log_count - 1; ++i) {
- persisted_logs.StoreLogCopy(blank_log);
- }
+
std::string last_kept = "Last to keep";
last_kept.resize(log_size, ' ');
- persisted_logs.StoreLogCopy(last_kept);
+ // Set the byte limit enough to keep everything but the first two logs.
+ const size_t min_log_bytes =
+ Compress(first_kept).length() + Compress(last_kept).length() +
+ (log_count - 4) * Compress(blank_log).length();
+ TestPersistedLogs persisted_logs(&prefs_, min_log_bytes);
+
+ persisted_logs.StoreLog("one");
+ persisted_logs.StoreLog("two");
+ persisted_logs.StoreLog(first_kept);
+ for (size_t i = persisted_logs.size(); i < log_count - 1; ++i) {
+ persisted_logs.StoreLog(blank_log);
+ }
+ persisted_logs.StoreLog(last_kept);
persisted_logs.SerializeLogs();
- TestPersistedLogs result_persisted_logs(&prefs_);
+ TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
result_persisted_logs.DeserializeLogs());
EXPECT_EQ(persisted_logs.size() - 2, result_persisted_logs.size());
@@ -165,19 +175,18 @@ TEST_F(PersistedLogsTest, LongButSmallLogList) {
// Store a set of logs within the length limit, but well over the minimum
// number of bytes.
TEST_F(PersistedLogsTest, ShortButLargeLogList) {
- TestPersistedLogs persisted_logs(&prefs_);
-
// Make the total byte count about twice the minimum.
size_t log_count = kLogCountLimit;
size_t log_size = (kLogByteLimit / log_count) * 2;
- std::string blank_log = std::string(log_size, ' ');
+ std::string log_data = GenerateLogWithMinCompressedSize(log_size);
+
+ TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
for (size_t i = 0; i < log_count; ++i) {
- persisted_logs.StoreLogCopy(blank_log);
+ persisted_logs.StoreLog(log_data);
}
-
persisted_logs.SerializeLogs();
- TestPersistedLogs result_persisted_logs(&prefs_);
+ TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
result_persisted_logs.DeserializeLogs());
EXPECT_EQ(persisted_logs.size(), result_persisted_logs.size());
@@ -186,154 +195,60 @@ TEST_F(PersistedLogsTest, ShortButLargeLogList) {
// Store a set of logs over the length limit, and over the minimum number of
// bytes.
TEST_F(PersistedLogsTest, LongAndLargeLogList) {
- TestPersistedLogs persisted_logs(&prefs_);
+ TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
// Include twice the max number of logs.
size_t log_count = kLogCountLimit * 2;
// Make the total byte count about four times the minimum.
size_t log_size = (kLogByteLimit / log_count) * 4;
+
std::string target_log = "First to keep";
- target_log.resize(log_size, ' ');
- std::string blank_log = std::string(log_size, ' ');
+ target_log += GenerateLogWithMinCompressedSize(log_size);
+
+ std::string log_data = GenerateLogWithMinCompressedSize(log_size);
for (size_t i = 0; i < log_count; ++i) {
if (i == log_count - kLogCountLimit)
- persisted_logs.StoreLogCopy(target_log);
+ persisted_logs.StoreLog(target_log);
else
- persisted_logs.StoreLogCopy(blank_log);
+ persisted_logs.StoreLog(log_data);
}
persisted_logs.SerializeLogs();
- TestPersistedLogs result_persisted_logs(&prefs_);
+ TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
result_persisted_logs.DeserializeLogs());
EXPECT_EQ(kLogCountLimit, result_persisted_logs.size());
while (result_persisted_logs.size() > 1) {
- result_persisted_logs.ExpectNextLog(blank_log);
+ result_persisted_logs.ExpectNextLog(log_data);
}
result_persisted_logs.ExpectNextLog(target_log);
}
-// Induce LIST_SIZE_TOO_SMALL corruption
-TEST_F(PersistedLogsTest, SmallRecoveredListSize) {
- TestPersistedLogs persisted_logs(&prefs_);
-
- persisted_logs.StoreLogCopy("Hello world!");
-
- persisted_logs.SerializeLogs();
-
- {
- ListPrefUpdate update(&prefs_, kTestPrefName);
- base::ListValue* list_value = update.Get();
- EXPECT_EQ(3U, list_value->GetSize());
-
- // Remove last element.
- list_value->Remove(list_value->GetSize() - 1, NULL);
- EXPECT_EQ(2U, list_value->GetSize());
- }
-
- TestPersistedLogs result_persisted_logs(&prefs_);
- EXPECT_EQ(PersistedLogs::LIST_SIZE_TOO_SMALL,
- result_persisted_logs.DeserializeLogs());
-}
-
-// Remove size from the stored list_value.
-TEST_F(PersistedLogsTest, RemoveSizeFromLogList) {
- TestPersistedLogs persisted_logs(&prefs_);
-
- persisted_logs.StoreLogCopy("one");
- persisted_logs.StoreLogCopy("two");
- EXPECT_EQ(2U, persisted_logs.size());
-
- persisted_logs.SerializeLogs();
-
- {
- ListPrefUpdate update(&prefs_, kTestPrefName);
- base::ListValue* list_value = update.Get();
- EXPECT_EQ(4U, list_value->GetSize());
-
- list_value->Remove(0, NULL); // Delete size (1st element).
- EXPECT_EQ(3U, list_value->GetSize());
- }
-
- TestPersistedLogs result_persisted_logs(&prefs_);
- EXPECT_EQ(PersistedLogs::LIST_SIZE_MISSING,
- result_persisted_logs.DeserializeLogs());
-}
-
-// Corrupt size of stored list_value.
-TEST_F(PersistedLogsTest, CorruptSizeOfLogList) {
- TestPersistedLogs persisted_logs(&prefs_);
-
- persisted_logs.StoreLogCopy("Hello world!");
-
- persisted_logs.SerializeLogs();
-
- {
- ListPrefUpdate update(&prefs_, kTestPrefName);
- base::ListValue* list_value = update.Get();
- EXPECT_EQ(3U, list_value->GetSize());
-
- // Change list_value size from 1 to 2.
- EXPECT_TRUE(list_value->Set(0, base::Value::CreateIntegerValue(2)));
- EXPECT_EQ(3U, list_value->GetSize());
- }
-
- TestPersistedLogs result_persisted_logs(&prefs_);
- EXPECT_EQ(PersistedLogs::LIST_SIZE_CORRUPTION,
- result_persisted_logs.DeserializeLogs());
-}
-
-// Corrupt checksum of stored list_value.
-TEST_F(PersistedLogsTest, CorruptChecksumOfLogList) {
- TestPersistedLogs persisted_logs(&prefs_);
-
- persisted_logs.StoreLogCopy("Hello world!");
-
- persisted_logs.SerializeLogs();
-
- {
- ListPrefUpdate update(&prefs_, kTestPrefName);
- base::ListValue* list_value = update.Get();
- EXPECT_EQ(3U, list_value->GetSize());
-
- // Fetch checksum (last element) and change it.
- std::string checksum;
- EXPECT_TRUE((*(list_value->end() - 1))->GetAsString(&checksum));
- checksum[0] = (checksum[0] == 'a') ? 'b' : 'a';
- EXPECT_TRUE(list_value->Set(2, base::Value::CreateStringValue(checksum)));
- EXPECT_EQ(3U, list_value->GetSize());
- }
-
- TestPersistedLogs result_persisted_logs(&prefs_);
- EXPECT_EQ(PersistedLogs::CHECKSUM_CORRUPTION,
- result_persisted_logs.DeserializeLogs());
-}
-
// Check that the store/stage/discard functions work as expected.
TEST_F(PersistedLogsTest, Staging) {
- TestPersistedLogs persisted_logs(&prefs_);
+ TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
std::string tmp;
EXPECT_FALSE(persisted_logs.has_staged_log());
- persisted_logs.StoreLogCopy("one");
+ persisted_logs.StoreLog("one");
EXPECT_FALSE(persisted_logs.has_staged_log());
- persisted_logs.StoreLogCopy("two");
+ persisted_logs.StoreLog("two");
persisted_logs.StageLog();
EXPECT_TRUE(persisted_logs.has_staged_log());
- EXPECT_EQ(persisted_logs.staged_log(), std::string("two"));
- persisted_logs.StoreLogCopy("three");
- EXPECT_EQ(persisted_logs.staged_log(), std::string("two"));
+ EXPECT_EQ(persisted_logs.staged_log(), Compress("two"));
+ persisted_logs.StoreLog("three");
+ EXPECT_EQ(persisted_logs.staged_log(), Compress("two"));
EXPECT_EQ(persisted_logs.size(), 2U);
persisted_logs.DiscardStagedLog();
EXPECT_FALSE(persisted_logs.has_staged_log());
EXPECT_EQ(persisted_logs.size(), 2U);
persisted_logs.StageLog();
- EXPECT_EQ(persisted_logs.staged_log(), std::string("three"));
+ EXPECT_EQ(persisted_logs.staged_log(), Compress("three"));
persisted_logs.DiscardStagedLog();
persisted_logs.StageLog();
- EXPECT_EQ(persisted_logs.staged_log(), std::string("one"));
+ EXPECT_EQ(persisted_logs.staged_log(), Compress("one"));
persisted_logs.DiscardStagedLog();
EXPECT_FALSE(persisted_logs.has_staged_log());
EXPECT_EQ(persisted_logs.size(), 0U);
@@ -341,16 +256,16 @@ TEST_F(PersistedLogsTest, Staging) {
TEST_F(PersistedLogsTest, ProvisionalStoreStandardFlow) {
// Ensure that provisional store works, and discards the correct log.
- TestPersistedLogs persisted_logs(&prefs_);
+ TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
- persisted_logs.StoreLogCopy("one");
+ persisted_logs.StoreLog("one");
persisted_logs.StageLog();
persisted_logs.StoreStagedLogAsUnsent(PersistedLogs::PROVISIONAL_STORE);
- persisted_logs.StoreLogCopy("two");
+ persisted_logs.StoreLog("two");
persisted_logs.DiscardLastProvisionalStore();
persisted_logs.SerializeLogs();
- TestPersistedLogs result_persisted_logs(&prefs_);
+ TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
result_persisted_logs.DeserializeLogs());
EXPECT_EQ(1U, result_persisted_logs.size());
@@ -360,20 +275,20 @@ TEST_F(PersistedLogsTest, ProvisionalStoreStandardFlow) {
TEST_F(PersistedLogsTest, ProvisionalStoreNoop1) {
// Ensure that trying to drop a sent log is a no-op, even if another log has
// since been staged.
- TestPersistedLogs persisted_logs(&prefs_);
+ TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
persisted_logs.DeserializeLogs();
- persisted_logs.StoreLogCopy("one");
+ persisted_logs.StoreLog("one");
persisted_logs.StageLog();
persisted_logs.StoreStagedLogAsUnsent(PersistedLogs::PROVISIONAL_STORE);
persisted_logs.StageLog();
persisted_logs.DiscardStagedLog();
- persisted_logs.StoreLogCopy("two");
+ persisted_logs.StoreLog("two");
persisted_logs.StageLog();
persisted_logs.StoreStagedLogAsUnsent(PersistedLogs::NORMAL_STORE);
persisted_logs.DiscardLastProvisionalStore();
persisted_logs.SerializeLogs();
- TestPersistedLogs result_persisted_logs(&prefs_);
+ TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
result_persisted_logs.DeserializeLogs());
EXPECT_EQ(1U, result_persisted_logs.size());
@@ -382,33 +297,34 @@ TEST_F(PersistedLogsTest, ProvisionalStoreNoop1) {
TEST_F(PersistedLogsTest, ProvisionalStoreNoop2) {
// Ensure that trying to drop more than once is a no-op
- TestPersistedLogs persisted_logs(&prefs_);
+ TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
persisted_logs.DeserializeLogs();
- persisted_logs.StoreLogCopy("one");
+ persisted_logs.StoreLog("one");
persisted_logs.StageLog();
persisted_logs.StoreStagedLogAsUnsent(PersistedLogs::NORMAL_STORE);
- persisted_logs.StoreLogCopy("two");
+ persisted_logs.StoreLog("two");
persisted_logs.StageLog();
persisted_logs.StoreStagedLogAsUnsent(PersistedLogs::PROVISIONAL_STORE);
persisted_logs.DiscardLastProvisionalStore();
persisted_logs.DiscardLastProvisionalStore();
persisted_logs.SerializeLogs();
- TestPersistedLogs result_persisted_logs(&prefs_);
+ TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
result_persisted_logs.DeserializeLogs());
EXPECT_EQ(1U, result_persisted_logs.size());
result_persisted_logs.ExpectNextLog("one");
}
-TEST_F(PersistedLogsTest, Hashes) {
+TEST_F(PersistedLogsTest, Encoding) {
const char kFooText[] = "foo";
const std::string foo_hash = base::SHA1HashString(kFooText);
- TestingPrefServiceSimple prefs_;
- prefs_.registry()->RegisterListPref(kTestPrefName);
- TestPersistedLogs persisted_logs(&prefs_);
- persisted_logs.StoreLogCopy(kFooText);
+
+ TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
+ persisted_logs.StoreLog(kFooText);
persisted_logs.StageLog();
+
+ EXPECT_EQ(Compress(kFooText), persisted_logs.staged_log());
EXPECT_EQ(foo_hash, persisted_logs.staged_log_hash());
}