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