• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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