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