OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/test/chromedriver/performance_logger.h" | 5 #include "chrome/test/chromedriver/performance_logger.h" |
6 | 6 |
7 #include "base/compiler_specific.h" | 7 #include "base/compiler_specific.h" |
8 #include "base/format_macros.h" | 8 #include "base/format_macros.h" |
9 #include "base/json/json_reader.h" | 9 #include "base/json/json_reader.h" |
10 #include "base/memory/scoped_vector.h" | 10 #include "base/memory/scoped_vector.h" |
11 #include "base/time/time.h" | 11 #include "base/time/time.h" |
12 #include "base/values.h" | 12 #include "base/values.h" |
| 13 #include "chrome/test/chromedriver/chrome/devtools_client_impl.h" |
13 #include "chrome/test/chromedriver/chrome/log.h" | 14 #include "chrome/test/chromedriver/chrome/log.h" |
14 #include "chrome/test/chromedriver/chrome/status.h" | 15 #include "chrome/test/chromedriver/chrome/status.h" |
15 #include "chrome/test/chromedriver/chrome/stub_devtools_client.h" | 16 #include "chrome/test/chromedriver/chrome/stub_devtools_client.h" |
| 17 #include "chrome/test/chromedriver/session.h" |
16 #include "testing/gtest/include/gtest/gtest.h" | 18 #include "testing/gtest/include/gtest/gtest.h" |
17 | 19 |
18 namespace { | 20 namespace { |
19 | 21 |
| 22 struct DevToolsCommand { |
| 23 DevToolsCommand(const std::string& in_method, |
| 24 base::DictionaryValue* in_params) |
| 25 : method(in_method) { |
| 26 params.reset(in_params); |
| 27 } |
| 28 ~DevToolsCommand() {} |
| 29 |
| 30 std::string method; |
| 31 scoped_ptr<base::DictionaryValue> params; |
| 32 }; |
| 33 |
20 class FakeDevToolsClient : public StubDevToolsClient { | 34 class FakeDevToolsClient : public StubDevToolsClient { |
21 public: | 35 public: |
22 explicit FakeDevToolsClient(const std::string& id) | 36 explicit FakeDevToolsClient(const std::string& id) |
23 : id_(id), listener_(NULL) {} | 37 : id_(id), listener_(NULL), command_index_(0) {} |
24 virtual ~FakeDevToolsClient() {} | 38 virtual ~FakeDevToolsClient() {} |
25 | 39 |
26 std::string PopSentCommand() { | 40 bool PopSentCommand(DevToolsCommand** out_command) { |
27 std::string command; | 41 if (sent_commands_.size() > command_index_) { |
28 if (!sent_command_queue_.empty()) { | 42 *out_command = sent_commands_.get().at(command_index_++); |
29 command = sent_command_queue_.front(); | 43 return true; |
30 sent_command_queue_.pop_front(); | |
31 } | 44 } |
32 return command; | 45 return false; |
33 } | 46 } |
34 | 47 |
35 Status TriggerEvent(const std::string& method) { | 48 Status TriggerEvent(const std::string& method) { |
36 base::DictionaryValue empty_params; | 49 base::DictionaryValue empty_params; |
37 return listener_->OnEvent(this, method, empty_params); | 50 return listener_->OnEvent(this, method, empty_params); |
38 } | 51 } |
39 | 52 |
| 53 Status TriggerEvent(const std::string& method, |
| 54 const base::DictionaryValue& params) { |
| 55 return listener_->OnEvent(this, method, params); |
| 56 } |
| 57 |
40 // Overridden from DevToolsClient: | 58 // Overridden from DevToolsClient: |
41 virtual Status ConnectIfNecessary() OVERRIDE { | 59 virtual Status ConnectIfNecessary() OVERRIDE { |
42 return listener_->OnConnected(this); | 60 return listener_->OnConnected(this); |
43 } | 61 } |
44 | 62 |
45 virtual Status SendCommandAndGetResult( | 63 virtual Status SendCommandAndGetResult( |
46 const std::string& method, | 64 const std::string& method, |
47 const base::DictionaryValue& params, | 65 const base::DictionaryValue& params, |
48 scoped_ptr<base::DictionaryValue>* result) OVERRIDE { | 66 scoped_ptr<base::DictionaryValue>* result) OVERRIDE { |
49 sent_command_queue_.push_back(method); | 67 sent_commands_.push_back(new DevToolsCommand(method, |
| 68 params.DeepCopy())); |
50 return Status(kOk); | 69 return Status(kOk); |
51 } | 70 } |
52 | 71 |
53 virtual void AddListener(DevToolsEventListener* listener) OVERRIDE { | 72 virtual void AddListener(DevToolsEventListener* listener) OVERRIDE { |
54 CHECK(!listener_); | 73 CHECK(!listener_); |
55 listener_ = listener; | 74 listener_ = listener; |
56 } | 75 } |
57 | 76 |
58 virtual const std::string& GetId() OVERRIDE { | 77 virtual const std::string& GetId() OVERRIDE { |
59 return id_; | 78 return id_; |
60 } | 79 } |
61 | 80 |
62 private: | 81 private: |
63 const std::string id_; // WebView id. | 82 const std::string id_; // WebView id. |
64 std::list<std::string> sent_command_queue_; // Commands that were sent. | 83 ScopedVector<DevToolsCommand> sent_commands_; // Commands that were sent. |
65 DevToolsEventListener* listener_; // The fake allows only one event listener. | 84 DevToolsEventListener* listener_; // The fake allows only one event listener. |
| 85 size_t command_index_; |
66 }; | 86 }; |
67 | 87 |
68 struct LogEntry { | 88 struct LogEntry { |
69 const base::Time timestamp; | 89 const base::Time timestamp; |
70 const Log::Level level; | 90 const Log::Level level; |
71 const std::string source; | 91 const std::string source; |
72 const std::string message; | 92 const std::string message; |
73 | 93 |
74 LogEntry(const base::Time& timestamp, | 94 LogEntry(const base::Time& timestamp, |
75 Log::Level level, | 95 Log::Level level, |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
114 if (!value->GetAsDictionary(&dict)) { | 134 if (!value->GetAsDictionary(&dict)) { |
115 SCOPED_TRACE("JSON object is not a dictionary"); | 135 SCOPED_TRACE("JSON object is not a dictionary"); |
116 ADD_FAILURE(); | 136 ADD_FAILURE(); |
117 return scoped_ptr<base::DictionaryValue>(); | 137 return scoped_ptr<base::DictionaryValue>(); |
118 } | 138 } |
119 return scoped_ptr<base::DictionaryValue>(dict->DeepCopy()); | 139 return scoped_ptr<base::DictionaryValue>(dict->DeepCopy()); |
120 } | 140 } |
121 | 141 |
122 void ValidateLogEntry(const LogEntry *entry, | 142 void ValidateLogEntry(const LogEntry *entry, |
123 const std::string& expected_webview, | 143 const std::string& expected_webview, |
124 const std::string& expected_method) { | 144 const std::string& expected_method, |
| 145 const base::DictionaryValue& expected_params) { |
125 EXPECT_EQ(Log::kInfo, entry->level); | 146 EXPECT_EQ(Log::kInfo, entry->level); |
126 EXPECT_LT(0, entry->timestamp.ToTimeT()); | 147 EXPECT_LT(0, entry->timestamp.ToTimeT()); |
127 | 148 |
128 scoped_ptr<base::DictionaryValue> message(ParseDictionary(entry->message)); | 149 scoped_ptr<base::DictionaryValue> message(ParseDictionary(entry->message)); |
129 std::string webview; | 150 std::string webview; |
130 EXPECT_TRUE(message->GetString("webview", &webview)); | 151 EXPECT_TRUE(message->GetString("webview", &webview)); |
131 EXPECT_EQ(expected_webview, webview); | 152 EXPECT_EQ(expected_webview, webview); |
132 std::string method; | 153 std::string method; |
133 EXPECT_TRUE(message->GetString("message.method", &method)); | 154 EXPECT_TRUE(message->GetString("message.method", &method)); |
134 EXPECT_EQ(expected_method, method); | 155 EXPECT_EQ(expected_method, method); |
135 base::DictionaryValue* params; | 156 base::DictionaryValue* params; |
136 EXPECT_TRUE(message->GetDictionary("message.params", ¶ms)); | 157 EXPECT_TRUE(message->GetDictionary("message.params", ¶ms)); |
137 EXPECT_EQ(0u, params->size()); | 158 EXPECT_TRUE(params->Equals(&expected_params)); |
| 159 } |
| 160 |
| 161 void ValidateLogEntry(const LogEntry *entry, |
| 162 const std::string& expected_webview, |
| 163 const std::string& expected_method) { |
| 164 base::DictionaryValue empty_params; |
| 165 ValidateLogEntry(entry, expected_webview, expected_method, empty_params); |
| 166 } |
| 167 |
| 168 void ExpectCommand(FakeDevToolsClient& client, const std::string& method) { |
| 169 DevToolsCommand* cmd; |
| 170 // Use ASSERT so that test fails if no command is returned. |
| 171 ASSERT_TRUE(client.PopSentCommand(&cmd)); |
| 172 EXPECT_EQ(method, cmd->method); |
138 } | 173 } |
139 | 174 |
140 void ExpectEnableDomains(FakeDevToolsClient& client) { | 175 void ExpectEnableDomains(FakeDevToolsClient& client) { |
141 EXPECT_EQ("Network.enable", client.PopSentCommand()); | 176 ExpectCommand(client, "Network.enable"); |
142 EXPECT_EQ("Page.enable", client.PopSentCommand()); | 177 ExpectCommand(client, "Page.enable"); |
143 EXPECT_EQ("Timeline.start", client.PopSentCommand()); | 178 ExpectCommand(client, "Timeline.start"); |
144 EXPECT_TRUE(client.PopSentCommand().empty()); | |
145 } | 179 } |
146 | 180 |
147 } // namespace | 181 } // namespace |
148 | 182 |
149 TEST(PerformanceLogger, OneWebView) { | 183 TEST(PerformanceLogger, OneWebView) { |
150 FakeDevToolsClient client("webview-1"); | 184 FakeDevToolsClient client("webview-1"); |
151 FakeLog log; | 185 FakeLog log; |
152 PerformanceLogger logger(&log); | 186 Session session("test"); |
| 187 PerformanceLogger logger(&log, &session); |
153 | 188 |
154 client.AddListener(&logger); | 189 client.AddListener(&logger); |
155 logger.OnConnected(&client); | 190 logger.OnConnected(&client); |
156 ExpectEnableDomains(client); | 191 ExpectEnableDomains(client); |
157 ASSERT_EQ(kOk, client.TriggerEvent("Network.gaga").code()); | 192 ASSERT_EQ(kOk, client.TriggerEvent("Network.gaga").code()); |
158 ASSERT_EQ(kOk, client.TriggerEvent("Page.ulala").code()); | 193 ASSERT_EQ(kOk, client.TriggerEvent("Page.ulala").code()); |
159 // Ignore -- different domain. | 194 // Ignore -- different domain. |
160 ASSERT_EQ(kOk, client.TriggerEvent("Console.bad").code()); | 195 ASSERT_EQ(kOk, client.TriggerEvent("Console.bad").code()); |
161 | 196 |
162 ASSERT_EQ(2u, log.GetEntries().size()); | 197 ASSERT_EQ(2u, log.GetEntries().size()); |
163 ValidateLogEntry(log.GetEntries()[0], "webview-1", "Network.gaga"); | 198 ValidateLogEntry(log.GetEntries()[0], "webview-1", "Network.gaga"); |
164 ValidateLogEntry(log.GetEntries()[1], "webview-1", "Page.ulala"); | 199 ValidateLogEntry(log.GetEntries()[1], "webview-1", "Page.ulala"); |
165 } | 200 } |
166 | 201 |
167 TEST(PerformanceLogger, TwoWebViews) { | 202 TEST(PerformanceLogger, TwoWebViews) { |
168 FakeDevToolsClient client1("webview-1"); | 203 FakeDevToolsClient client1("webview-1"); |
169 FakeDevToolsClient client2("webview-2"); | 204 FakeDevToolsClient client2("webview-2"); |
170 FakeLog log; | 205 FakeLog log; |
171 PerformanceLogger logger(&log); | 206 Session session("test"); |
| 207 PerformanceLogger logger(&log, &session); |
172 | 208 |
173 client1.AddListener(&logger); | 209 client1.AddListener(&logger); |
174 client2.AddListener(&logger); | 210 client2.AddListener(&logger); |
175 logger.OnConnected(&client1); | 211 logger.OnConnected(&client1); |
176 logger.OnConnected(&client2); | 212 logger.OnConnected(&client2); |
177 ExpectEnableDomains(client1); | 213 ExpectEnableDomains(client1); |
178 ExpectEnableDomains(client2); | 214 ExpectEnableDomains(client2); |
179 // OnConnected sends the enable command only to that client, not others. | 215 // OnConnected sends the enable command only to that client, not others. |
180 client1.ConnectIfNecessary(); | 216 client1.ConnectIfNecessary(); |
181 ExpectEnableDomains(client1); | 217 ExpectEnableDomains(client1); |
182 EXPECT_TRUE(client2.PopSentCommand().empty()); | 218 DevToolsCommand* cmd; |
| 219 ASSERT_FALSE(client2.PopSentCommand(&cmd)); |
183 | 220 |
184 ASSERT_EQ(kOk, client1.TriggerEvent("Page.gaga1").code()); | 221 ASSERT_EQ(kOk, client1.TriggerEvent("Page.gaga1").code()); |
185 ASSERT_EQ(kOk, client2.TriggerEvent("Timeline.gaga2").code()); | 222 ASSERT_EQ(kOk, client2.TriggerEvent("Timeline.gaga2").code()); |
186 | 223 |
187 ASSERT_EQ(2u, log.GetEntries().size()); | 224 ASSERT_EQ(2u, log.GetEntries().size()); |
188 ValidateLogEntry(log.GetEntries()[0], "webview-1", "Page.gaga1"); | 225 ValidateLogEntry(log.GetEntries()[0], "webview-1", "Page.gaga1"); |
189 ValidateLogEntry(log.GetEntries()[1], "webview-2", "Timeline.gaga2"); | 226 ValidateLogEntry(log.GetEntries()[1], "webview-2", "Timeline.gaga2"); |
190 } | 227 } |
191 | 228 |
192 TEST(PerformanceLogger, PerfLoggingPrefs) { | 229 TEST(PerformanceLogger, PerfLoggingPrefs) { |
193 FakeDevToolsClient client("webview-1"); | 230 FakeDevToolsClient client("webview-1"); |
194 FakeLog log; | 231 FakeLog log; |
| 232 Session session("test"); |
195 PerfLoggingPrefs prefs; | 233 PerfLoggingPrefs prefs; |
196 ASSERT_EQ(PerfLoggingPrefs::InspectorDomainStatus::kDefaultEnabled, | 234 EXPECT_EQ(PerfLoggingPrefs::InspectorDomainStatus::kDefaultEnabled, |
197 prefs.network); | 235 prefs.network); |
198 prefs.network = PerfLoggingPrefs::InspectorDomainStatus::kExplicitlyDisabled; | 236 prefs.network = PerfLoggingPrefs::InspectorDomainStatus::kExplicitlyDisabled; |
199 // Trace categories should be ignored until tracing support is implemented. | 237 prefs.trace_categories = "benchmark,blink.console"; |
200 prefs.trace_categories = "benchmark,webkit.console"; | 238 PerformanceLogger logger(&log, &session, prefs); |
201 PerformanceLogger logger(&log, prefs); | |
202 | 239 |
203 client.AddListener(&logger); | 240 client.AddListener(&logger); |
204 logger.OnConnected(&client); | 241 logger.OnConnected(&client); |
205 EXPECT_EQ("Page.enable", client.PopSentCommand()); | 242 ExpectCommand(client, "Page.enable"); |
206 // Trace categories ignored, so Timeline shouldn't be implicitly disabled. | 243 // Do not expect Timeline.enable command since specifying trace categories |
207 EXPECT_EQ("Timeline.start", client.PopSentCommand()); | 244 // implicitly disables Timeline feed. |
208 EXPECT_TRUE(client.PopSentCommand().empty()); | 245 DevToolsCommand* cmd; |
| 246 ASSERT_FALSE(client.PopSentCommand(&cmd)); |
209 } | 247 } |
| 248 |
| 249 namespace { |
| 250 |
| 251 class FakeBrowserwideClient : public FakeDevToolsClient { |
| 252 public: |
| 253 FakeBrowserwideClient() |
| 254 : FakeDevToolsClient(DevToolsClientImpl::kBrowserwideDevToolsClientId), |
| 255 events_handled_(false) {} |
| 256 virtual ~FakeBrowserwideClient() {} |
| 257 |
| 258 bool events_handled() const { |
| 259 return events_handled_; |
| 260 } |
| 261 |
| 262 // Overridden from DevToolsClient: |
| 263 virtual Status HandleEventsUntil( |
| 264 const ConditionalFunc& conditional_func, |
| 265 const base::TimeDelta& timeout) OVERRIDE { |
| 266 TriggerEvent("Tracing.tracingComplete"); |
| 267 events_handled_ = true; |
| 268 return Status(kOk); |
| 269 } |
| 270 |
| 271 private: |
| 272 bool events_handled_; |
| 273 }; |
| 274 |
| 275 } // namespace |
| 276 |
| 277 TEST(PerformanceLogger, TracingStartStop) { |
| 278 FakeBrowserwideClient client; |
| 279 FakeLog log; |
| 280 Session session("test"); |
| 281 PerfLoggingPrefs prefs; |
| 282 prefs.trace_categories = "benchmark,blink.console"; |
| 283 PerformanceLogger logger(&log, &session, prefs); |
| 284 |
| 285 client.AddListener(&logger); |
| 286 logger.OnConnected(&client); |
| 287 DevToolsCommand* cmd; |
| 288 ASSERT_TRUE(client.PopSentCommand(&cmd)); |
| 289 EXPECT_EQ("Tracing.start", cmd->method); |
| 290 std::string expected_cats; |
| 291 EXPECT_TRUE(cmd->params->GetString("categories", &expected_cats)); |
| 292 EXPECT_EQ("benchmark,blink.console", expected_cats); |
| 293 int expected_interval = 0; |
| 294 EXPECT_TRUE(cmd->params->GetInteger("bufferUsageReportingInterval", |
| 295 &expected_interval)); |
| 296 EXPECT_GT(expected_interval, 0); |
| 297 ASSERT_FALSE(client.PopSentCommand(&cmd)); |
| 298 |
| 299 EXPECT_FALSE(client.events_handled()); |
| 300 // Trigger a dump of the DevTools trace buffer. |
| 301 ASSERT_EQ(kOk, logger.BeforeCommand("GetLog").code()); |
| 302 EXPECT_TRUE(client.events_handled()); |
| 303 ExpectCommand(client, "Tracing.end"); |
| 304 ExpectCommand(client, "Tracing.start"); // Tracing should re-start. |
| 305 ASSERT_FALSE(client.PopSentCommand(&cmd)); |
| 306 } |
| 307 |
| 308 TEST(PerformanceLogger, RecordTraceEvents) { |
| 309 FakeBrowserwideClient client; |
| 310 FakeLog log; |
| 311 Session session("test"); |
| 312 PerfLoggingPrefs prefs; |
| 313 prefs.trace_categories = "benchmark,blink.console"; |
| 314 PerformanceLogger logger(&log, &session, prefs); |
| 315 |
| 316 client.AddListener(&logger); |
| 317 logger.OnConnected(&client); |
| 318 base::DictionaryValue params; |
| 319 base::ListValue* trace_events = new base::ListValue(); |
| 320 base::DictionaryValue* event1 = new base::DictionaryValue(); |
| 321 event1->SetString("cat", "foo"); |
| 322 trace_events->Append(event1); |
| 323 base::DictionaryValue* event2 = new base::DictionaryValue(); |
| 324 event2->SetString("cat", "bar"); |
| 325 trace_events->Append(event2); |
| 326 params.Set("value", trace_events); |
| 327 ASSERT_EQ(kOk, client.TriggerEvent("Tracing.dataCollected", params).code()); |
| 328 |
| 329 ASSERT_EQ(2u, log.GetEntries().size()); |
| 330 ValidateLogEntry(log.GetEntries()[0], |
| 331 DevToolsClientImpl::kBrowserwideDevToolsClientId, |
| 332 "Tracing.dataCollected", *event1); |
| 333 ValidateLogEntry(log.GetEntries()[1], |
| 334 DevToolsClientImpl::kBrowserwideDevToolsClientId, |
| 335 "Tracing.dataCollected", *event2); |
| 336 } |
| 337 |
| 338 TEST(PerformanceLogger, ShouldRequestTraceEvents) { |
| 339 FakeBrowserwideClient client; |
| 340 FakeLog log; |
| 341 Session session("test"); |
| 342 PerfLoggingPrefs prefs; |
| 343 prefs.trace_categories = "benchmark,blink.console"; |
| 344 PerformanceLogger logger(&log, &session, prefs); |
| 345 |
| 346 client.AddListener(&logger); |
| 347 logger.OnConnected(&client); |
| 348 EXPECT_FALSE(client.events_handled()); |
| 349 // Trace events should not be dumped for commands not in whitelist. |
| 350 ASSERT_EQ(kOk, logger.BeforeCommand("Blah").code()); |
| 351 EXPECT_FALSE(client.events_handled()); |
| 352 ASSERT_EQ(kOk, logger.BeforeCommand("Foo").code()); |
| 353 EXPECT_FALSE(client.events_handled()); |
| 354 // Trace events should always be dumped for GetLog command. |
| 355 ASSERT_EQ(kOk, logger.BeforeCommand("GetLog").code()); |
| 356 EXPECT_TRUE(client.events_handled()); |
| 357 } |
| 358 |
| 359 TEST(PerformanceLogger, WarnWhenTraceBufferFull) { |
| 360 FakeBrowserwideClient client; |
| 361 FakeLog log; |
| 362 Session session("test"); |
| 363 PerfLoggingPrefs prefs; |
| 364 prefs.trace_categories = "benchmark,blink.console"; |
| 365 PerformanceLogger logger(&log, &session, prefs); |
| 366 |
| 367 client.AddListener(&logger); |
| 368 logger.OnConnected(&client); |
| 369 base::DictionaryValue params; |
| 370 params.SetDouble("value", 1.0); |
| 371 ASSERT_EQ(kOk, client.TriggerEvent("Tracing.bufferUsage", params).code()); |
| 372 |
| 373 ASSERT_EQ(1u, log.GetEntries().size()); |
| 374 LogEntry* entry = log.GetEntries()[0]; |
| 375 EXPECT_EQ(Log::kWarning, entry->level); |
| 376 EXPECT_LT(0, entry->timestamp.ToTimeT()); |
| 377 scoped_ptr<base::DictionaryValue> message(ParseDictionary(entry->message)); |
| 378 std::string webview; |
| 379 EXPECT_TRUE(message->GetString("webview", &webview)); |
| 380 EXPECT_EQ(DevToolsClientImpl::kBrowserwideDevToolsClientId, webview); |
| 381 std::string method; |
| 382 EXPECT_TRUE(message->GetString("message.method", &method)); |
| 383 EXPECT_EQ("Tracing.bufferUsage", method); |
| 384 base::DictionaryValue* actual_params; |
| 385 EXPECT_TRUE(message->GetDictionary("message.params", &actual_params)); |
| 386 EXPECT_TRUE(actual_params->HasKey("error")); |
| 387 } |
OLD | NEW |