1// Copyright (c) 2013 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 "chrome/test/chromedriver/chrome/console_logger.h" 6 7#include "base/compiler_specific.h" 8#include "base/format_macros.h" 9#include "base/memory/scoped_vector.h" 10#include "base/time/time.h" 11#include "base/values.h" 12#include "chrome/test/chromedriver/chrome/log.h" 13#include "chrome/test/chromedriver/chrome/status.h" 14#include "chrome/test/chromedriver/chrome/stub_devtools_client.h" 15#include "testing/gtest/include/gtest/gtest.h" 16 17namespace { 18 19class FakeDevToolsClient : public StubDevToolsClient { 20 public: 21 explicit FakeDevToolsClient(const std::string& id) 22 : id_(id), listener_(NULL) {} 23 virtual ~FakeDevToolsClient() {} 24 25 std::string PopSentCommand() { 26 std::string command; 27 if (!sent_command_queue_.empty()) { 28 command = sent_command_queue_.front(); 29 sent_command_queue_.pop_front(); 30 } 31 return command; 32 } 33 34 Status TriggerEvent(const std::string& method, 35 const base::DictionaryValue& params) { 36 return listener_->OnEvent(this, method, params); 37 } 38 39 // Overridden from DevToolsClient: 40 virtual Status ConnectIfNecessary() OVERRIDE { 41 return listener_->OnConnected(this); 42 } 43 44 virtual Status SendCommandAndGetResult( 45 const std::string& method, 46 const base::DictionaryValue& params, 47 scoped_ptr<base::DictionaryValue>* result) OVERRIDE { 48 sent_command_queue_.push_back(method); 49 return Status(kOk); 50 } 51 52 virtual void AddListener(DevToolsEventListener* listener) OVERRIDE { 53 CHECK(!listener_); 54 listener_ = listener; 55 } 56 57 virtual const std::string& GetId() OVERRIDE { 58 return id_; 59 } 60 61 private: 62 const std::string id_; // WebView id. 63 std::list<std::string> sent_command_queue_; // Commands that were sent. 64 DevToolsEventListener* listener_; // The fake allows only one event listener. 65}; 66 67struct LogEntry { 68 const base::Time timestamp; 69 const Log::Level level; 70 const std::string source; 71 const std::string message; 72 73 LogEntry(const base::Time& timestamp, 74 Log::Level level, 75 const std::string& source, 76 const std::string& message) 77 : timestamp(timestamp), level(level), source(source), message(message) {} 78}; 79 80class FakeLog : public Log { 81 public: 82 virtual void AddEntryTimestamped(const base::Time& timestamp, 83 Level level, 84 const std::string& source, 85 const std::string& message) OVERRIDE; 86 87 const ScopedVector<LogEntry>& GetEntries() { 88 return entries_; 89 } 90 91private: 92 ScopedVector<LogEntry> entries_; 93}; 94 95void FakeLog::AddEntryTimestamped(const base::Time& timestamp, 96 Level level, 97 const std::string& source, 98 const std::string& message) { 99 entries_.push_back(new LogEntry(timestamp, level, source, message)); 100} 101 102void ValidateLogEntry(const LogEntry *entry, 103 Log::Level expected_level, 104 const std::string& expected_source, 105 const std::string& expected_message) { 106 EXPECT_EQ(expected_level, entry->level); 107 EXPECT_EQ(expected_source, entry->source); 108 EXPECT_LT(0, entry->timestamp.ToTimeT()); 109 EXPECT_EQ(expected_message, entry->message); 110} 111 112void ConsoleLogParams(base::DictionaryValue* out_params, 113 const char* source, 114 const char* url, 115 const char* level, 116 int line, 117 int column, 118 const char* text) { 119 if (source != NULL) 120 out_params->SetString("message.source", source); 121 if (url != NULL) 122 out_params->SetString("message.url", url); 123 if (level != NULL) 124 out_params->SetString("message.level", level); 125 if (line != -1) 126 out_params->SetInteger("message.line", line); 127 if (column != -1) 128 out_params->SetInteger("message.column", column); 129 if (text != NULL) 130 out_params->SetString("message.text", text); 131} 132 133} // namespace 134 135TEST(ConsoleLogger, ConsoleMessages) { 136 FakeDevToolsClient client("webview"); 137 FakeLog log; 138 ConsoleLogger logger(&log); 139 140 client.AddListener(&logger); 141 logger.OnConnected(&client); 142 EXPECT_EQ("Console.enable", client.PopSentCommand()); 143 EXPECT_TRUE(client.PopSentCommand().empty()); 144 145 base::DictionaryValue params1; // All fields are set. 146 ConsoleLogParams(¶ms1, "source1", "url1", "debug", 10, 1, "text1"); 147 ASSERT_EQ(kOk, client.TriggerEvent("Console.messageAdded", params1).code()); 148 // Ignored -- wrong method. 149 ASSERT_EQ(kOk, client.TriggerEvent("Console.gaga", params1).code()); 150 151 base::DictionaryValue params2; // All optionals are not set. 152 ConsoleLogParams(¶ms2, "source2", NULL, "log", -1, -1, "text2"); 153 ASSERT_EQ(kOk, client.TriggerEvent("Console.messageAdded", params2).code()); 154 155 base::DictionaryValue params3; // Line without column, no source. 156 ConsoleLogParams(¶ms3, NULL, "url3", "warning", 30, -1, "text3"); 157 ASSERT_EQ(kOk, client.TriggerEvent("Console.messageAdded", params3).code()); 158 159 base::DictionaryValue params4; // Column without line. 160 ConsoleLogParams(¶ms4, "source4", "url4", "error", -1, 4, "text4"); 161 ASSERT_EQ(kOk, client.TriggerEvent("Console.messageAdded", params4).code()); 162 163 base::DictionaryValue params5; // Bad level name. 164 ConsoleLogParams(¶ms5, "source5", "url5", "gaga", 50, 5, "ulala"); 165 ASSERT_EQ(kOk, client.TriggerEvent("Console.messageAdded", params5).code()); 166 167 base::DictionaryValue params6; // Unset level. 168 ConsoleLogParams(¶ms6, "source6", "url6", NULL, 60, 6, NULL); 169 ASSERT_EQ(kOk, client.TriggerEvent("Console.messageAdded", params6).code()); 170 171 base::DictionaryValue params7; // No text. 172 ConsoleLogParams(¶ms7, "source7", "url7", "log", -1, -1, NULL); 173 ASSERT_EQ(kOk, client.TriggerEvent("Console.messageAdded", params7).code()); 174 175 base::DictionaryValue params8; // No message object. 176 params8.SetInteger("gaga", 8); 177 ASSERT_EQ(kOk, client.TriggerEvent("Console.messageAdded", params8).code()); 178 179 EXPECT_TRUE(client.PopSentCommand().empty()); // No other commands sent. 180 181 ASSERT_EQ(8u, log.GetEntries().size()); 182 ValidateLogEntry(log.GetEntries()[0], Log::kDebug, "source1", 183 "url1 10:1 text1"); 184 ValidateLogEntry(log.GetEntries()[1], Log::kInfo, "source2", 185 "source2 - text2"); 186 ValidateLogEntry(log.GetEntries()[2], Log::kWarning, "", "url3 30 text3"); 187 ValidateLogEntry(log.GetEntries()[3], Log::kError, "source4", 188 "url4 - text4"); 189 ValidateLogEntry( 190 log.GetEntries()[4], Log::kWarning, "", 191 "{\"message\":{\"column\":5,\"level\":\"gaga\",\"line\":50," 192 "\"source\":\"source5\",\"text\":\"ulala\",\"url\":\"url5\"}}"); 193 ValidateLogEntry( 194 log.GetEntries()[5], Log::kWarning, "", 195 "{\"message\":{\"column\":6,\"line\":60," 196 "\"source\":\"source6\",\"url\":\"url6\"}}"); 197 ValidateLogEntry( 198 log.GetEntries()[6], Log::kWarning, "", 199 "{\"message\":{\"level\":\"log\"," 200 "\"source\":\"source7\",\"url\":\"url7\"}}"); 201 ValidateLogEntry(log.GetEntries()[7], Log::kWarning, "", "{\"gaga\":8}"); 202} 203