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