Home | History | Annotate | Download | only in metrics
      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