blob: b4a79a73ea626e9c02db9d0938bb987c4907d537 [file] [log] [blame]
[email protected]7f07db62014-05-15 01:12:451// 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.
4
[email protected]9706e1b2014-06-11 16:31:245#include "components/metrics/persisted_logs.h"
6
avi26062922015-12-26 00:14:187#include <stddef.h>
8
[email protected]7f07db62014-05-15 01:12:459#include "base/base64.h"
avi26062922015-12-26 00:14:1810#include "base/macros.h"
[email protected]9706e1b2014-06-11 16:31:2411#include "base/rand_util.h"
[email protected]7f07db62014-05-15 01:12:4512#include "base/sha1.h"
13#include "base/values.h"
brettwf00b9b42016-02-01 22:11:3814#include "components/prefs/pref_registry_simple.h"
15#include "components/prefs/scoped_user_pref_update.h"
16#include "components/prefs/testing_pref_service.h"
[email protected]7f07db62014-05-15 01:12:4517#include "testing/gtest/include/gtest/gtest.h"
hajimehoshi1e1cc1a2016-01-14 19:09:0018#include "third_party/zlib/google/compression_utils.h"
[email protected]7f07db62014-05-15 01:12:4519
20namespace metrics {
21
22namespace {
23
24const char kTestPrefName[] = "TestPref";
25const size_t kLogCountLimit = 3;
26const size_t kLogByteLimit = 1000;
27
[email protected]9706e1b2014-06-11 16:31:2428// Compresses |log_data| and returns the result.
29std::string Compress(const std::string& log_data) {
30 std::string compressed_log_data;
blundell64fedf12015-08-25 12:22:2731 EXPECT_TRUE(compression::GzipCompress(log_data, &compressed_log_data));
[email protected]9706e1b2014-06-11 16:31:2432 return compressed_log_data;
33}
34
35// Generates and returns log data such that its size after compression is at
36// least |min_compressed_size|.
37std::string GenerateLogWithMinCompressedSize(size_t min_compressed_size) {
38 // Since the size check is done against a compressed log, generate enough
39 // data that compresses to larger than |log_size|.
40 std::string rand_bytes = base::RandBytesAsString(min_compressed_size);
41 while (Compress(rand_bytes).size() < min_compressed_size)
42 rand_bytes.append(base::RandBytesAsString(min_compressed_size));
43 std::string base64_data_for_logging;
44 base::Base64Encode(rand_bytes, &base64_data_for_logging);
45 SCOPED_TRACE(testing::Message() << "Using random data "
46 << base64_data_for_logging);
47 return rand_bytes;
48}
[email protected]7f07db62014-05-15 01:12:4549
50class PersistedLogsTest : public testing::Test {
51 public:
52 PersistedLogsTest() {
53 prefs_.registry()->RegisterListPref(kTestPrefName);
54 }
55
56 protected:
57 TestingPrefServiceSimple prefs_;
58
59 private:
60 DISALLOW_COPY_AND_ASSIGN(PersistedLogsTest);
61};
62
63class TestPersistedLogs : public PersistedLogs {
64 public:
[email protected]9706e1b2014-06-11 16:31:2465 TestPersistedLogs(PrefService* service, size_t min_log_bytes)
[email protected]f61623e2014-08-19 16:25:5766 : PersistedLogs(service, kTestPrefName, kLogCountLimit, min_log_bytes,
67 0) {
[email protected]7f07db62014-05-15 01:12:4568 }
69
70 // Stages and removes the next log, while testing it's value.
71 void ExpectNextLog(const std::string& expected_log) {
72 StageLog();
[email protected]9706e1b2014-06-11 16:31:2473 EXPECT_EQ(staged_log(), Compress(expected_log));
[email protected]7f07db62014-05-15 01:12:4574 DiscardStagedLog();
75 }
[email protected]9706e1b2014-06-11 16:31:2476
77 private:
78 DISALLOW_COPY_AND_ASSIGN(TestPersistedLogs);
[email protected]7f07db62014-05-15 01:12:4579};
80
81} // namespace
82
83// Store and retrieve empty list_value.
84TEST_F(PersistedLogsTest, EmptyLogList) {
[email protected]9706e1b2014-06-11 16:31:2485 TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
[email protected]7f07db62014-05-15 01:12:4586
87 persisted_logs.SerializeLogs();
88 const base::ListValue* list_value = prefs_.GetList(kTestPrefName);
89 EXPECT_EQ(0U, list_value->GetSize());
90
[email protected]9706e1b2014-06-11 16:31:2491 TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
[email protected]7f07db62014-05-15 01:12:4592 EXPECT_EQ(PersistedLogs::LIST_EMPTY, result_persisted_logs.DeserializeLogs());
93 EXPECT_EQ(0U, result_persisted_logs.size());
94}
95
96// Store and retrieve a single log value.
97TEST_F(PersistedLogsTest, SingleElementLogList) {
[email protected]9706e1b2014-06-11 16:31:2498 TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
[email protected]7f07db62014-05-15 01:12:4599
[email protected]9706e1b2014-06-11 16:31:24100 persisted_logs.StoreLog("Hello world!");
[email protected]7f07db62014-05-15 01:12:45101 persisted_logs.SerializeLogs();
102
[email protected]9706e1b2014-06-11 16:31:24103 TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
[email protected]7f07db62014-05-15 01:12:45104 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
105 result_persisted_logs.DeserializeLogs());
106 EXPECT_EQ(1U, result_persisted_logs.size());
[email protected]9706e1b2014-06-11 16:31:24107
108 // Verify that the result log matches the initial log.
109 persisted_logs.StageLog();
110 result_persisted_logs.StageLog();
111 EXPECT_EQ(persisted_logs.staged_log(), result_persisted_logs.staged_log());
112 EXPECT_EQ(persisted_logs.staged_log_hash(),
113 result_persisted_logs.staged_log_hash());
[email protected]7f07db62014-05-15 01:12:45114}
115
116// Store a set of logs over the length limit, but smaller than the min number of
117// bytes.
118TEST_F(PersistedLogsTest, LongButTinyLogList) {
[email protected]9706e1b2014-06-11 16:31:24119 TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
[email protected]7f07db62014-05-15 01:12:45120
121 size_t log_count = kLogCountLimit * 5;
122 for (size_t i = 0; i < log_count; ++i)
[email protected]9706e1b2014-06-11 16:31:24123 persisted_logs.StoreLog("x");
[email protected]7f07db62014-05-15 01:12:45124
125 persisted_logs.SerializeLogs();
126
[email protected]9706e1b2014-06-11 16:31:24127 TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
[email protected]7f07db62014-05-15 01:12:45128 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
129 result_persisted_logs.DeserializeLogs());
130 EXPECT_EQ(persisted_logs.size(), result_persisted_logs.size());
131
132 result_persisted_logs.ExpectNextLog("x");
133}
134
135// Store a set of logs over the length limit, but that doesn't reach the minimum
136// number of bytes until after passing the length limit.
137TEST_F(PersistedLogsTest, LongButSmallLogList) {
[email protected]7f07db62014-05-15 01:12:45138 size_t log_count = kLogCountLimit * 5;
[email protected]9706e1b2014-06-11 16:31:24139 size_t log_size = 50;
140
[email protected]7f07db62014-05-15 01:12:45141 std::string first_kept = "First to keep";
142 first_kept.resize(log_size, ' ');
[email protected]9706e1b2014-06-11 16:31:24143
[email protected]7f07db62014-05-15 01:12:45144 std::string blank_log = std::string(log_size, ' ');
[email protected]9706e1b2014-06-11 16:31:24145
[email protected]7f07db62014-05-15 01:12:45146 std::string last_kept = "Last to keep";
147 last_kept.resize(log_size, ' ');
[email protected]7f07db62014-05-15 01:12:45148
[email protected]9706e1b2014-06-11 16:31:24149 // Set the byte limit enough to keep everything but the first two logs.
150 const size_t min_log_bytes =
151 Compress(first_kept).length() + Compress(last_kept).length() +
152 (log_count - 4) * Compress(blank_log).length();
153 TestPersistedLogs persisted_logs(&prefs_, min_log_bytes);
154
155 persisted_logs.StoreLog("one");
156 persisted_logs.StoreLog("two");
157 persisted_logs.StoreLog(first_kept);
158 for (size_t i = persisted_logs.size(); i < log_count - 1; ++i) {
159 persisted_logs.StoreLog(blank_log);
160 }
161 persisted_logs.StoreLog(last_kept);
[email protected]7f07db62014-05-15 01:12:45162 persisted_logs.SerializeLogs();
163
[email protected]9706e1b2014-06-11 16:31:24164 TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
[email protected]7f07db62014-05-15 01:12:45165 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
166 result_persisted_logs.DeserializeLogs());
167 EXPECT_EQ(persisted_logs.size() - 2, result_persisted_logs.size());
168
169 result_persisted_logs.ExpectNextLog(last_kept);
170 while (result_persisted_logs.size() > 1) {
171 result_persisted_logs.ExpectNextLog(blank_log);
172 }
173 result_persisted_logs.ExpectNextLog(first_kept);
174}
175
176// Store a set of logs within the length limit, but well over the minimum
177// number of bytes.
178TEST_F(PersistedLogsTest, ShortButLargeLogList) {
[email protected]7f07db62014-05-15 01:12:45179 // Make the total byte count about twice the minimum.
180 size_t log_count = kLogCountLimit;
181 size_t log_size = (kLogByteLimit / log_count) * 2;
[email protected]9706e1b2014-06-11 16:31:24182 std::string log_data = GenerateLogWithMinCompressedSize(log_size);
[email protected]7f07db62014-05-15 01:12:45183
[email protected]9706e1b2014-06-11 16:31:24184 TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
185 for (size_t i = 0; i < log_count; ++i) {
186 persisted_logs.StoreLog(log_data);
187 }
[email protected]7f07db62014-05-15 01:12:45188 persisted_logs.SerializeLogs();
189
[email protected]9706e1b2014-06-11 16:31:24190 TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
[email protected]7f07db62014-05-15 01:12:45191 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
192 result_persisted_logs.DeserializeLogs());
193 EXPECT_EQ(persisted_logs.size(), result_persisted_logs.size());
194}
195
196// Store a set of logs over the length limit, and over the minimum number of
197// bytes.
198TEST_F(PersistedLogsTest, LongAndLargeLogList) {
[email protected]9706e1b2014-06-11 16:31:24199 TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
[email protected]7f07db62014-05-15 01:12:45200
201 // Include twice the max number of logs.
202 size_t log_count = kLogCountLimit * 2;
203 // Make the total byte count about four times the minimum.
204 size_t log_size = (kLogByteLimit / log_count) * 4;
[email protected]9706e1b2014-06-11 16:31:24205
[email protected]7f07db62014-05-15 01:12:45206 std::string target_log = "First to keep";
[email protected]9706e1b2014-06-11 16:31:24207 target_log += GenerateLogWithMinCompressedSize(log_size);
208
209 std::string log_data = GenerateLogWithMinCompressedSize(log_size);
[email protected]7f07db62014-05-15 01:12:45210 for (size_t i = 0; i < log_count; ++i) {
211 if (i == log_count - kLogCountLimit)
[email protected]9706e1b2014-06-11 16:31:24212 persisted_logs.StoreLog(target_log);
[email protected]7f07db62014-05-15 01:12:45213 else
[email protected]9706e1b2014-06-11 16:31:24214 persisted_logs.StoreLog(log_data);
[email protected]7f07db62014-05-15 01:12:45215 }
216
217 persisted_logs.SerializeLogs();
218
[email protected]9706e1b2014-06-11 16:31:24219 TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
[email protected]7f07db62014-05-15 01:12:45220 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
221 result_persisted_logs.DeserializeLogs());
222 EXPECT_EQ(kLogCountLimit, result_persisted_logs.size());
223
224 while (result_persisted_logs.size() > 1) {
[email protected]9706e1b2014-06-11 16:31:24225 result_persisted_logs.ExpectNextLog(log_data);
[email protected]7f07db62014-05-15 01:12:45226 }
227 result_persisted_logs.ExpectNextLog(target_log);
228}
229
[email protected]7f07db62014-05-15 01:12:45230// Check that the store/stage/discard functions work as expected.
231TEST_F(PersistedLogsTest, Staging) {
[email protected]9706e1b2014-06-11 16:31:24232 TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
[email protected]7f07db62014-05-15 01:12:45233 std::string tmp;
234
235 EXPECT_FALSE(persisted_logs.has_staged_log());
[email protected]9706e1b2014-06-11 16:31:24236 persisted_logs.StoreLog("one");
[email protected]7f07db62014-05-15 01:12:45237 EXPECT_FALSE(persisted_logs.has_staged_log());
[email protected]9706e1b2014-06-11 16:31:24238 persisted_logs.StoreLog("two");
[email protected]7f07db62014-05-15 01:12:45239 persisted_logs.StageLog();
240 EXPECT_TRUE(persisted_logs.has_staged_log());
[email protected]9706e1b2014-06-11 16:31:24241 EXPECT_EQ(persisted_logs.staged_log(), Compress("two"));
242 persisted_logs.StoreLog("three");
243 EXPECT_EQ(persisted_logs.staged_log(), Compress("two"));
[email protected]faa8d222014-07-25 21:30:26244 EXPECT_EQ(persisted_logs.size(), 3U);
[email protected]7f07db62014-05-15 01:12:45245 persisted_logs.DiscardStagedLog();
246 EXPECT_FALSE(persisted_logs.has_staged_log());
247 EXPECT_EQ(persisted_logs.size(), 2U);
248 persisted_logs.StageLog();
[email protected]9706e1b2014-06-11 16:31:24249 EXPECT_EQ(persisted_logs.staged_log(), Compress("three"));
[email protected]7f07db62014-05-15 01:12:45250 persisted_logs.DiscardStagedLog();
251 persisted_logs.StageLog();
[email protected]9706e1b2014-06-11 16:31:24252 EXPECT_EQ(persisted_logs.staged_log(), Compress("one"));
[email protected]7f07db62014-05-15 01:12:45253 persisted_logs.DiscardStagedLog();
254 EXPECT_FALSE(persisted_logs.has_staged_log());
255 EXPECT_EQ(persisted_logs.size(), 0U);
256}
257
[email protected]faa8d222014-07-25 21:30:26258TEST_F(PersistedLogsTest, DiscardOrder) {
259 // Ensure that the correct log is discarded if new logs are pushed while
260 // a log is staged.
[email protected]9706e1b2014-06-11 16:31:24261 TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
[email protected]7f07db62014-05-15 01:12:45262
[email protected]9706e1b2014-06-11 16:31:24263 persisted_logs.StoreLog("one");
[email protected]7f07db62014-05-15 01:12:45264 persisted_logs.StageLog();
[email protected]9706e1b2014-06-11 16:31:24265 persisted_logs.StoreLog("two");
[email protected]7f07db62014-05-15 01:12:45266 persisted_logs.DiscardStagedLog();
[email protected]7f07db62014-05-15 01:12:45267 persisted_logs.SerializeLogs();
268
[email protected]9706e1b2014-06-11 16:31:24269 TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
[email protected]7f07db62014-05-15 01:12:45270 EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
271 result_persisted_logs.DeserializeLogs());
272 EXPECT_EQ(1U, result_persisted_logs.size());
273 result_persisted_logs.ExpectNextLog("two");
274}
275
[email protected]7f07db62014-05-15 01:12:45276
[email protected]faa8d222014-07-25 21:30:26277TEST_F(PersistedLogsTest, Hashes) {
[email protected]7f07db62014-05-15 01:12:45278 const char kFooText[] = "foo";
279 const std::string foo_hash = base::SHA1HashString(kFooText);
[email protected]9706e1b2014-06-11 16:31:24280
281 TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
282 persisted_logs.StoreLog(kFooText);
[email protected]7f07db62014-05-15 01:12:45283 persisted_logs.StageLog();
[email protected]9706e1b2014-06-11 16:31:24284
285 EXPECT_EQ(Compress(kFooText), persisted_logs.staged_log());
[email protected]7f07db62014-05-15 01:12:45286 EXPECT_EQ(foo_hash, persisted_logs.staged_log_hash());
287}
288
289} // namespace metrics