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 size_t kLogCountLimit
= 3;
23 const size_t kLogByteLimit
= 1000;
25 // Compresses |log_data| and returns the result.
26 std::string
Compress(const std::string
& log_data
) {
27 std::string compressed_log_data
;
28 EXPECT_TRUE(GzipCompress(log_data
, &compressed_log_data
));
29 return compressed_log_data
;
32 // Generates and returns log data such that its size after compression is at
33 // least |min_compressed_size|.
34 std::string
GenerateLogWithMinCompressedSize(size_t min_compressed_size
) {
35 // Since the size check is done against a compressed log, generate enough
36 // data that compresses to larger than |log_size|.
37 std::string rand_bytes
= base::RandBytesAsString(min_compressed_size
);
38 while (Compress(rand_bytes
).size() < min_compressed_size
)
39 rand_bytes
.append(base::RandBytesAsString(min_compressed_size
));
40 std::string base64_data_for_logging
;
41 base::Base64Encode(rand_bytes
, &base64_data_for_logging
);
42 SCOPED_TRACE(testing::Message() << "Using random data "
43 << base64_data_for_logging
);
47 class PersistedLogsTest
: public testing::Test
{
50 prefs_
.registry()->RegisterListPref(kTestPrefName
);
54 TestingPrefServiceSimple prefs_
;
57 DISALLOW_COPY_AND_ASSIGN(PersistedLogsTest
);
60 class TestPersistedLogs
: public PersistedLogs
{
62 TestPersistedLogs(PrefService
* service
, size_t min_log_bytes
)
63 : PersistedLogs(service
, kTestPrefName
, kLogCountLimit
, min_log_bytes
,
67 // Stages and removes the next log, while testing it's value.
68 void ExpectNextLog(const std::string
& expected_log
) {
70 EXPECT_EQ(staged_log(), Compress(expected_log
));
75 DISALLOW_COPY_AND_ASSIGN(TestPersistedLogs
);
80 // Store and retrieve empty list_value.
81 TEST_F(PersistedLogsTest
, EmptyLogList
) {
82 TestPersistedLogs
persisted_logs(&prefs_
, kLogByteLimit
);
84 persisted_logs
.SerializeLogs();
85 const base::ListValue
* list_value
= prefs_
.GetList(kTestPrefName
);
86 EXPECT_EQ(0U, list_value
->GetSize());
88 TestPersistedLogs
result_persisted_logs(&prefs_
, kLogByteLimit
);
89 EXPECT_EQ(PersistedLogs::LIST_EMPTY
, result_persisted_logs
.DeserializeLogs());
90 EXPECT_EQ(0U, result_persisted_logs
.size());
93 // Store and retrieve a single log value.
94 TEST_F(PersistedLogsTest
, SingleElementLogList
) {
95 TestPersistedLogs
persisted_logs(&prefs_
, kLogByteLimit
);
97 persisted_logs
.StoreLog("Hello world!");
98 persisted_logs
.SerializeLogs();
100 TestPersistedLogs
result_persisted_logs(&prefs_
, kLogByteLimit
);
101 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS
,
102 result_persisted_logs
.DeserializeLogs());
103 EXPECT_EQ(1U, result_persisted_logs
.size());
105 // Verify that the result log matches the initial log.
106 persisted_logs
.StageLog();
107 result_persisted_logs
.StageLog();
108 EXPECT_EQ(persisted_logs
.staged_log(), result_persisted_logs
.staged_log());
109 EXPECT_EQ(persisted_logs
.staged_log_hash(),
110 result_persisted_logs
.staged_log_hash());
113 // Store a set of logs over the length limit, but smaller than the min number of
115 TEST_F(PersistedLogsTest
, LongButTinyLogList
) {
116 TestPersistedLogs
persisted_logs(&prefs_
, kLogByteLimit
);
118 size_t log_count
= kLogCountLimit
* 5;
119 for (size_t i
= 0; i
< log_count
; ++i
)
120 persisted_logs
.StoreLog("x");
122 persisted_logs
.SerializeLogs();
124 TestPersistedLogs
result_persisted_logs(&prefs_
, kLogByteLimit
);
125 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS
,
126 result_persisted_logs
.DeserializeLogs());
127 EXPECT_EQ(persisted_logs
.size(), result_persisted_logs
.size());
129 result_persisted_logs
.ExpectNextLog("x");
132 // Store a set of logs over the length limit, but that doesn't reach the minimum
133 // number of bytes until after passing the length limit.
134 TEST_F(PersistedLogsTest
, LongButSmallLogList
) {
135 size_t log_count
= kLogCountLimit
* 5;
136 size_t log_size
= 50;
138 std::string first_kept
= "First to keep";
139 first_kept
.resize(log_size
, ' ');
141 std::string blank_log
= std::string(log_size
, ' ');
143 std::string last_kept
= "Last to keep";
144 last_kept
.resize(log_size
, ' ');
146 // Set the byte limit enough to keep everything but the first two logs.
147 const size_t min_log_bytes
=
148 Compress(first_kept
).length() + Compress(last_kept
).length() +
149 (log_count
- 4) * Compress(blank_log
).length();
150 TestPersistedLogs
persisted_logs(&prefs_
, min_log_bytes
);
152 persisted_logs
.StoreLog("one");
153 persisted_logs
.StoreLog("two");
154 persisted_logs
.StoreLog(first_kept
);
155 for (size_t i
= persisted_logs
.size(); i
< log_count
- 1; ++i
) {
156 persisted_logs
.StoreLog(blank_log
);
158 persisted_logs
.StoreLog(last_kept
);
159 persisted_logs
.SerializeLogs();
161 TestPersistedLogs
result_persisted_logs(&prefs_
, kLogByteLimit
);
162 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS
,
163 result_persisted_logs
.DeserializeLogs());
164 EXPECT_EQ(persisted_logs
.size() - 2, result_persisted_logs
.size());
166 result_persisted_logs
.ExpectNextLog(last_kept
);
167 while (result_persisted_logs
.size() > 1) {
168 result_persisted_logs
.ExpectNextLog(blank_log
);
170 result_persisted_logs
.ExpectNextLog(first_kept
);
173 // Store a set of logs within the length limit, but well over the minimum
175 TEST_F(PersistedLogsTest
, ShortButLargeLogList
) {
176 // Make the total byte count about twice the minimum.
177 size_t log_count
= kLogCountLimit
;
178 size_t log_size
= (kLogByteLimit
/ log_count
) * 2;
179 std::string log_data
= GenerateLogWithMinCompressedSize(log_size
);
181 TestPersistedLogs
persisted_logs(&prefs_
, kLogByteLimit
);
182 for (size_t i
= 0; i
< log_count
; ++i
) {
183 persisted_logs
.StoreLog(log_data
);
185 persisted_logs
.SerializeLogs();
187 TestPersistedLogs
result_persisted_logs(&prefs_
, kLogByteLimit
);
188 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS
,
189 result_persisted_logs
.DeserializeLogs());
190 EXPECT_EQ(persisted_logs
.size(), result_persisted_logs
.size());
193 // Store a set of logs over the length limit, and over the minimum number of
195 TEST_F(PersistedLogsTest
, LongAndLargeLogList
) {
196 TestPersistedLogs
persisted_logs(&prefs_
, kLogByteLimit
);
198 // Include twice the max number of logs.
199 size_t log_count
= kLogCountLimit
* 2;
200 // Make the total byte count about four times the minimum.
201 size_t log_size
= (kLogByteLimit
/ log_count
) * 4;
203 std::string target_log
= "First to keep";
204 target_log
+= GenerateLogWithMinCompressedSize(log_size
);
206 std::string log_data
= GenerateLogWithMinCompressedSize(log_size
);
207 for (size_t i
= 0; i
< log_count
; ++i
) {
208 if (i
== log_count
- kLogCountLimit
)
209 persisted_logs
.StoreLog(target_log
);
211 persisted_logs
.StoreLog(log_data
);
214 persisted_logs
.SerializeLogs();
216 TestPersistedLogs
result_persisted_logs(&prefs_
, kLogByteLimit
);
217 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS
,
218 result_persisted_logs
.DeserializeLogs());
219 EXPECT_EQ(kLogCountLimit
, result_persisted_logs
.size());
221 while (result_persisted_logs
.size() > 1) {
222 result_persisted_logs
.ExpectNextLog(log_data
);
224 result_persisted_logs
.ExpectNextLog(target_log
);
227 // Check that the store/stage/discard functions work as expected.
228 TEST_F(PersistedLogsTest
, Staging
) {
229 TestPersistedLogs
persisted_logs(&prefs_
, kLogByteLimit
);
232 EXPECT_FALSE(persisted_logs
.has_staged_log());
233 persisted_logs
.StoreLog("one");
234 EXPECT_FALSE(persisted_logs
.has_staged_log());
235 persisted_logs
.StoreLog("two");
236 persisted_logs
.StageLog();
237 EXPECT_TRUE(persisted_logs
.has_staged_log());
238 EXPECT_EQ(persisted_logs
.staged_log(), Compress("two"));
239 persisted_logs
.StoreLog("three");
240 EXPECT_EQ(persisted_logs
.staged_log(), Compress("two"));
241 EXPECT_EQ(persisted_logs
.size(), 3U);
242 persisted_logs
.DiscardStagedLog();
243 EXPECT_FALSE(persisted_logs
.has_staged_log());
244 EXPECT_EQ(persisted_logs
.size(), 2U);
245 persisted_logs
.StageLog();
246 EXPECT_EQ(persisted_logs
.staged_log(), Compress("three"));
247 persisted_logs
.DiscardStagedLog();
248 persisted_logs
.StageLog();
249 EXPECT_EQ(persisted_logs
.staged_log(), Compress("one"));
250 persisted_logs
.DiscardStagedLog();
251 EXPECT_FALSE(persisted_logs
.has_staged_log());
252 EXPECT_EQ(persisted_logs
.size(), 0U);
255 TEST_F(PersistedLogsTest
, DiscardOrder
) {
256 // Ensure that the correct log is discarded if new logs are pushed while
258 TestPersistedLogs
persisted_logs(&prefs_
, kLogByteLimit
);
260 persisted_logs
.StoreLog("one");
261 persisted_logs
.StageLog();
262 persisted_logs
.StoreLog("two");
263 persisted_logs
.DiscardStagedLog();
264 persisted_logs
.SerializeLogs();
266 TestPersistedLogs
result_persisted_logs(&prefs_
, kLogByteLimit
);
267 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS
,
268 result_persisted_logs
.DeserializeLogs());
269 EXPECT_EQ(1U, result_persisted_logs
.size());
270 result_persisted_logs
.ExpectNextLog("two");
274 TEST_F(PersistedLogsTest
, Hashes
) {
275 const char kFooText
[] = "foo";
276 const std::string foo_hash
= base::SHA1HashString(kFooText
);
278 TestPersistedLogs
persisted_logs(&prefs_
, kLogByteLimit
);
279 persisted_logs
.StoreLog(kFooText
);
280 persisted_logs
.StageLog();
282 EXPECT_EQ(Compress(kFooText
), persisted_logs
.staged_log());
283 EXPECT_EQ(foo_hash
, persisted_logs
.staged_log_hash());
286 } // namespace metrics