Home | History | Annotate | Download | only in metrics
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "components/metrics/persisted_logs.h"
      6 
      7 #include "base/base64.h"
      8 #include "base/prefs/pref_registry_simple.h"
      9 #include "base/prefs/scoped_user_pref_update.h"
     10 #include "base/prefs/testing_pref_service.h"
     11 #include "base/rand_util.h"
     12 #include "base/sha1.h"
     13 #include "base/values.h"
     14 #include "components/metrics/compression_utils.h"
     15 #include "testing/gtest/include/gtest/gtest.h"
     16 
     17 namespace metrics {
     18 
     19 namespace {
     20 
     21 const char kTestPrefName[] = "TestPref";
     22 const size_t kLogCountLimit = 3;
     23 const size_t kLogByteLimit = 1000;
     24 
     25 // Compresses |log_data| and returns the result.
     26 std::string Compress(const std::string& log_data) {
     27   std::string compressed_log_data;
     28   EXPECT_TRUE(GzipCompress(log_data, &compressed_log_data));
     29   return compressed_log_data;
     30 }
     31 
     32 // Generates and returns log data such that its size after compression is at
     33 // least |min_compressed_size|.
     34 std::string GenerateLogWithMinCompressedSize(size_t min_compressed_size) {
     35   // Since the size check is done against a compressed log, generate enough
     36   // data that compresses to larger than |log_size|.
     37   std::string rand_bytes = base::RandBytesAsString(min_compressed_size);
     38   while (Compress(rand_bytes).size() < min_compressed_size)
     39     rand_bytes.append(base::RandBytesAsString(min_compressed_size));
     40   std::string base64_data_for_logging;
     41   base::Base64Encode(rand_bytes, &base64_data_for_logging);
     42   SCOPED_TRACE(testing::Message() << "Using random data "
     43                                   << base64_data_for_logging);
     44   return rand_bytes;
     45 }
     46 
     47 class PersistedLogsTest : public testing::Test {
     48  public:
     49   PersistedLogsTest() {
     50     prefs_.registry()->RegisterListPref(kTestPrefName);
     51   }
     52 
     53  protected:
     54   TestingPrefServiceSimple prefs_;
     55 
     56  private:
     57   DISALLOW_COPY_AND_ASSIGN(PersistedLogsTest);
     58 };
     59 
     60 class TestPersistedLogs : public PersistedLogs {
     61  public:
     62   TestPersistedLogs(PrefService* service, size_t min_log_bytes)
     63       : PersistedLogs(service, kTestPrefName, kLogCountLimit, min_log_bytes,
     64                       0) {
     65   }
     66 
     67   // Stages and removes the next log, while testing it's value.
     68   void ExpectNextLog(const std::string& expected_log) {
     69     StageLog();
     70     EXPECT_EQ(staged_log(), Compress(expected_log));
     71     DiscardStagedLog();
     72   }
     73 
     74  private:
     75   DISALLOW_COPY_AND_ASSIGN(TestPersistedLogs);
     76 };
     77 
     78 }  // namespace
     79 
     80 // Store and retrieve empty list_value.
     81 TEST_F(PersistedLogsTest, EmptyLogList) {
     82   TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
     83 
     84   persisted_logs.SerializeLogs();
     85   const base::ListValue* list_value = prefs_.GetList(kTestPrefName);
     86   EXPECT_EQ(0U, list_value->GetSize());
     87 
     88   TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
     89   EXPECT_EQ(PersistedLogs::LIST_EMPTY, result_persisted_logs.DeserializeLogs());
     90   EXPECT_EQ(0U, result_persisted_logs.size());
     91 }
     92 
     93 // Store and retrieve a single log value.
     94 TEST_F(PersistedLogsTest, SingleElementLogList) {
     95   TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
     96 
     97   persisted_logs.StoreLog("Hello world!");
     98   persisted_logs.SerializeLogs();
     99 
    100   TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
    101   EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
    102             result_persisted_logs.DeserializeLogs());
    103   EXPECT_EQ(1U, result_persisted_logs.size());
    104 
    105   // Verify that the result log matches the initial log.
    106   persisted_logs.StageLog();
    107   result_persisted_logs.StageLog();
    108   EXPECT_EQ(persisted_logs.staged_log(), result_persisted_logs.staged_log());
    109   EXPECT_EQ(persisted_logs.staged_log_hash(),
    110             result_persisted_logs.staged_log_hash());
    111 }
    112 
    113 // Store a set of logs over the length limit, but smaller than the min number of
    114 // bytes.
    115 TEST_F(PersistedLogsTest, LongButTinyLogList) {
    116   TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
    117 
    118   size_t log_count = kLogCountLimit * 5;
    119   for (size_t i = 0; i < log_count; ++i)
    120     persisted_logs.StoreLog("x");
    121 
    122   persisted_logs.SerializeLogs();
    123 
    124   TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
    125   EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
    126             result_persisted_logs.DeserializeLogs());
    127   EXPECT_EQ(persisted_logs.size(), result_persisted_logs.size());
    128 
    129   result_persisted_logs.ExpectNextLog("x");
    130 }
    131 
    132 // Store a set of logs over the length limit, but that doesn't reach the minimum
    133 // number of bytes until after passing the length limit.
    134 TEST_F(PersistedLogsTest, LongButSmallLogList) {
    135   size_t log_count = kLogCountLimit * 5;
    136   size_t log_size = 50;
    137 
    138   std::string first_kept = "First to keep";
    139   first_kept.resize(log_size, ' ');
    140 
    141   std::string blank_log = std::string(log_size, ' ');
    142 
    143   std::string last_kept = "Last to keep";
    144   last_kept.resize(log_size, ' ');
    145 
    146   // Set the byte limit enough to keep everything but the first two logs.
    147   const size_t min_log_bytes =
    148       Compress(first_kept).length() + Compress(last_kept).length() +
    149       (log_count - 4) * Compress(blank_log).length();
    150   TestPersistedLogs persisted_logs(&prefs_, min_log_bytes);
    151 
    152   persisted_logs.StoreLog("one");
    153   persisted_logs.StoreLog("two");
    154   persisted_logs.StoreLog(first_kept);
    155   for (size_t i = persisted_logs.size(); i < log_count - 1; ++i) {
    156     persisted_logs.StoreLog(blank_log);
    157   }
    158   persisted_logs.StoreLog(last_kept);
    159   persisted_logs.SerializeLogs();
    160 
    161   TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
    162   EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
    163             result_persisted_logs.DeserializeLogs());
    164   EXPECT_EQ(persisted_logs.size() - 2, result_persisted_logs.size());
    165 
    166   result_persisted_logs.ExpectNextLog(last_kept);
    167   while (result_persisted_logs.size() > 1) {
    168     result_persisted_logs.ExpectNextLog(blank_log);
    169   }
    170   result_persisted_logs.ExpectNextLog(first_kept);
    171 }
    172 
    173 // Store a set of logs within the length limit, but well over the minimum
    174 // number of bytes.
    175 TEST_F(PersistedLogsTest, ShortButLargeLogList) {
    176   // Make the total byte count about twice the minimum.
    177   size_t log_count = kLogCountLimit;
    178   size_t log_size = (kLogByteLimit / log_count) * 2;
    179   std::string log_data = GenerateLogWithMinCompressedSize(log_size);
    180 
    181   TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
    182   for (size_t i = 0; i < log_count; ++i) {
    183     persisted_logs.StoreLog(log_data);
    184   }
    185   persisted_logs.SerializeLogs();
    186 
    187   TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
    188   EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
    189             result_persisted_logs.DeserializeLogs());
    190   EXPECT_EQ(persisted_logs.size(), result_persisted_logs.size());
    191 }
    192 
    193 // Store a set of logs over the length limit, and over the minimum number of
    194 // bytes.
    195 TEST_F(PersistedLogsTest, LongAndLargeLogList) {
    196   TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
    197 
    198   // Include twice the max number of logs.
    199   size_t log_count = kLogCountLimit * 2;
    200   // Make the total byte count about four times the minimum.
    201   size_t log_size = (kLogByteLimit / log_count) * 4;
    202 
    203   std::string target_log = "First to keep";
    204   target_log += GenerateLogWithMinCompressedSize(log_size);
    205 
    206   std::string log_data = GenerateLogWithMinCompressedSize(log_size);
    207   for (size_t i = 0; i < log_count; ++i) {
    208     if (i == log_count - kLogCountLimit)
    209       persisted_logs.StoreLog(target_log);
    210     else
    211       persisted_logs.StoreLog(log_data);
    212   }
    213 
    214   persisted_logs.SerializeLogs();
    215 
    216   TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
    217   EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
    218             result_persisted_logs.DeserializeLogs());
    219   EXPECT_EQ(kLogCountLimit, result_persisted_logs.size());
    220 
    221   while (result_persisted_logs.size() > 1) {
    222     result_persisted_logs.ExpectNextLog(log_data);
    223   }
    224   result_persisted_logs.ExpectNextLog(target_log);
    225 }
    226 
    227 // Check that the store/stage/discard functions work as expected.
    228 TEST_F(PersistedLogsTest, Staging) {
    229   TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
    230   std::string tmp;
    231 
    232   EXPECT_FALSE(persisted_logs.has_staged_log());
    233   persisted_logs.StoreLog("one");
    234   EXPECT_FALSE(persisted_logs.has_staged_log());
    235   persisted_logs.StoreLog("two");
    236   persisted_logs.StageLog();
    237   EXPECT_TRUE(persisted_logs.has_staged_log());
    238   EXPECT_EQ(persisted_logs.staged_log(), Compress("two"));
    239   persisted_logs.StoreLog("three");
    240   EXPECT_EQ(persisted_logs.staged_log(), Compress("two"));
    241   EXPECT_EQ(persisted_logs.size(), 3U);
    242   persisted_logs.DiscardStagedLog();
    243   EXPECT_FALSE(persisted_logs.has_staged_log());
    244   EXPECT_EQ(persisted_logs.size(), 2U);
    245   persisted_logs.StageLog();
    246   EXPECT_EQ(persisted_logs.staged_log(), Compress("three"));
    247   persisted_logs.DiscardStagedLog();
    248   persisted_logs.StageLog();
    249   EXPECT_EQ(persisted_logs.staged_log(), Compress("one"));
    250   persisted_logs.DiscardStagedLog();
    251   EXPECT_FALSE(persisted_logs.has_staged_log());
    252   EXPECT_EQ(persisted_logs.size(), 0U);
    253 }
    254 
    255 TEST_F(PersistedLogsTest, DiscardOrder) {
    256   // Ensure that the correct log is discarded if new logs are pushed while
    257   // a log is staged.
    258   TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
    259 
    260   persisted_logs.StoreLog("one");
    261   persisted_logs.StageLog();
    262   persisted_logs.StoreLog("two");
    263   persisted_logs.DiscardStagedLog();
    264   persisted_logs.SerializeLogs();
    265 
    266   TestPersistedLogs result_persisted_logs(&prefs_, kLogByteLimit);
    267   EXPECT_EQ(PersistedLogs::RECALL_SUCCESS,
    268             result_persisted_logs.DeserializeLogs());
    269   EXPECT_EQ(1U, result_persisted_logs.size());
    270   result_persisted_logs.ExpectNextLog("two");
    271 }
    272 
    273 
    274 TEST_F(PersistedLogsTest, Hashes) {
    275   const char kFooText[] = "foo";
    276   const std::string foo_hash = base::SHA1HashString(kFooText);
    277 
    278   TestPersistedLogs persisted_logs(&prefs_, kLogByteLimit);
    279   persisted_logs.StoreLog(kFooText);
    280   persisted_logs.StageLog();
    281 
    282   EXPECT_EQ(Compress(kFooText), persisted_logs.staged_log());
    283   EXPECT_EQ(foo_hash, persisted_logs.staged_log_hash());
    284 }
    285 
    286 }  // namespace metrics
    287