1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/metrics/unsent_log_store.h"
6
7 #include <stddef.h>
8 #include <limits>
9
10 #include "base/base64.h"
11 #include "base/hash/sha1.h"
12 #include "base/rand_util.h"
13 #include "base/values.h"
14 #include "components/metrics/unsent_log_store_metrics_impl.h"
15 #include "components/prefs/pref_registry_simple.h"
16 #include "components/prefs/scoped_user_pref_update.h"
17 #include "components/prefs/testing_pref_service.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/zlib/google/compression_utils.h"
20
21 namespace metrics {
22
23 namespace {
24
25 const char kTestPrefName[] = "TestPref";
26 const char kTestMetaDataPrefName[] = "TestMetaDataPref";
27
28 const size_t kLogCountLimit = 3;
29 const size_t kLogByteLimit = 1000;
30
31 // Compresses |log_data| and returns the result.
Compress(const std::string & log_data)32 std::string Compress(const std::string& log_data) {
33 std::string compressed_log_data;
34 EXPECT_TRUE(compression::GzipCompress(log_data, &compressed_log_data));
35 return compressed_log_data;
36 }
37
38 // Generates and returns log data such that its size after compression is at
39 // least |min_compressed_size|.
GenerateLogWithMinCompressedSize(size_t min_compressed_size)40 std::string GenerateLogWithMinCompressedSize(size_t min_compressed_size) {
41 // Since the size check is done against a compressed log, generate enough
42 // data that compresses to larger than |log_size|.
43 std::string rand_bytes = base::RandBytesAsString(min_compressed_size);
44 while (Compress(rand_bytes).size() < min_compressed_size)
45 rand_bytes.append(base::RandBytesAsString(min_compressed_size));
46 std::string base64_data_for_logging;
47 base::Base64Encode(rand_bytes, &base64_data_for_logging);
48 SCOPED_TRACE(testing::Message()
49 << "Using random data " << base64_data_for_logging);
50 return rand_bytes;
51 }
52
53 class UnsentLogStoreTest : public testing::Test {
54 public:
UnsentLogStoreTest()55 UnsentLogStoreTest() {
56 prefs_.registry()->RegisterListPref(kTestPrefName);
57 prefs_.registry()->RegisterDictionaryPref(kTestMetaDataPrefName);
58 }
59
60 UnsentLogStoreTest(const UnsentLogStoreTest&) = delete;
61 UnsentLogStoreTest& operator=(const UnsentLogStoreTest&) = delete;
62
63 protected:
64 TestingPrefServiceSimple prefs_;
65 };
66
67 class TestUnsentLogStoreMetrics : public UnsentLogStoreMetrics {
68 public:
69 TestUnsentLogStoreMetrics() = default;
70
RecordLastUnsentLogMetadataMetrics(int unsent_samples_count,int sent_samples_count,int persisted_size_in_kb)71 void RecordLastUnsentLogMetadataMetrics(int unsent_samples_count,
72 int sent_samples_count,
73 int persisted_size_in_kb) override {
74 unsent_samples_count_ = unsent_samples_count;
75 sent_samples_count_ = sent_samples_count;
76 persisted_size_in_kb_ = persisted_size_in_kb;
77 }
78
unsent_samples_count() const79 int unsent_samples_count() const { return unsent_samples_count_; }
sent_samples_count() const80 int sent_samples_count() const { return sent_samples_count_; }
persisted_size_in_kb() const81 int persisted_size_in_kb() const { return persisted_size_in_kb_; }
82
83 private:
84 int unsent_samples_count_ = 0;
85 int sent_samples_count_ = 0;
86 int persisted_size_in_kb_ = 0;
87 };
88
89 class TestUnsentLogStore : public UnsentLogStore {
90 public:
TestUnsentLogStore(PrefService * service,size_t min_log_bytes)91 TestUnsentLogStore(PrefService* service, size_t min_log_bytes)
92 : UnsentLogStore(std::make_unique<UnsentLogStoreMetricsImpl>(),
93 service,
94 kTestPrefName,
95 /*metadata_pref_name=*/nullptr,
96 kLogCountLimit,
97 min_log_bytes,
98 /*max_log_size=*/0,
99 /*signing_key=*/std::string(),
100 /*logs_event_manager=*/nullptr) {}
TestUnsentLogStore(PrefService * service,size_t min_log_bytes,const std::string & signing_key)101 TestUnsentLogStore(PrefService* service,
102 size_t min_log_bytes,
103 const std::string& signing_key)
104 : UnsentLogStore(std::make_unique<UnsentLogStoreMetricsImpl>(),
105 service,
106 kTestPrefName,
107 /*metadata_pref_name=*/nullptr,
108 kLogCountLimit,
109 min_log_bytes,
110 /*max_log_size=*/0,
111 signing_key,
112 /*logs_event_manager=*/nullptr) {}
TestUnsentLogStore(std::unique_ptr<UnsentLogStoreMetrics> metrics,PrefService * service,size_t max_log_size)113 TestUnsentLogStore(std::unique_ptr<UnsentLogStoreMetrics> metrics,
114 PrefService* service,
115 size_t max_log_size)
116 : UnsentLogStore(std::move(metrics),
117 service,
118 kTestPrefName,
119 kTestMetaDataPrefName,
120 kLogCountLimit,
121 /*min_log_bytes=*/1,
122 max_log_size,
123 /*signing_key=*/std::string(),
124 /*logs_event_manager=*/nullptr) {}
125
126 TestUnsentLogStore(const TestUnsentLogStore&) = delete;
127 TestUnsentLogStore& operator=(const TestUnsentLogStore&) = delete;
128
129 // Stages and removes the next log, while testing it's value.
ExpectNextLog(const std::string & expected_log)130 void ExpectNextLog(const std::string& expected_log) {
131 StageNextLog();
132 EXPECT_EQ(staged_log(), Compress(expected_log));
133 DiscardStagedLog();
134 }
135 };
136
137 } // namespace
138
139 // Store and retrieve empty list_value.
TEST_F(UnsentLogStoreTest,EmptyLogList)140 TEST_F(UnsentLogStoreTest, EmptyLogList) {
141 TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
142
143 unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
144 EXPECT_EQ(0U, prefs_.GetList(kTestPrefName).size());
145
146 TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
147 result_unsent_log_store.LoadPersistedUnsentLogs();
148 EXPECT_EQ(0U, result_unsent_log_store.size());
149 }
150
151 // Store and retrieve a single log value.
TEST_F(UnsentLogStoreTest,SingleElementLogList)152 TEST_F(UnsentLogStoreTest, SingleElementLogList) {
153 TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
154
155 LogMetadata log_metadata;
156 unsent_log_store.StoreLog("Hello world!", log_metadata,
157 MetricsLogsEventManager::CreateReason::kUnknown);
158 unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
159
160 TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
161 result_unsent_log_store.LoadPersistedUnsentLogs();
162 EXPECT_EQ(1U, result_unsent_log_store.size());
163
164 // Verify that the result log matches the initial log.
165 unsent_log_store.StageNextLog();
166 result_unsent_log_store.StageNextLog();
167 EXPECT_EQ(unsent_log_store.staged_log(),
168 result_unsent_log_store.staged_log());
169 EXPECT_EQ(unsent_log_store.staged_log_hash(),
170 result_unsent_log_store.staged_log_hash());
171 EXPECT_EQ(unsent_log_store.staged_log_signature(),
172 result_unsent_log_store.staged_log_signature());
173 EXPECT_EQ(unsent_log_store.staged_log_timestamp(),
174 result_unsent_log_store.staged_log_timestamp());
175 }
176
177 // Store a set of logs over the length limit, but smaller than the min number of
178 // bytes. This should leave the logs unchanged.
TEST_F(UnsentLogStoreTest,LongButTinyLogList)179 TEST_F(UnsentLogStoreTest, LongButTinyLogList) {
180 TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
181 LogMetadata log_metadata;
182
183 size_t log_count = kLogCountLimit * 5;
184 for (size_t i = 0; i < log_count; ++i)
185 unsent_log_store.StoreLog("x", log_metadata,
186 MetricsLogsEventManager::CreateReason::kUnknown);
187
188 EXPECT_EQ(log_count, unsent_log_store.size());
189 unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
190 EXPECT_EQ(log_count, unsent_log_store.size());
191
192 TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
193 result_unsent_log_store.LoadPersistedUnsentLogs();
194 EXPECT_EQ(unsent_log_store.size(), result_unsent_log_store.size());
195
196 result_unsent_log_store.ExpectNextLog("x");
197 }
198
199 // Store a set of logs over the length limit, but that doesn't reach the minimum
200 // number of bytes until after passing the length limit.
TEST_F(UnsentLogStoreTest,LongButSmallLogList)201 TEST_F(UnsentLogStoreTest, LongButSmallLogList) {
202 size_t log_count = kLogCountLimit * 5;
203 size_t log_size = 50;
204 LogMetadata log_metadata;
205
206 std::string first_kept = "First to keep";
207 first_kept.resize(log_size, ' ');
208
209 std::string blank_log = std::string(log_size, ' ');
210
211 std::string last_kept = "Last to keep";
212 last_kept.resize(log_size, ' ');
213
214 // Set the byte limit enough to keep everything but the first two logs.
215 const size_t min_log_bytes = Compress(first_kept).length() +
216 Compress(last_kept).length() +
217 (log_count - 4) * Compress(blank_log).length();
218 TestUnsentLogStore unsent_log_store(&prefs_, min_log_bytes);
219
220 unsent_log_store.StoreLog("one", log_metadata,
221 MetricsLogsEventManager::CreateReason::kUnknown);
222 unsent_log_store.StoreLog("two", log_metadata,
223 MetricsLogsEventManager::CreateReason::kUnknown);
224 unsent_log_store.StoreLog(first_kept, log_metadata,
225 MetricsLogsEventManager::CreateReason::kUnknown);
226 for (size_t i = unsent_log_store.size(); i < log_count - 1; ++i) {
227 unsent_log_store.StoreLog(blank_log, log_metadata,
228 MetricsLogsEventManager::CreateReason::kUnknown);
229 }
230 unsent_log_store.StoreLog(last_kept, log_metadata,
231 MetricsLogsEventManager::CreateReason::kUnknown);
232
233 size_t original_size = unsent_log_store.size();
234 unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
235 // New size has been reduced.
236 EXPECT_EQ(original_size - 2, unsent_log_store.size());
237
238 TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
239 result_unsent_log_store.LoadPersistedUnsentLogs();
240 // Prefs should be the same size.
241 EXPECT_EQ(unsent_log_store.size(), result_unsent_log_store.size());
242
243 result_unsent_log_store.ExpectNextLog(last_kept);
244 while (result_unsent_log_store.size() > 1) {
245 result_unsent_log_store.ExpectNextLog(blank_log);
246 }
247 result_unsent_log_store.ExpectNextLog(first_kept);
248 }
249
250 // Store a set of logs within the length limit, but well over the minimum
251 // number of bytes. This should leave the logs unchanged.
TEST_F(UnsentLogStoreTest,ShortButLargeLogList)252 TEST_F(UnsentLogStoreTest, ShortButLargeLogList) {
253 // Make the total byte count about twice the minimum.
254 size_t log_count = kLogCountLimit;
255 size_t log_size = (kLogByteLimit / log_count) * 2;
256 std::string log_data = GenerateLogWithMinCompressedSize(log_size);
257
258 TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
259 LogMetadata log_metadata;
260 for (size_t i = 0; i < log_count; ++i) {
261 unsent_log_store.StoreLog(log_data, log_metadata,
262 MetricsLogsEventManager::CreateReason::kUnknown);
263 }
264 unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
265
266 TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
267 result_unsent_log_store.LoadPersistedUnsentLogs();
268 // Both have expected number of logs (original amount).
269 EXPECT_EQ(kLogCountLimit, unsent_log_store.size());
270 EXPECT_EQ(kLogCountLimit, result_unsent_log_store.size());
271 }
272
273 // Store a set of logs over the length limit, and over the minimum number of
274 // bytes. This will trim the set of logs.
TEST_F(UnsentLogStoreTest,LongAndLargeLogList)275 TEST_F(UnsentLogStoreTest, LongAndLargeLogList) {
276 TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
277
278 // Include twice the max number of logs.
279 size_t log_count = kLogCountLimit * 2;
280 // Make the total byte count about four times the minimum.
281 size_t log_size = (kLogByteLimit / log_count) * 4;
282
283 std::string target_log = "First to keep";
284 target_log += GenerateLogWithMinCompressedSize(log_size);
285
286 std::string log_data = GenerateLogWithMinCompressedSize(log_size);
287 LogMetadata log_metadata;
288 for (size_t i = 0; i < log_count; ++i) {
289 if (i == log_count - kLogCountLimit)
290 unsent_log_store.StoreLog(
291 target_log, log_metadata,
292 MetricsLogsEventManager::CreateReason::kUnknown);
293 else
294 unsent_log_store.StoreLog(
295 log_data, log_metadata,
296 MetricsLogsEventManager::CreateReason::kUnknown);
297 }
298
299 unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
300
301 TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
302 result_unsent_log_store.LoadPersistedUnsentLogs();
303 // Both original log and persisted are reduced to limit.
304 EXPECT_EQ(kLogCountLimit, unsent_log_store.size());
305 EXPECT_EQ(kLogCountLimit, result_unsent_log_store.size());
306
307 while (result_unsent_log_store.size() > 1) {
308 result_unsent_log_store.ExpectNextLog(log_data);
309 }
310 result_unsent_log_store.ExpectNextLog(target_log);
311 }
312
313 // Store a set of logs over the length limit, and over the minimum number of
314 // bytes. The first log will be a staged log that should be trimmed away. This
315 // should make the log store not have a staged log anymore.
TEST_F(UnsentLogStoreTest,TrimStagedLog)316 TEST_F(UnsentLogStoreTest, TrimStagedLog) {
317 TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
318
319 // Make each log byte count the limit.
320 size_t log_size = kLogByteLimit;
321
322 // Create a target log that will be the staged log that we want to trim away.
323 std::string target_log = "First that should be trimmed";
324 target_log += GenerateLogWithMinCompressedSize(log_size);
325 LogMetadata log_metadata;
326 unsent_log_store.StoreLog(target_log, log_metadata,
327 MetricsLogsEventManager::CreateReason::kUnknown);
328 unsent_log_store.StageNextLog();
329 EXPECT_TRUE(unsent_log_store.has_staged_log());
330
331 // Add |kLogCountLimit| additional logs.
332 std::string log_data = GenerateLogWithMinCompressedSize(log_size);
333 for (size_t i = 0; i < kLogCountLimit; ++i) {
334 unsent_log_store.StoreLog(log_data, log_metadata,
335 MetricsLogsEventManager::CreateReason::kUnknown);
336 }
337
338 EXPECT_EQ(kLogCountLimit + 1, unsent_log_store.size());
339 unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
340
341 // Verify that the first log (the staged one) was trimmed away, and that the
342 // log store does not consider to have any staged log anymore. The other logs
343 // are not trimmed because the most recent logs are prioritized and we trim
344 // until we have |kLogCountLimit| logs.
345 EXPECT_EQ(kLogCountLimit, unsent_log_store.size());
346 EXPECT_FALSE(unsent_log_store.has_staged_log());
347 // Verify that all of the logs in the log store are not the |target_log|.
348 while (unsent_log_store.size() > 0) {
349 unsent_log_store.ExpectNextLog(log_data);
350 }
351 }
352
353 // Verifies that when calling TrimAndPersistUnsentLogs() with
354 // |overwrite_in_memory_store| set to false, the in memory log store is
355 // unaffected.
TEST_F(UnsentLogStoreTest,TrimAndPersistUnsentLogs_DoNotOverwriteInMemoryStore)356 TEST_F(UnsentLogStoreTest,
357 TrimAndPersistUnsentLogs_DoNotOverwriteInMemoryStore) {
358 TestUnsentLogStore unsent_log_store(
359 std::make_unique<UnsentLogStoreMetricsImpl>(), &prefs_, kLogByteLimit);
360
361 LogMetadata log_metadata;
362 std::string log_data = GenerateLogWithMinCompressedSize(kLogByteLimit + 1);
363 unsent_log_store.StoreLog(log_data, log_metadata,
364 MetricsLogsEventManager::CreateReason::kUnknown);
365 unsent_log_store.TrimAndPersistUnsentLogs(
366 /*overwrite_in_memory_store=*/false);
367
368 // Verify that the log store still contains the log.
369 EXPECT_EQ(1U, unsent_log_store.size());
370 unsent_log_store.ExpectNextLog(log_data);
371
372 // Verify that the log was trimmed when persisted to memory.
373 TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
374 result_unsent_log_store.LoadPersistedUnsentLogs();
375 EXPECT_EQ(0U, result_unsent_log_store.size());
376 }
377
378 // Verifies that TrimAndPersistUnsentLogs() maintains the log order.
TEST_F(UnsentLogStoreTest,TrimAndPersistUnsentLogs_MaintainsLogOrder)379 TEST_F(UnsentLogStoreTest, TrimAndPersistUnsentLogs_MaintainsLogOrder) {
380 TestUnsentLogStore unsent_log_store(
381 std::make_unique<UnsentLogStoreMetricsImpl>(), &prefs_, kLogByteLimit);
382
383 LogMetadata log_metadata;
384 unsent_log_store.StoreLog("1", log_metadata,
385 MetricsLogsEventManager::CreateReason::kUnknown);
386 unsent_log_store.StoreLog(GenerateLogWithMinCompressedSize(kLogByteLimit + 1),
387 log_metadata,
388 MetricsLogsEventManager::CreateReason::kUnknown);
389 unsent_log_store.StoreLog("2", log_metadata,
390 MetricsLogsEventManager::CreateReason::kUnknown);
391 unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
392
393 // Verify that only the second log was trimmed (since it was over the max log
394 // size), and that the log order was maintained. I.e., "2" should be the most
395 // recent log, followed by "1".
396 EXPECT_EQ(2U, unsent_log_store.size());
397 unsent_log_store.ExpectNextLog("2");
398 unsent_log_store.ExpectNextLog("1");
399
400 // Similarly, verify that the order was also maintained in persistent memory.
401 TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
402 result_unsent_log_store.LoadPersistedUnsentLogs();
403 EXPECT_EQ(2U, result_unsent_log_store.size());
404 result_unsent_log_store.ExpectNextLog("2");
405 result_unsent_log_store.ExpectNextLog("1");
406 }
407
408 // Verifies that calling TrimAndPersistUnsentLogs() clears the pref list before
409 // writing the trimmed logs list.
TEST_F(UnsentLogStoreTest,TrimAndPersistUnsentLogs_OverwritesPrefs)410 TEST_F(UnsentLogStoreTest, TrimAndPersistUnsentLogs_OverwritesPrefs) {
411 TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
412
413 LogMetadata log_metadata;
414 unsent_log_store.StoreLog("Hello world!", log_metadata,
415 MetricsLogsEventManager::CreateReason::kUnknown);
416 // Call TrimAndPersistUnsentLogs(). The log should not be trimmed.
417 unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
418
419 TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
420 result_unsent_log_store.LoadPersistedUnsentLogs();
421 EXPECT_EQ(1U, result_unsent_log_store.size());
422
423 // Verify that the result log matches the initial log.
424 result_unsent_log_store.ExpectNextLog("Hello world!");
425
426 // Call TrimAndPersistUnsentLogs() and load the persisted logs once again.
427 // There should still only be one log.
428 unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
429
430 TestUnsentLogStore result_unsent_log_store2(&prefs_, kLogByteLimit);
431 result_unsent_log_store2.LoadPersistedUnsentLogs();
432 EXPECT_EQ(1U, result_unsent_log_store2.size());
433
434 // Verify that the result log matches the initial log.
435 result_unsent_log_store2.ExpectNextLog("Hello world!");
436 }
437
438 // Check that the store/stage/discard functions work as expected.
TEST_F(UnsentLogStoreTest,Staging)439 TEST_F(UnsentLogStoreTest, Staging) {
440 TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
441 LogMetadata log_metadata;
442 std::string tmp;
443
444 EXPECT_FALSE(unsent_log_store.has_staged_log());
445 unsent_log_store.StoreLog("one", log_metadata,
446 MetricsLogsEventManager::CreateReason::kUnknown);
447 EXPECT_FALSE(unsent_log_store.has_staged_log());
448 unsent_log_store.StoreLog("two", log_metadata,
449 MetricsLogsEventManager::CreateReason::kUnknown);
450 unsent_log_store.StageNextLog();
451 EXPECT_TRUE(unsent_log_store.has_staged_log());
452 EXPECT_EQ(unsent_log_store.staged_log(), Compress("two"));
453 unsent_log_store.StoreLog("three", log_metadata,
454 MetricsLogsEventManager::CreateReason::kUnknown);
455 EXPECT_EQ(unsent_log_store.staged_log(), Compress("two"));
456 EXPECT_EQ(unsent_log_store.size(), 3U);
457 unsent_log_store.DiscardStagedLog();
458 EXPECT_FALSE(unsent_log_store.has_staged_log());
459 EXPECT_EQ(unsent_log_store.size(), 2U);
460 unsent_log_store.StageNextLog();
461 EXPECT_EQ(unsent_log_store.staged_log(), Compress("three"));
462 unsent_log_store.DiscardStagedLog();
463 unsent_log_store.StageNextLog();
464 EXPECT_EQ(unsent_log_store.staged_log(), Compress("one"));
465 unsent_log_store.DiscardStagedLog();
466 EXPECT_FALSE(unsent_log_store.has_staged_log());
467 EXPECT_EQ(unsent_log_store.size(), 0U);
468 }
469
TEST_F(UnsentLogStoreTest,DiscardOrder)470 TEST_F(UnsentLogStoreTest, DiscardOrder) {
471 // Ensure that the correct log is discarded if new logs are pushed while
472 // a log is staged.
473 TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
474 LogMetadata log_metadata;
475
476 unsent_log_store.StoreLog("one", log_metadata,
477 MetricsLogsEventManager::CreateReason::kUnknown);
478 unsent_log_store.StageNextLog();
479 unsent_log_store.StoreLog("two", log_metadata,
480 MetricsLogsEventManager::CreateReason::kUnknown);
481 unsent_log_store.DiscardStagedLog();
482 unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
483
484 TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
485 result_unsent_log_store.LoadPersistedUnsentLogs();
486 EXPECT_EQ(1U, result_unsent_log_store.size());
487 result_unsent_log_store.ExpectNextLog("two");
488 }
489
TEST_F(UnsentLogStoreTest,Hashes)490 TEST_F(UnsentLogStoreTest, Hashes) {
491 const char kFooText[] = "foo";
492 const std::string foo_hash = base::SHA1HashString(kFooText);
493 LogMetadata log_metadata;
494
495 TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
496 unsent_log_store.StoreLog(kFooText, log_metadata,
497 MetricsLogsEventManager::CreateReason::kUnknown);
498 unsent_log_store.StageNextLog();
499
500 EXPECT_EQ(Compress(kFooText), unsent_log_store.staged_log());
501 EXPECT_EQ(foo_hash, unsent_log_store.staged_log_hash());
502 }
503
TEST_F(UnsentLogStoreTest,Signatures)504 TEST_F(UnsentLogStoreTest, Signatures) {
505 const char kFooText[] = "foo";
506 LogMetadata log_metadata;
507
508 TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
509 unsent_log_store.StoreLog(kFooText, log_metadata,
510 MetricsLogsEventManager::CreateReason::kUnknown);
511 unsent_log_store.StageNextLog();
512
513 EXPECT_EQ(Compress(kFooText), unsent_log_store.staged_log());
514
515 // The expected signature as a base 64 encoded string. The value was obtained
516 // by running the test with an empty expected_signature_base64 and taking the
517 // actual value from the test failure message. Can be verifying by the
518 // following python code:
519 // import hmac, hashlib, base64
520 // key = ''
521 // print(base64.b64encode(
522 // hmac.new(key, msg='foo', digestmod=hashlib.sha256).digest()).decode())
523 std::string expected_signature_base64 =
524 "DA2Y9+PZ1F5y6Id7wbEEMn77nAexjy/+ztdtgTB/H/8=";
525
526 std::string actual_signature_base64;
527 base::Base64Encode(unsent_log_store.staged_log_signature(),
528 &actual_signature_base64);
529 EXPECT_EQ(expected_signature_base64, actual_signature_base64);
530
531 // Test a different key results in a different signature.
532 std::string key = "secret key, don't tell anyone";
533 TestUnsentLogStore unsent_log_store_different_key(&prefs_, kLogByteLimit,
534 key);
535
536 unsent_log_store_different_key.StoreLog(
537 kFooText, log_metadata, MetricsLogsEventManager::CreateReason::kUnknown);
538 unsent_log_store_different_key.StageNextLog();
539
540 EXPECT_EQ(Compress(kFooText), unsent_log_store_different_key.staged_log());
541
542 // Base 64 encoded signature obtained in similar fashion to previous
543 // signature. To use previous python code change:
544 // key = "secret key, don't tell anyone"
545 expected_signature_base64 = "DV7z8wdDrjLkQrCzrXR3UjWsR3/YVM97tIhMnhUvfXM=";
546 base::Base64Encode(unsent_log_store_different_key.staged_log_signature(),
547 &actual_signature_base64);
548
549 EXPECT_EQ(expected_signature_base64, actual_signature_base64);
550 }
551
TEST_F(UnsentLogStoreTest,StoreLogWithUserId)552 TEST_F(UnsentLogStoreTest, StoreLogWithUserId) {
553 const char foo_text[] = "foo";
554 const uint64_t user_id = 12345L;
555
556 TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
557 LogMetadata log_metadata(absl::nullopt, user_id);
558 unsent_log_store.StoreLog(foo_text, log_metadata,
559 MetricsLogsEventManager::CreateReason::kUnknown);
560 unsent_log_store.StageNextLog();
561
562 EXPECT_EQ(Compress(foo_text), unsent_log_store.staged_log());
563 EXPECT_EQ(unsent_log_store.staged_log_user_id().value(), user_id);
564
565 unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
566
567 // Reads persisted logs from new log store.
568 TestUnsentLogStore read_unsent_log_store(&prefs_, kLogByteLimit);
569 read_unsent_log_store.LoadPersistedUnsentLogs();
570 EXPECT_EQ(1U, read_unsent_log_store.size());
571
572 // Ensure that the user_id was parsed correctly.
573 read_unsent_log_store.StageNextLog();
574 EXPECT_EQ(user_id, read_unsent_log_store.staged_log_user_id().value());
575 }
576
TEST_F(UnsentLogStoreTest,StoreLogWithLargeUserId)577 TEST_F(UnsentLogStoreTest, StoreLogWithLargeUserId) {
578 const char foo_text[] = "foo";
579 const uint64_t large_user_id = std::numeric_limits<uint64_t>::max();
580
581 TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
582 LogMetadata log_metadata(absl::nullopt, large_user_id);
583 unsent_log_store.StoreLog(foo_text, log_metadata,
584 MetricsLogsEventManager::CreateReason::kUnknown);
585 unsent_log_store.StageNextLog();
586
587 EXPECT_EQ(Compress(foo_text), unsent_log_store.staged_log());
588 EXPECT_EQ(unsent_log_store.staged_log_user_id().value(), large_user_id);
589
590 unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
591
592 // Reads persisted logs from new log store.
593 TestUnsentLogStore read_unsent_log_store(&prefs_, kLogByteLimit);
594 read_unsent_log_store.LoadPersistedUnsentLogs();
595 EXPECT_EQ(1U, read_unsent_log_store.size());
596
597 // Ensure that the user_id was parsed correctly.
598 read_unsent_log_store.StageNextLog();
599 EXPECT_EQ(large_user_id, read_unsent_log_store.staged_log_user_id().value());
600 }
601
TEST_F(UnsentLogStoreTest,UnsentLogMetadataMetrics)602 TEST_F(UnsentLogStoreTest, UnsentLogMetadataMetrics) {
603 std::unique_ptr<TestUnsentLogStoreMetrics> metrics =
604 std::make_unique<TestUnsentLogStoreMetrics>();
605 TestUnsentLogStoreMetrics* m = metrics.get();
606 TestUnsentLogStore unsent_log_store(std::move(metrics), &prefs_,
607 kLogByteLimit * 10);
608
609 // Prepare 4 logs.
610 const char kFooText[] = "foo";
611 const base::HistogramBase::Count kFooSampleCount = 3;
612
613 // The |foobar_log| whose compressed size is over 1kb will be staged first, so
614 // the persisted_size_in_kb shall be reduced by 1kb afterwards.
615 std::string foobar_log = GenerateLogWithMinCompressedSize(1024);
616 const base::HistogramBase::Count kFooBarSampleCount = 5;
617
618 // The |oversize_log| shall not be persisted.
619 std::string oversize_log =
620 GenerateLogWithMinCompressedSize(kLogByteLimit * 10 + 1);
621 const base::HistogramBase::Count kOversizeLogSampleCount = 50;
622
623 // The log without the SampleCount will not be counted to metrics.
624 const char kNoSampleLog[] = "no sample log";
625
626 LogMetadata log_metadata_with_oversize_sample(kOversizeLogSampleCount,
627 absl::nullopt);
628 unsent_log_store.StoreLog(oversize_log, log_metadata_with_oversize_sample,
629 MetricsLogsEventManager::CreateReason::kUnknown);
630
631 LogMetadata log_metadata_with_no_sample;
632 unsent_log_store.StoreLog(kNoSampleLog, log_metadata_with_no_sample,
633 MetricsLogsEventManager::CreateReason::kUnknown);
634
635 LogMetadata log_metadata_foo_sample(kFooSampleCount, absl::nullopt);
636 unsent_log_store.StoreLog(kFooText, log_metadata_foo_sample,
637 MetricsLogsEventManager::CreateReason::kUnknown);
638
639 // The foobar_log will be staged first.
640 LogMetadata log_metadata_foo_bar_sample(kFooBarSampleCount, absl::nullopt);
641 unsent_log_store.StoreLog(foobar_log, log_metadata_foo_bar_sample,
642 MetricsLogsEventManager::CreateReason::kUnknown);
643
644 unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
645
646 unsent_log_store.RecordMetaDataMetrics();
647 // The |oversize_log| was ignored, the kNoSampleLog won't be counted to
648 // metrics,
649 EXPECT_EQ(kFooSampleCount + kFooBarSampleCount, m->unsent_samples_count());
650 EXPECT_EQ(0, m->sent_samples_count());
651 EXPECT_EQ(2, m->persisted_size_in_kb());
652
653 // Pretend to send log.
654 unsent_log_store.StageNextLog();
655 unsent_log_store.MarkStagedLogAsSent();
656 unsent_log_store.DiscardStagedLog();
657 unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
658 unsent_log_store.RecordMetaDataMetrics();
659
660 // The |foobar_log| shall be sent.
661 EXPECT_EQ(kFooSampleCount, m->unsent_samples_count());
662 EXPECT_EQ(kFooBarSampleCount, m->sent_samples_count());
663 EXPECT_EQ(1, m->persisted_size_in_kb());
664
665 // Pretend |kFooText| upload failure.
666 unsent_log_store.StageNextLog();
667 unsent_log_store.DiscardStagedLog();
668 unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
669 unsent_log_store.RecordMetaDataMetrics();
670
671 // Verify the failed upload wasn't added to the sent samples count.
672 EXPECT_EQ(0, m->unsent_samples_count());
673 EXPECT_EQ(kFooBarSampleCount, m->sent_samples_count());
674 EXPECT_EQ(1, m->persisted_size_in_kb());
675 }
676
677 } // namespace metrics
678