1 // Copyright (c) 2011 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 <string> 6 7 #include "base/string_util.h" 8 #include "base/time.h" 9 #include "chrome/browser/metrics/metrics_log.h" 10 #include "chrome/browser/prefs/browser_prefs.h" 11 #include "chrome/browser/prefs/pref_service.h" 12 #include "chrome/common/pref_names.h" 13 #include "chrome/test/testing_pref_service.h" 14 #include "googleurl/src/gurl.h" 15 #include "testing/gtest/include/gtest/gtest.h" 16 17 using base::TimeDelta; 18 19 namespace { 20 class MetricsLogTest : public testing::Test { 21 }; 22 }; 23 24 25 // Since buildtime is highly variable, this function will scan an output log and 26 // replace it with a consistent number. 27 static void NormalizeBuildtime(std::string* xml_encoded) { 28 std::string prefix = "buildtime=\""; 29 const char postfix = '\"'; 30 size_t offset = xml_encoded->find(prefix); 31 ASSERT_NE(std::string::npos, offset); 32 offset += prefix.size(); 33 size_t postfix_position = xml_encoded->find(postfix, offset); 34 ASSERT_NE(std::string::npos, postfix_position); 35 for (size_t i = offset; i < postfix_position; ++i) { 36 char digit = xml_encoded->at(i); 37 ASSERT_GE(digit, '0'); 38 ASSERT_LE(digit, '9'); 39 } 40 41 // Insert a single fake buildtime. 42 xml_encoded->replace(offset, postfix_position - offset, "123246"); 43 } 44 45 TEST(MetricsLogTest, EmptyRecord) { 46 std::string expected_output = StringPrintf( 47 "<log clientid=\"bogus client ID\" buildtime=\"123456789\" " 48 "appversion=\"%s\"/>", MetricsLog::GetVersionString().c_str()); 49 50 MetricsLog log("bogus client ID", 0); 51 log.CloseLog(); 52 53 int size = log.GetEncodedLogSize(); 54 ASSERT_GT(size, 0); 55 56 std::string encoded; 57 // Leave room for the NUL terminator. 58 ASSERT_TRUE(log.GetEncodedLog(WriteInto(&encoded, size + 1), size)); 59 TrimWhitespaceASCII(encoded, TRIM_ALL, &encoded); 60 NormalizeBuildtime(&encoded); 61 NormalizeBuildtime(&expected_output); 62 63 ASSERT_EQ(expected_output, encoded); 64 } 65 66 #if defined(OS_CHROMEOS) 67 TEST(MetricsLogTest, ChromeOSEmptyRecord) { 68 std::string expected_output = StringPrintf( 69 "<log clientid=\"bogus client ID\" buildtime=\"123456789\" " 70 "appversion=\"%s\" hardwareclass=\"sample-class\"/>", 71 MetricsLog::GetVersionString().c_str()); 72 73 MetricsLog log("bogus client ID", 0); 74 log.set_hardware_class("sample-class"); 75 log.CloseLog(); 76 77 int size = log.GetEncodedLogSize(); 78 ASSERT_GT(size, 0); 79 80 std::string encoded; 81 // Leave room for the NUL terminator. 82 ASSERT_TRUE(log.GetEncodedLog(WriteInto(&encoded, size + 1), size)); 83 TrimWhitespaceASCII(encoded, TRIM_ALL, &encoded); 84 NormalizeBuildtime(&encoded); 85 NormalizeBuildtime(&expected_output); 86 87 ASSERT_EQ(expected_output, encoded); 88 } 89 #endif // OS_CHROMEOS 90 91 namespace { 92 93 class NoTimeMetricsLog : public MetricsLog { 94 public: 95 NoTimeMetricsLog(std::string client_id, int session_id): 96 MetricsLog(client_id, session_id) {} 97 virtual ~NoTimeMetricsLog() {} 98 99 // Override this so that output is testable. 100 virtual std::string GetCurrentTimeString() { 101 return std::string(); 102 } 103 }; 104 105 }; // namespace 106 107 TEST(MetricsLogTest, WindowEvent) { 108 std::string expected_output = StringPrintf( 109 "<log clientid=\"bogus client ID\" buildtime=\"123456789\" " 110 "appversion=\"%s\">\n" 111 " <window action=\"create\" windowid=\"0\" session=\"0\" time=\"\"/>\n" 112 " <window action=\"open\" windowid=\"1\" parent=\"0\" " 113 "session=\"0\" time=\"\"/>\n" 114 " <window action=\"close\" windowid=\"1\" parent=\"0\" " 115 "session=\"0\" time=\"\"/>\n" 116 " <window action=\"destroy\" windowid=\"0\" session=\"0\" time=\"\"/>\n" 117 "</log>", MetricsLog::GetVersionString().c_str()); 118 119 NoTimeMetricsLog log("bogus client ID", 0); 120 log.RecordWindowEvent(MetricsLog::WINDOW_CREATE, 0, -1); 121 log.RecordWindowEvent(MetricsLog::WINDOW_OPEN, 1, 0); 122 log.RecordWindowEvent(MetricsLog::WINDOW_CLOSE, 1, 0); 123 log.RecordWindowEvent(MetricsLog::WINDOW_DESTROY, 0, -1); 124 log.CloseLog(); 125 126 ASSERT_EQ(4, log.num_events()); 127 128 int size = log.GetEncodedLogSize(); 129 ASSERT_GT(size, 0); 130 131 std::string encoded; 132 // Leave room for the NUL terminator. 133 ASSERT_TRUE(log.GetEncodedLog(WriteInto(&encoded, size + 1), size)); 134 TrimWhitespaceASCII(encoded, TRIM_ALL, &encoded); 135 NormalizeBuildtime(&encoded); 136 NormalizeBuildtime(&expected_output); 137 138 ASSERT_EQ(expected_output, encoded); 139 } 140 141 TEST(MetricsLogTest, LoadEvent) { 142 std::string expected_output = StringPrintf( 143 "<log clientid=\"bogus client ID\" buildtime=\"123456789\" " 144 "appversion=\"%s\">\n" 145 " <document action=\"load\" docid=\"1\" window=\"3\" loadtime=\"7219\" " 146 "origin=\"link\" session=\"0\" time=\"\"/>\n" 147 "</log>", MetricsLog::GetVersionString().c_str()); 148 149 NoTimeMetricsLog log("bogus client ID", 0); 150 log.RecordLoadEvent(3, GURL("http://google.com"), PageTransition::LINK, 151 1, TimeDelta::FromMilliseconds(7219)); 152 153 log.CloseLog(); 154 155 ASSERT_EQ(1, log.num_events()); 156 157 int size = log.GetEncodedLogSize(); 158 ASSERT_GT(size, 0); 159 160 std::string encoded; 161 // Leave room for the NUL terminator. 162 ASSERT_TRUE(log.GetEncodedLog(WriteInto(&encoded, size + 1), size)); 163 TrimWhitespaceASCII(encoded, TRIM_ALL, &encoded); 164 NormalizeBuildtime(&encoded); 165 NormalizeBuildtime(&expected_output); 166 167 ASSERT_EQ(expected_output, encoded); 168 } 169 170 #if defined(OS_CHROMEOS) 171 TEST(MetricsLogTest, ChromeOSLoadEvent) { 172 std::string expected_output = StringPrintf( 173 "<log clientid=\"bogus client ID\" buildtime=\"123456789\" " 174 "appversion=\"%s\" hardwareclass=\"sample-class\">\n" 175 " <document action=\"load\" docid=\"1\" window=\"3\" loadtime=\"7219\" " 176 "origin=\"link\" session=\"0\" time=\"\"/>\n" 177 "</log>", MetricsLog::GetVersionString().c_str()); 178 179 NoTimeMetricsLog log("bogus client ID", 0); 180 log.RecordLoadEvent(3, GURL("http://google.com"), PageTransition::LINK, 181 1, TimeDelta::FromMilliseconds(7219)); 182 log.set_hardware_class("sample-class"); 183 log.CloseLog(); 184 185 ASSERT_EQ(1, log.num_events()); 186 187 int size = log.GetEncodedLogSize(); 188 ASSERT_GT(size, 0); 189 190 std::string encoded; 191 // Leave room for the NUL terminator. 192 ASSERT_TRUE(log.GetEncodedLog(WriteInto(&encoded, size + 1), size)); 193 TrimWhitespaceASCII(encoded, TRIM_ALL, &encoded); 194 NormalizeBuildtime(&encoded); 195 NormalizeBuildtime(&expected_output); 196 197 ASSERT_EQ(expected_output, encoded); 198 } 199 200 TEST(MetricsLogTest, ChromeOSStabilityData) { 201 NoTimeMetricsLog log("bogus client ID", 0); 202 TestingPrefService prefs; 203 browser::RegisterLocalState(&prefs); 204 205 prefs.SetInteger(prefs::kStabilityChildProcessCrashCount, 10); 206 prefs.SetInteger(prefs::kStabilityOtherUserCrashCount, 11); 207 prefs.SetInteger(prefs::kStabilityKernelCrashCount, 12); 208 prefs.SetInteger(prefs::kStabilitySystemUncleanShutdownCount, 13); 209 std::string expected_output = StringPrintf( 210 "<log clientid=\"bogus client ID\" buildtime=\"123456789\" " 211 "appversion=\"%s\">\n" 212 "<stability stuff>\n", MetricsLog::GetVersionString().c_str()); 213 // Expect 3 warnings about not yet being able to send the 214 // Chrome OS stability stats. 215 log.WriteStabilityElement(&prefs); 216 log.CloseLog(); 217 218 int size = log.GetEncodedLogSize(); 219 ASSERT_GT(size, 0); 220 221 EXPECT_EQ(0, prefs.GetInteger(prefs::kStabilityChildProcessCrashCount)); 222 EXPECT_EQ(0, prefs.GetInteger(prefs::kStabilityOtherUserCrashCount)); 223 EXPECT_EQ(0, prefs.GetInteger(prefs::kStabilityKernelCrashCount)); 224 EXPECT_EQ(0, prefs.GetInteger(prefs::kStabilitySystemUncleanShutdownCount)); 225 226 std::string encoded; 227 // Leave room for the NUL terminator. 228 bool encoding_result = log.GetEncodedLog( 229 WriteInto(&encoded, size + 1), size); 230 ASSERT_TRUE(encoding_result); 231 232 // Check that we can find childprocesscrashcount, but not 233 // any of the ChromeOS ones that we are not emitting until log 234 // servers can handle them. 235 EXPECT_NE(std::string::npos, 236 encoded.find(" childprocesscrashcount=\"10\"")); 237 EXPECT_EQ(std::string::npos, 238 encoded.find(" otherusercrashcount=")); 239 EXPECT_EQ(std::string::npos, 240 encoded.find(" kernelcrashcount=")); 241 EXPECT_EQ(std::string::npos, 242 encoded.find(" systemuncleanshutdowns=")); 243 } 244 245 #endif // OS_CHROMEOS 246 247 // Make sure our ID hashes are the same as what we see on the server side. 248 TEST(MetricsLogTest, CreateHash) { 249 static const struct { 250 std::string input; 251 std::string output; 252 } cases[] = { 253 {"Back", "0x0557fa923dcee4d0"}, 254 {"Forward", "0x67d2f6740a8eaebf"}, 255 {"NewTab", "0x290eb683f96572f1"}, 256 }; 257 258 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { 259 std::string hash_string = MetricsLog::CreateHash(cases[i].input); 260 261 // Convert to hex string 262 // We're only checking the first 8 bytes, because that's what 263 // the metrics server uses. 264 std::string hash_hex = "0x"; 265 for (size_t j = 0; j < 8; j++) { 266 base::StringAppendF(&hash_hex, "%02x", 267 static_cast<uint8>(hash_string.data()[j])); 268 } 269 EXPECT_EQ(cases[i].output, hash_hex); 270 } 271 }; 272