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