diff options
author | ziadh@chromium.org <ziadh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-19 08:00:42 +0000 |
---|---|---|
committer | ziadh@chromium.org <ziadh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-19 08:00:42 +0000 |
commit | 46f89e149da3971fcf52b778de939256fae4249a (patch) | |
tree | 24a85f97832b082072dd54035a89de4ca9cc86cf /chrome/browser/metrics | |
parent | a110dd1ff6a3b8507ee164ababea537df52642cb (diff) | |
download | chromium_src-46f89e149da3971fcf52b778de939256fae4249a.zip chromium_src-46f89e149da3971fcf52b778de939256fae4249a.tar.gz chromium_src-46f89e149da3971fcf52b778de939256fae4249a.tar.bz2 |
Compress and checksum pending logs that are going to be persisted. Persisted logs now have the following format:
[list_size, log1, log2, ..., log_n, checksum].
where each log is bzipped before being written. Upon reading the logs from disk, we verify the data and register whether we faced corruptions or not.
r=jar
Review URL: http://codereview.chromium.org/2936005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@52885 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/metrics')
-rw-r--r-- | chrome/browser/metrics/metrics_service.cc | 220 | ||||
-rw-r--r-- | chrome/browser/metrics/metrics_service.h | 42 | ||||
-rw-r--r-- | chrome/browser/metrics/metrics_service_unittest.cc | 179 |
3 files changed, 374 insertions, 67 deletions
diff --git a/chrome/browser/metrics/metrics_service.cc b/chrome/browser/metrics/metrics_service.cc index 3e30eab..87eae58 100644 --- a/chrome/browser/metrics/metrics_service.cc +++ b/chrome/browser/metrics/metrics_service.cc @@ -19,9 +19,8 @@ // transmission. Transmission includes submitting a compressed log as data in a // URL-post, and retransmitting (or retaining at process termination) if the // attempted transmission failed. Retention across process terminations is done -// using the the PrefServices facilities. The format for the retained -// logs (ones that never got transmitted) is always the uncompressed textual -// representation. +// using the the PrefServices facilities. The retained logs (the ones that never +// got transmitted) are compressed and base64-encoded before being persisted. // // Logs fall into one of two categories: "initial logs," and "ongoing logs." // There is at most one initial log sent for each complete run of the chromium @@ -164,12 +163,8 @@ #include <objbase.h> #endif -#if defined(USE_SYSTEM_LIBBZ2) -#include <bzlib.h> -#else -#include "third_party/bzip2/bzlib.h" -#endif - +#include "base/base64.h" +#include "base/md5.h" #include "base/thread.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/browser_list.h" @@ -205,6 +200,21 @@ static const char kHardwareClassTool[] = "/usr/bin/hardware_class"; static const char kUnknownHardwareClass[] = "unknown"; #endif +namespace { +MetricsService::LogRecallStatus MakeRecallStatusHistogram( + MetricsService::LogRecallStatus status) { + UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecall", status, + MetricsService::END_RECALL_STATUS); + return status; +} + +// TODO(ziadh): Remove this when done with experiment. +void MakeStoreStatusHistogram(MetricsService::LogStoreStatus status) { + UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogStore", status, + MetricsService::END_STORE_STATUS); +} +} // namespace + using base::Time; using base::TimeDelta; @@ -270,6 +280,10 @@ static const size_t kMaxInitialLogsPersisted = 20; // ongoing_log_ at startup). static const size_t kMaxOngoingLogsPersisted = 8; +// We append (2) more elements to persisted lists: the size of the list and a +// checksum of the elements. +static const size_t kChecksumEntryCount = 2; + // Handles asynchronous fetching of memory details. // Will run the provided task after finished. @@ -883,7 +897,7 @@ void MetricsService::PushPendingLogsToUnsentLists() { PreparePendingLogText(); if (state_ == INITIAL_LOG_READY) { // We may race here, and send second copy of initial log later. - unsent_initial_logs_.push_back(pending_log_text_); + unsent_initial_logs_.push_back(compressed_log_); state_ = SEND_OLD_INITIAL_LOGS; } else { // TODO(jar): Verify correctness in other states, including sending unsent @@ -905,14 +919,13 @@ void MetricsService::PushPendingLogTextToUnsentOngoingLogs() { // log. It wasn't supposed to be uploaded anyway. if (!server_permits_upload_) return; - - if (pending_log_text_.length() > + if (compressed_log_.length() > static_cast<size_t>(kUploadLogAvoidRetransmitSize)) { UMA_HISTOGRAM_COUNTS("UMA.Large Accumulated Log Not Persisted", - static_cast<int>(pending_log_text_.length())); + static_cast<int>(compressed_log_.length())); return; } - unsent_ongoing_logs_.push_back(pending_log_text_); + unsent_ongoing_logs_.push_back(compressed_log_); } //------------------------------------------------------------------------------ @@ -1078,7 +1091,7 @@ void MetricsService::MakePendingLog() { case SEND_OLD_INITIAL_LOGS: if (!unsent_initial_logs_.empty()) { - pending_log_text_ = unsent_initial_logs_.back(); + compressed_log_ = unsent_initial_logs_.back(); break; } state_ = SENDING_OLD_LOGS; @@ -1086,7 +1099,7 @@ void MetricsService::MakePendingLog() { case SENDING_OLD_LOGS: if (!unsent_ongoing_logs_.empty()) { - pending_log_text_ = unsent_ongoing_logs_.back(); + compressed_log_ = unsent_ongoing_logs_.back(); break; } state_ = SENDING_CURRENT_LOGS; @@ -1145,30 +1158,114 @@ void MetricsService::PrepareInitialLog() { pending_log_ = log; } -void MetricsService::RecallUnsentLogs() { - DCHECK(unsent_initial_logs_.empty()); - DCHECK(unsent_ongoing_logs_.empty()); +// static +MetricsService::LogRecallStatus MetricsService::RecallUnsentLogsHelper( + const ListValue& list, + std::vector<std::string>* local_list) { + DCHECK(local_list->empty()); + if (list.GetSize() == 0) + return MakeRecallStatusHistogram(LIST_EMPTY); + if (list.GetSize() < 3) + return MakeRecallStatusHistogram(LIST_SIZE_TOO_SMALL); + + // The size is stored at the beginning of the list. + int size; + bool valid = (*list.begin())->GetAsInteger(&size); + if (!valid) + return MakeRecallStatusHistogram(LIST_SIZE_MISSING); + + // Account for checksum and size included in the list. + if (static_cast<unsigned int>(size) != + list.GetSize() - kChecksumEntryCount) + return MakeRecallStatusHistogram(LIST_SIZE_CORRUPTION); + + MD5Context ctx; + MD5Init(&ctx); + std::string encoded_log; + std::string decoded_log; + for (ListValue::const_iterator it = list.begin() + 1; + it != list.end() - 1; ++it) { // Last element is the checksum. + valid = (*it)->GetAsString(&encoded_log); + if (!valid) { + local_list->clear(); + return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION); + } + MD5Update(&ctx, encoded_log.data(), encoded_log.length()); + + if (!base::Base64Decode(encoded_log, &decoded_log)) { + local_list->clear(); + return MakeRecallStatusHistogram(DECODE_FAIL); + } + local_list->push_back(decoded_log); + } + + // Verify checksum. + MD5Digest digest; + MD5Final(&digest, &ctx); + std::string recovered_md5; + // We store the hash at the end of the list. + valid = (*(list.end() - 1))->GetAsString(&recovered_md5); + if (!valid) { + local_list->clear(); + return MakeRecallStatusHistogram(CHECKSUM_STRING_CORRUPTION); + } + if (recovered_md5 != MD5DigestToBase16(digest)) { + local_list->clear(); + return MakeRecallStatusHistogram(CHECKSUM_CORRUPTION); + } + return MakeRecallStatusHistogram(RECALL_SUCCESS); +} +void MetricsService::RecallUnsentLogs() { PrefService* local_state = g_browser_process->local_state(); DCHECK(local_state); ListValue* unsent_initial_logs = local_state->GetMutableList( prefs::kMetricsInitialLogs); - for (ListValue::const_iterator it = unsent_initial_logs->begin(); - it != unsent_initial_logs->end(); ++it) { - std::string log; - (*it)->GetAsString(&log); - unsent_initial_logs_.push_back(log); - } + RecallUnsentLogsHelper(*unsent_initial_logs, &unsent_initial_logs_); ListValue* unsent_ongoing_logs = local_state->GetMutableList( prefs::kMetricsOngoingLogs); - for (ListValue::const_iterator it = unsent_ongoing_logs->begin(); - it != unsent_ongoing_logs->end(); ++it) { - std::string log; - (*it)->GetAsString(&log); - unsent_ongoing_logs_.push_back(log); + RecallUnsentLogsHelper(*unsent_ongoing_logs, &unsent_ongoing_logs_); +} + +// static +void MetricsService::StoreUnsentLogsHelper( + const std::vector<std::string>& local_list, + const size_t kMaxLocalListSize, + ListValue* list) { + list->Clear(); + size_t start = 0; + if (local_list.size() > kMaxLocalListSize) + start = local_list.size() - kMaxLocalListSize; + DCHECK(start <= local_list.size()); + if (local_list.size() == start) + return; + + // Store size at the beginning of the list. + list->Append(Value::CreateIntegerValue(local_list.size() - start)); + + MD5Context ctx; + MD5Init(&ctx); + std::string encoded_log; + for (std::vector<std::string>::const_iterator it = local_list.begin() + start; + it != local_list.end(); ++it) { + // We encode the compressed log as Value::CreateStringValue() expects to + // take a valid UTF8 string. + if (!base::Base64Encode(*it, &encoded_log)) { + MakeStoreStatusHistogram(ENCODE_FAIL); + list->Clear(); + return; + } + MD5Update(&ctx, encoded_log.data(), encoded_log.length()); + list->Append(Value::CreateStringValue(encoded_log)); } + + // Append hash to the end of the list. + MD5Digest digest; + MD5Final(&digest, &ctx); + list->Append(Value::CreateStringValue(MD5DigestToBase16(digest))); + DCHECK(list->GetSize() >= 3); // Minimum of 3 elements (size, data, hash). } void MetricsService::StoreUnsentLogs() { @@ -1180,59 +1277,51 @@ void MetricsService::StoreUnsentLogs() { ListValue* unsent_initial_logs = local_state->GetMutableList( prefs::kMetricsInitialLogs); - unsent_initial_logs->Clear(); - size_t start = 0; - if (unsent_initial_logs_.size() > kMaxInitialLogsPersisted) - start = unsent_initial_logs_.size() - kMaxInitialLogsPersisted; - for (size_t i = start; i < unsent_initial_logs_.size(); ++i) - unsent_initial_logs->Append( - Value::CreateStringValue(unsent_initial_logs_[i])); + StoreUnsentLogsHelper(unsent_initial_logs_, kMaxInitialLogsPersisted, + unsent_initial_logs); ListValue* unsent_ongoing_logs = local_state->GetMutableList( prefs::kMetricsOngoingLogs); - unsent_ongoing_logs->Clear(); - start = 0; - if (unsent_ongoing_logs_.size() > kMaxOngoingLogsPersisted) - start = unsent_ongoing_logs_.size() - kMaxOngoingLogsPersisted; - - for (size_t i = start; i < unsent_ongoing_logs_.size(); ++i) - unsent_ongoing_logs->Append( - Value::CreateStringValue(unsent_ongoing_logs_[i])); + StoreUnsentLogsHelper(unsent_ongoing_logs_, kMaxOngoingLogsPersisted, + unsent_ongoing_logs); } void MetricsService::PreparePendingLogText() { DCHECK(pending_log()); - if (!pending_log_text_.empty()) + if (!compressed_log_.empty()) return; int text_size = pending_log_->GetEncodedLogSize(); - // Leave room for the NUL terminator. - pending_log_->GetEncodedLog(WriteInto(&pending_log_text_, text_size + 1), + std::string pending_log_text; + // Leave room for the NULL terminator. + pending_log_->GetEncodedLog(WriteInto(&pending_log_text, text_size + 1), text_size); + + if (Bzip2Compress(pending_log_text, &compressed_log_)) { + // Allow security conscious users to see all metrics logs that we send. + LOG(INFO) << "COMPRESSED FOLLOWING METRICS LOG: " << pending_log_text; + } else { + LOG(DFATAL) << "Failed to compress log for transmission."; + // We can't discard the logs as other caller functions expect that + // |compressed_log_| not be empty. We can detect this failure at the server + // after we transmit. + compressed_log_ = "Unable to compress!"; + MakeStoreStatusHistogram(COMPRESS_FAIL); + return; + } } void MetricsService::PrepareFetchWithPendingLog() { DCHECK(pending_log()); DCHECK(!current_fetch_.get()); PreparePendingLogText(); - DCHECK(!pending_log_text_.empty()); - - // Allow security conscious users to see all metrics logs that we send. - LOG(INFO) << "METRICS LOG: " << pending_log_text_; - - std::string compressed_log; - if (!Bzip2Compress(pending_log_text_, &compressed_log)) { - LOG(DFATAL) << "Failed to compress log for transmission."; - DiscardPendingLog(); - StartLogTransmissionTimer(); // Maybe we'll do better on next log :-/. - return; - } + DCHECK(!compressed_log_.empty()); current_fetch_.reset(new URLFetcher(GURL(WideToUTF16(server_url_)), URLFetcher::POST, this)); current_fetch_->set_request_context(Profile::GetDefaultRequestContext()); - current_fetch_->set_upload_data(kMetricsType, compressed_log); + current_fetch_->set_upload_data(kMetricsType, compressed_log_); } static const char* StatusToString(const URLRequestStatus& status) { @@ -1277,10 +1366,10 @@ void MetricsService::OnURLFetchComplete(const URLFetcher* source, bool discard_log = false; if (response_code != 200 && - pending_log_text_.length() > - static_cast<size_t>(kUploadLogAvoidRetransmitSize)) { + (compressed_log_.length() > + static_cast<size_t>(kUploadLogAvoidRetransmitSize))) { UMA_HISTOGRAM_COUNTS("UMA.Large Rejected Log was Discarded", - static_cast<int>(pending_log_text_.length())); + static_cast<int>(compressed_log_.length())); discard_log = true; } else if (response_code == 400) { // Bad syntax. Retransmission won't work. @@ -1361,7 +1450,7 @@ void MetricsService::HandleBadResponseCode() { LOG(INFO) << "METRICS: transmission retry being scheduled in " << interlog_duration_.InSeconds() << " seconds for " << - pending_log_text_; + compressed_log_; } } @@ -1378,7 +1467,6 @@ void MetricsService::GetSettingsFromResponseData(const std::string& data) { return; } xmlDocPtr doc = xmlReadMemory(data.c_str(), data_size, "", NULL, 0); - DCHECK(doc); // If the document is malformed, we just use the settings that were there. if (!doc) { LOG(INFO) << "METRICS: reading xml from server response data failed"; diff --git a/chrome/browser/metrics/metrics_service.h b/chrome/browser/metrics/metrics_service.h index 7e318f0..5066d82 100644 --- a/chrome/browser/metrics/metrics_service.h +++ b/chrome/browser/metrics/metrics_service.h @@ -80,6 +80,30 @@ class MetricsService : public NotificationObserver, public URLFetcher::Delegate, public MetricsServiceBase { public: + // Used to produce a historgram that keeps track of the status of recalling + // persisted per logs. + enum LogRecallStatus { + RECALL_SUCCESS, // We were able to correctly recall a persisted log. + LIST_EMPTY, // Attempting to recall from an empty list. + LIST_SIZE_MISSING, // Failed to recover list size using GetAsInteger(). + LIST_SIZE_TOO_SMALL, // Too few elements in the list (less than 3). + LIST_SIZE_CORRUPTION, // List size is not as expected. + LOG_STRING_CORRUPTION, // Failed to recover log string using GetAsString(). + CHECKSUM_CORRUPTION, // Failed to verify checksum. + CHECKSUM_STRING_CORRUPTION, // Failed to recover checksum string using + // GetAsString(). + DECODE_FAIL, // Failed to decode log. + END_RECALL_STATUS // Number of bins to use to create the histogram. + }; + + // TODO(ziadh): This is here temporarily for a side experiment. Remove later + // on. + enum LogStoreStatus { + ENCODE_FAIL, // Failed to encode log. + COMPRESS_FAIL, // Failed to compress log. + END_STORE_STATUS // Number of bins to use to create the histogram. + }; + MetricsService(); virtual ~MetricsService(); @@ -263,7 +287,16 @@ class MetricsService : public NotificationObserver, void PrepareInitialLog(); // Pull copies of unsent logs from prefs into instance variables. void RecallUnsentLogs(); - // Convert pending_log_ to XML in pending_log_text_ for transmission. + // Decode and verify written pref log data. + static MetricsService::LogRecallStatus RecallUnsentLogsHelper( + const ListValue& list, + std::vector<std::string>* local_list); + // Encode and write list size and checksum for perf log data. + static void StoreUnsentLogsHelper(const std::vector<std::string>& local_list, + const size_t kMaxLocalListSize, + ListValue* list); + // Convert |pending_log_| to XML in |compressed_log_|, and compress it for + // transmission. void PreparePendingLogText(); // Convert pending_log_ to XML, compress it, and prepare to pass to server. @@ -489,6 +522,13 @@ class MetricsService : public NotificationObserver, scoped_refptr<chromeos::ExternalMetrics> external_metrics_; #endif + FRIEND_TEST(MetricsServiceTest, EmptyLogList); + FRIEND_TEST(MetricsServiceTest, SingleElementLogList); + FRIEND_TEST(MetricsServiceTest, OverLimitLogList); + FRIEND_TEST(MetricsServiceTest, SmallRecoveredListSize); + FRIEND_TEST(MetricsServiceTest, RemoveSizeFromLogList); + FRIEND_TEST(MetricsServiceTest, CorruptSizeOfLogList); + FRIEND_TEST(MetricsServiceTest, CorruptChecksumOfLogList); FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, ClientIdGeneratesAllZeroes); FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, ClientIdGeneratesCorrectly); FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, ClientIdCorrectlyFormatted); diff --git a/chrome/browser/metrics/metrics_service_unittest.cc b/chrome/browser/metrics/metrics_service_unittest.cc index 9de7644..e2c822d 100644 --- a/chrome/browser/metrics/metrics_service_unittest.cc +++ b/chrome/browser/metrics/metrics_service_unittest.cc @@ -5,6 +5,11 @@ #include "chrome/browser/metrics/metrics_service.h" #include <string> +#include <vector> + +#include "base/base64.h" +#include "base/md5.h" + #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_POSIX) && defined(LINUX2) @@ -43,3 +48,177 @@ TEST(MetricsServiceTest, GetHardwareClass) { EXPECT_EQ("unknown", hardware_class); } #endif // OS_CHROMEOS + +class MetricsServiceTest : public ::testing::Test { +}; + +static const size_t kMaxLocalListSize = 3; + +// Store and retrieve empty list. +TEST(MetricsServiceTest, EmptyLogList) { + ListValue list; + std::vector<std::string> local_list; + + MetricsService::StoreUnsentLogsHelper(local_list, kMaxLocalListSize, &list); + EXPECT_EQ(0U, list.GetSize()); + + local_list.clear(); // RecallUnsentLogsHelper() expects empty |local_list|. + EXPECT_EQ(MetricsService::LIST_EMPTY, + MetricsService::RecallUnsentLogsHelper(list, &local_list)); + EXPECT_EQ(0U, local_list.size()); +} + +// Store and retrieve a single log value. +TEST(MetricsServiceTest, SingleElementLogList) { + ListValue list; + std::vector<std::string> local_list; + + local_list.push_back("Hello world!"); + EXPECT_EQ(1U, local_list.size()); + + MetricsService::StoreUnsentLogsHelper(local_list, kMaxLocalListSize, &list); + + // |list| will now contain the following: + // [1, Base64Encode("Hello world!"), MD5("Hello world!")]. + EXPECT_EQ(3U, list.GetSize()); + + // Examine each element. + ListValue::const_iterator it = list.begin(); + int size = 0; + (*it)->GetAsInteger(&size); + EXPECT_EQ(1, size); + + ++it; + std::string str; + (*it)->GetAsString(&str); // Base64 encoded "Hello world!" string. + std::string encoded; + base::Base64Encode("Hello world!", &encoded); + EXPECT_TRUE(encoded == str); + + ++it; + (*it)->GetAsString(&str); // MD5 for encoded "Hello world!" string. + EXPECT_TRUE(MD5String(encoded) == str); + + ++it; + EXPECT_TRUE(it == list.end()); // Reached end of list. + + local_list.clear(); + EXPECT_EQ(MetricsService::RECALL_SUCCESS, + MetricsService::RecallUnsentLogsHelper(list, &local_list)); + EXPECT_EQ(1U, local_list.size()); +} + +// Store elements greater than the limit. +TEST(MetricsServiceTest, OverLimitLogList) { + ListValue list; + std::vector<std::string> local_list; + + local_list.push_back("one"); + local_list.push_back("two"); + local_list.push_back("three"); + local_list.push_back("four"); + EXPECT_EQ(4U, local_list.size()); + + std::string expected_first; + base::Base64Encode(local_list[local_list.size() - kMaxLocalListSize], + &expected_first); + std::string expected_last; + base::Base64Encode(local_list[local_list.size() - 1], + &expected_last); + + MetricsService::StoreUnsentLogsHelper(local_list, kMaxLocalListSize, &list); + EXPECT_EQ(kMaxLocalListSize + 2, list.GetSize()); + + std::string actual_first; + EXPECT_TRUE((*(list.begin() + 1))->GetAsString(&actual_first)); + EXPECT_TRUE(expected_first == actual_first); + + std::string actual_last; + EXPECT_TRUE((*(list.end() - 2))->GetAsString(&actual_last)); + EXPECT_TRUE(expected_last == actual_last); + + local_list.clear(); + EXPECT_EQ(MetricsService::RECALL_SUCCESS, + MetricsService::RecallUnsentLogsHelper(list, &local_list)); + EXPECT_EQ(kMaxLocalListSize, local_list.size()); +} + +// Induce LIST_SIZE_TOO_SMALL corruption +TEST(MetricsServiceTest, SmallRecoveredListSize) { + ListValue list; + std::vector<std::string> local_list; + + local_list.push_back("Hello world!"); + EXPECT_EQ(1U, local_list.size()); + MetricsService::StoreUnsentLogsHelper(local_list, kMaxLocalListSize, &list); + EXPECT_EQ(3U, list.GetSize()); + + // Remove last element. + list.Remove(list.GetSize() - 1, NULL); + EXPECT_EQ(2U, list.GetSize()); + + local_list.clear(); + EXPECT_EQ(MetricsService::LIST_SIZE_TOO_SMALL, + MetricsService::RecallUnsentLogsHelper(list, &local_list)); +} + +// Remove size from the stored list. +TEST(MetricsServiceTest, RemoveSizeFromLogList) { + ListValue list; + std::vector<std::string> local_list; + + local_list.push_back("one"); + local_list.push_back("two"); + EXPECT_EQ(2U, local_list.size()); + MetricsService::StoreUnsentLogsHelper(local_list, kMaxLocalListSize, &list); + EXPECT_EQ(4U, list.GetSize()); + + list.Remove(0, NULL); // Delete size (1st element). + EXPECT_EQ(3U, list.GetSize()); + + local_list.clear(); + EXPECT_EQ(MetricsService::LIST_SIZE_MISSING, + MetricsService::RecallUnsentLogsHelper(list, &local_list)); +} + +// Corrupt size of stored list. +TEST(MetricsServiceTest, CorruptSizeOfLogList) { + ListValue list; + std::vector<std::string> local_list; + + local_list.push_back("Hello world!"); + EXPECT_EQ(1U, local_list.size()); + MetricsService::StoreUnsentLogsHelper(local_list, kMaxLocalListSize, &list); + EXPECT_EQ(3U, list.GetSize()); + + // Change list size from 1 to 2. + EXPECT_TRUE(list.Set(0, Value::CreateIntegerValue(2))); + EXPECT_EQ(3U, list.GetSize()); + + local_list.clear(); + EXPECT_EQ(MetricsService::LIST_SIZE_CORRUPTION, + MetricsService::RecallUnsentLogsHelper(list, &local_list)); +} + +// Corrupt checksum of stored list. +TEST(MetricsServiceTest, CorruptChecksumOfLogList) { + ListValue list; + std::vector<std::string> local_list; + + local_list.clear(); + local_list.push_back("Hello world!"); + EXPECT_EQ(1U, local_list.size()); + MetricsService::StoreUnsentLogsHelper(local_list, kMaxLocalListSize, &list); + EXPECT_EQ(3U, list.GetSize()); + + // Fetch checksum (last element) and change it. + std::string checksum; + EXPECT_TRUE((*(list.end() - 1))->GetAsString(&checksum)); + checksum[0] = (checksum[0] == 'a') ? 'b' : 'a'; + EXPECT_TRUE(list.Set(2, Value::CreateStringValue(checksum))); + EXPECT_EQ(3U, list.GetSize()); + + local_list.clear(); + EXPECT_EQ(MetricsService::CHECKSUM_CORRUPTION, + MetricsService::RecallUnsentLogsHelper(list, &local_list)); +} |