• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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