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
17using base::TimeDelta;
18
19namespace {
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.
27static 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
45TEST(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)
67TEST(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
91namespace {
92
93class 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
107TEST(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
141TEST(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)
171TEST(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
200TEST(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.
248TEST(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