1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/metrics/persisted_logs.h"
7 #include "base/base64.h"
8 #include "base/prefs/pref_registry_simple.h"
9 #include "base/prefs/scoped_user_pref_update.h"
10 #include "base/prefs/testing_pref_service.h"
11 #include "base/rand_util.h"
12 #include "base/sha1.h"
13 #include "base/values.h"
14 #include "components/metrics/compression_utils.h"
15 #include "testing/gtest/include/gtest/gtest.h"
21 const char kTestPrefName[] = "TestPref";
22 const char kTestOldPrefName[] = "TestPrefOld";
23 const size_t kLogCountLimit = 3;
24 const size_t kLogByteLimit = 1000;
26 // Compresses |log_data| and returns the result.
27 std::string Compress(const std::string& log_data) {
28 std::string compressed_log_data;
29 EXPECT_TRUE(GzipCompress(log_data, &compressed_log_data));
30 return compressed_log_data;
33 // Generates and returns log data such that its size after compression is at
34 // least |min_compressed_size|.
35 std::string GenerateLogWithMinCompressedSize(size_t min_compressed_size) {
36 // Since the size check is done against a compressed log, generate enough
37 // data that compresses to larger than |log_size|.
38 std::string rand_bytes = base::RandBytesAsString(min_compressed_size);
39 while (Compress(rand_bytes).size() < min_compressed_size)
40 rand_bytes.append(base::RandBytesAsString(min_compressed_size));
41 std::string base64_data_for_logging;
42 base::Base64Encode(rand_bytes, &base64_data_for_logging);
43 SCOPED_TRACE(testing::Message() << "Using random data "
44 << base64_data_for_logging);
48 class PersistedLogsTest : public testing::Test {
51 prefs_.registry()->RegisterListPref(kTestPrefName);
52 prefs_.registry()->RegisterListPref(kTestOldPrefName);
56 TestingPrefServiceSimple prefs_;
59 DISALLOW_COPY_AND_ASSIGN(PersistedLogsTest);
62 class TestPersistedLogs : public PersistedLogs {
64 TestPersistedLogs(PrefService* service, size_t min_log_bytes)
65 : PersistedLogs(service, kTestPrefName, kTestOldPrefName, kLogCountLimit,
69 // Stages and removes the next log, while testing it's value.
70 void ExpectNextLog(const std::string& expected_log) {
72 EXPECT_EQ(staged_log(), Compress(expected_log));
77 DISALLOW_COPY_AND_ASSIGN(TestPersistedLogs);
82 // Store and retrieve empty list_value.
83 TEST_F(PersistedLogsTest, EmptyLogList) {
84 TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
86 persisted_logs.SerializeLogs();
87 const base::ListValue* list_value = prefs_.GetList(kTestPrefName);
88 EXPECT_EQ(0U, list_value->GetSize());
90 TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
91 EXPECT_EQ(PersistedLogs::LIST_EMPTY, result_persisted_logs.DeserializeLogs());
92 EXPECT_EQ(0U, result_persisted_logs.size());
95 // Store and retrieve a single log value.
96 TEST_F(PersistedLogsTest, SingleElementLogList) {
97 TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
99 persisted_logs.StoreLog("Hello world!");
100 persisted_logs.SerializeLogs();
102 TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
103 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
104 result_persisted_logs.DeserializeLogs());
105 EXPECT_EQ(1U, result_persisted_logs.size());
107 // Verify that the result log matches the initial log.
108 persisted_logs.StageLog();
109 result_persisted_logs.StageLog();
110 EXPECT_EQ(persisted_logs.staged_log(), result_persisted_logs.staged_log());
111 EXPECT_EQ(persisted_logs.staged_log_hash(),
112 result_persisted_logs.staged_log_hash());
115 // Store a set of logs over the length limit, but smaller than the min number of
117 TEST_F(PersistedLogsTest, LongButTinyLogList) {
118 TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
120 size_t log_count = kLogCountLimit * 5;
121 for (size_t i = 0; i < log_count; ++i)
122 persisted_logs.StoreLog("x");
124 persisted_logs.SerializeLogs();
126 TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
127 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
128 result_persisted_logs.DeserializeLogs());
129 EXPECT_EQ(persisted_logs.size(), result_persisted_logs.size());
131 result_persisted_logs.ExpectNextLog("x");
134 // Store a set of logs over the length limit, but that doesn't reach the minimum
135 // number of bytes until after passing the length limit.
136 TEST_F(PersistedLogsTest, LongButSmallLogList) {
137 size_t log_count = kLogCountLimit * 5;
138 size_t log_size = 50;
140 std::string first_kept = "First to keep";
141 first_kept.resize(log_size, ' ');
143 std::string blank_log = std::string(log_size, ' ');
145 std::string last_kept = "Last to keep";
146 last_kept.resize(log_size, ' ');
148 // Set the byte limit enough to keep everything but the first two logs.
149 const size_t min_log_bytes =
150 Compress(first_kept).length() + Compress(last_kept).length() +
151 (log_count - 4) * Compress(blank_log).length();
152 TestPersistedLogs persisted_logs(&prefs_, min_log_bytes);
154 persisted_logs.StoreLog("one");
155 persisted_logs.StoreLog("two");
156 persisted_logs.StoreLog(first_kept);
157 for (size_t i = persisted_logs.size(); i < log_count - 1; ++i) {
158 persisted_logs.StoreLog(blank_log);
160 persisted_logs.StoreLog(last_kept);
161 persisted_logs.SerializeLogs();
163 TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
164 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
165 result_persisted_logs.DeserializeLogs());
166 EXPECT_EQ(persisted_logs.size() - 2, result_persisted_logs.size());
168 result_persisted_logs.ExpectNextLog(last_kept);
169 while (result_persisted_logs.size() > 1) {
170 result_persisted_logs.ExpectNextLog(blank_log);
172 result_persisted_logs.ExpectNextLog(first_kept);
175 // Store a set of logs within the length limit, but well over the minimum
177 TEST_F(PersistedLogsTest, ShortButLargeLogList) {
178 // Make the total byte count about twice the minimum.
179 size_t log_count = kLogCountLimit;
180 size_t log_size = (kLogByteLimit / log_count) * 2;
181 std::string log_data = GenerateLogWithMinCompressedSize(log_size);
183 TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
184 for (size_t i = 0; i < log_count; ++i) {
185 persisted_logs.StoreLog(log_data);
187 persisted_logs.SerializeLogs();
189 TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
190 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
191 result_persisted_logs.DeserializeLogs());
192 EXPECT_EQ(persisted_logs.size(), result_persisted_logs.size());
195 // Store a set of logs over the length limit, and over the minimum number of
197 TEST_F(PersistedLogsTest, LongAndLargeLogList) {
198 TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
200 // Include twice the max number of logs.
201 size_t log_count = kLogCountLimit * 2;
202 // Make the total byte count about four times the minimum.
203 size_t log_size = (kLogByteLimit / log_count) * 4;
205 std::string target_log = "First to keep";
206 target_log += GenerateLogWithMinCompressedSize(log_size);
208 std::string log_data = GenerateLogWithMinCompressedSize(log_size);
209 for (size_t i = 0; i < log_count; ++i) {
210 if (i == log_count - kLogCountLimit)
211 persisted_logs.StoreLog(target_log);
213 persisted_logs.StoreLog(log_data);
216 persisted_logs.SerializeLogs();
218 TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
219 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
220 result_persisted_logs.DeserializeLogs());
221 EXPECT_EQ(kLogCountLimit, result_persisted_logs.size());
223 while (result_persisted_logs.size() > 1) {
224 result_persisted_logs.ExpectNextLog(log_data);
226 result_persisted_logs.ExpectNextLog(target_log);
229 // Check that the store/stage/discard functions work as expected.
230 TEST_F(PersistedLogsTest, Staging) {
231 TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
234 EXPECT_FALSE(persisted_logs.has_staged_log());
235 persisted_logs.StoreLog("one");
236 EXPECT_FALSE(persisted_logs.has_staged_log());
237 persisted_logs.StoreLog("two");
238 persisted_logs.StageLog();
239 EXPECT_TRUE(persisted_logs.has_staged_log());
240 EXPECT_EQ(persisted_logs.staged_log(), Compress("two"));
241 persisted_logs.StoreLog("three");
242 EXPECT_EQ(persisted_logs.staged_log(), Compress("two"));
243 EXPECT_EQ(persisted_logs.size(), 3U);
244 persisted_logs.DiscardStagedLog();
245 EXPECT_FALSE(persisted_logs.has_staged_log());
246 EXPECT_EQ(persisted_logs.size(), 2U);
247 persisted_logs.StageLog();
248 EXPECT_EQ(persisted_logs.staged_log(), Compress("three"));
249 persisted_logs.DiscardStagedLog();
250 persisted_logs.StageLog();
251 EXPECT_EQ(persisted_logs.staged_log(), Compress("one"));
252 persisted_logs.DiscardStagedLog();
253 EXPECT_FALSE(persisted_logs.has_staged_log());
254 EXPECT_EQ(persisted_logs.size(), 0U);
257 TEST_F(PersistedLogsTest, DiscardOrder) {
258 // Ensure that the correct log is discarded if new logs are pushed while
260 TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
262 persisted_logs.StoreLog("one");
263 persisted_logs.StageLog();
264 persisted_logs.StoreLog("two");
265 persisted_logs.DiscardStagedLog();
266 persisted_logs.SerializeLogs();
268 TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
269 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
270 result_persisted_logs.DeserializeLogs());
271 EXPECT_EQ(1U, result_persisted_logs.size());
272 result_persisted_logs.ExpectNextLog("two");
276 TEST_F(PersistedLogsTest, Hashes) {
277 const char kFooText[] = "foo";
278 const std::string foo_hash = base::SHA1HashString(kFooText);
280 TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
281 persisted_logs.StoreLog(kFooText);
282 persisted_logs.StageLog();
284 EXPECT_EQ(Compress(kFooText), persisted_logs.staged_log());
285 EXPECT_EQ(foo_hash, persisted_logs.staged_log_hash());
288 } // namespace metrics