1 // Copyright 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/browser/diagnostics/diagnostics_writer.h"
6
7 #include "build/build_config.h"
8
9 #if defined(OS_POSIX)
10 #include <stdio.h>
11 #include <unistd.h>
12 #endif
13
14 #include <string>
15
16 #include "base/basictypes.h"
17 #include "base/command_line.h"
18 #include "base/logging.h"
19 #include "base/strings/string16.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "ui/base/ui_base_paths.h"
24
25 namespace diagnostics {
26
27 // This is a minimalistic interface to wrap the platform console.
28 class SimpleConsole {
29 public:
30 enum Color {
31 DEFAULT,
32 RED,
33 GREEN,
34 };
35
~SimpleConsole()36 virtual ~SimpleConsole() {}
37
38 // Init must be called before using any other method. If it returns
39 // false there will be no console output.
40 virtual bool Init() = 0;
41
42 // Writes a string to the console with the current color.
43 virtual bool Write(const base::string16& text) = 0;
44
45 // Called when the program is about to exit.
46 virtual void OnQuit() = 0;
47
48 // Sets the foreground text color.
49 virtual bool SetColor(Color color) = 0;
50
51 // Create an appropriate SimpleConsole instance. May return NULL if there is
52 // no implementation for the current platform.
53 static SimpleConsole* Create();
54 };
55
56 #if defined(OS_WIN)
57 namespace {
58
59 // Wrapper for the windows console operating in high-level IO mode.
60 class WinConsole : public SimpleConsole {
61 public:
62 // The ctor allocates a console. This avoids having to ask the user to start
63 // chrome from a command prompt.
WinConsole()64 WinConsole()
65 : std_out_(INVALID_HANDLE_VALUE),
66 std_in_(INVALID_HANDLE_VALUE) {
67 ::AllocConsole();
68 }
69
~WinConsole()70 virtual ~WinConsole() {
71 ::FreeConsole();
72 }
73
Init()74 virtual bool Init() {
75 return SetIOHandles();
76 }
77
Write(const base::string16 & txt)78 virtual bool Write(const base::string16& txt) {
79 DWORD sz = txt.size();
80 return (TRUE == ::WriteConsoleW(std_out_, txt.c_str(), sz, &sz, NULL));
81 }
82
83 // Reads a string from the console. Internally it is limited to 256
84 // characters.
OnQuit()85 virtual void OnQuit() {
86 // Block here so the user can see the results.
87 SetColor(SimpleConsole::DEFAULT);
88 Write(L"Press [enter] to continue\n");
89 wchar_t buf[256];
90 DWORD read = arraysize(buf);
91 ::ReadConsoleW(std_in_, buf, read, &read, NULL);
92 }
93
94 // Sets the foreground and background color.
SetColor(Color color)95 virtual bool SetColor(Color color) {
96 uint16 color_combo = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
97 FOREGROUND_INTENSITY;
98 switch (color) {
99 case RED:
100 color_combo = FOREGROUND_RED | FOREGROUND_INTENSITY;
101 break;
102 case GREEN:
103 color_combo = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
104 break;
105 case DEFAULT:
106 break;
107 default:
108 NOTREACHED();
109 }
110 return (TRUE == ::SetConsoleTextAttribute(std_out_, color_combo));
111 }
112
113 private:
SetIOHandles()114 bool SetIOHandles() {
115 std_out_ = ::GetStdHandle(STD_OUTPUT_HANDLE);
116 std_in_ = ::GetStdHandle(STD_INPUT_HANDLE);
117 return ((std_out_ != INVALID_HANDLE_VALUE) &&
118 (std_in_ != INVALID_HANDLE_VALUE));
119 }
120
121 // The input and output handles to the screen. They seem to be
122 // implemented as pipes but they have non-documented protocol.
123 HANDLE std_out_;
124 HANDLE std_in_;
125
126 DISALLOW_COPY_AND_ASSIGN(WinConsole);
127 };
128
129 } // namespace
130
Create()131 SimpleConsole* SimpleConsole::Create() { return new WinConsole(); }
132
133 #elif defined(OS_POSIX)
134 namespace {
135
136 class PosixConsole : public SimpleConsole {
137 public:
PosixConsole()138 PosixConsole() : use_color_(false) {}
139
Init()140 virtual bool Init() OVERRIDE {
141 // Technically, we should also check the terminal capabilities before using
142 // color, but in practice this is unlikely to be an issue.
143 use_color_ = isatty(STDOUT_FILENO);
144 return true;
145 }
146
Write(const base::string16 & text)147 virtual bool Write(const base::string16& text) OVERRIDE {
148 // We're assuming that the terminal is using UTF-8 encoding.
149 printf("%s", base::UTF16ToUTF8(text).c_str());
150 return true;
151 }
152
OnQuit()153 virtual void OnQuit() OVERRIDE {
154 // The "press enter to continue" prompt isn't very unixy, so only do that on
155 // Windows.
156 }
157
SetColor(Color color)158 virtual bool SetColor(Color color) OVERRIDE {
159 if (!use_color_)
160 return false;
161
162 const char* code = "\033[m";
163 switch (color) {
164 case RED:
165 code = "\033[1;31m";
166 break;
167 case GREEN:
168 code = "\033[1;32m";
169 break;
170 case DEFAULT:
171 break;
172 default:
173 NOTREACHED();
174 }
175 printf("%s", code);
176 return true;
177 }
178
179 private:
180 bool use_color_;
181
182 DISALLOW_COPY_AND_ASSIGN(PosixConsole);
183 };
184
185 } // namespace
186
Create()187 SimpleConsole* SimpleConsole::Create() { return new PosixConsole(); }
188
189 #else // !defined(OS_WIN) && !defined(OS_POSIX)
Create()190 SimpleConsole* SimpleConsole::Create() { return NULL; }
191 #endif
192
193 ///////////////////////////////////////////////////////////
194 // DiagnosticsWriter
195
DiagnosticsWriter(FormatType format)196 DiagnosticsWriter::DiagnosticsWriter(FormatType format)
197 : failures_(0), format_(format) {
198 // Only create consoles for non-log output.
199 if (format_ != LOG) {
200 console_.reset(SimpleConsole::Create());
201 console_->Init();
202 }
203 }
204
~DiagnosticsWriter()205 DiagnosticsWriter::~DiagnosticsWriter() {
206 if (console_.get())
207 console_->OnQuit();
208 }
209
WriteInfoLine(const std::string & info_text)210 bool DiagnosticsWriter::WriteInfoLine(const std::string& info_text) {
211 if (format_ == LOG) {
212 LOG(WARNING) << info_text;
213 return true;
214 } else {
215 if (console_.get()) {
216 console_->SetColor(SimpleConsole::DEFAULT);
217 console_->Write(base::UTF8ToUTF16(info_text + "\n"));
218 }
219 }
220 return true;
221 }
222
OnTestFinished(int index,DiagnosticsModel * model)223 void DiagnosticsWriter::OnTestFinished(int index, DiagnosticsModel* model) {
224 const DiagnosticsModel::TestInfo& test_info = model->GetTest(index);
225 bool success = (DiagnosticsModel::TEST_OK == test_info.GetResult());
226 WriteResult(success,
227 test_info.GetName(),
228 test_info.GetTitle(),
229 test_info.GetOutcomeCode(),
230 test_info.GetAdditionalInfo());
231 }
232
OnAllTestsDone(DiagnosticsModel * model)233 void DiagnosticsWriter::OnAllTestsDone(DiagnosticsModel* model) {
234 WriteInfoLine(
235 base::StringPrintf("Finished %d tests.", model->GetTestRunCount()));
236 }
237
OnRecoveryFinished(int index,DiagnosticsModel * model)238 void DiagnosticsWriter::OnRecoveryFinished(int index, DiagnosticsModel* model) {
239 const DiagnosticsModel::TestInfo& test_info = model->GetTest(index);
240 WriteInfoLine("Finished Recovery for: " + test_info.GetTitle());
241 }
242
OnAllRecoveryDone(DiagnosticsModel * model)243 void DiagnosticsWriter::OnAllRecoveryDone(DiagnosticsModel* model) {
244 WriteInfoLine("Finished All Recovery operations.");
245 }
246
WriteResult(bool success,const std::string & id,const std::string & name,int outcome_code,const std::string & extra)247 bool DiagnosticsWriter::WriteResult(bool success,
248 const std::string& id,
249 const std::string& name,
250 int outcome_code,
251 const std::string& extra) {
252 std::string result;
253 SimpleConsole::Color color;
254
255 if (success) {
256 result = "[PASS] ";
257 color = SimpleConsole::GREEN;
258 } else {
259 color = SimpleConsole::RED;
260 result = "[FAIL] ";
261 failures_++;
262 }
263
264 if (format_ != LOG) {
265 if (console_.get()) {
266 console_->SetColor(color);
267 console_->Write(base::ASCIIToUTF16(result));
268 }
269 if (format_ == MACHINE) {
270 return WriteInfoLine(base::StringPrintf(
271 "%03d %s (%s)", outcome_code, id.c_str(), extra.c_str()));
272 } else {
273 return WriteInfoLine(name + "\n " + extra + "\n");
274 }
275 } else {
276 if (!success) {
277 // For log output, we only care about the tests that failed.
278 return WriteInfoLine(base::StringPrintf("%s%03d %s (%s)",
279 result.c_str(),
280 outcome_code,
281 id.c_str(),
282 extra.c_str()));
283 }
284 }
285 return true;
286 }
287
288 } // namespace diagnostics
289