blob: a55fed695e659ac921d4d97fcbd39714f9b787a4 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/diagnostics/diagnostics_writer.h"
#include <stdint.h>
#include <string>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/string16.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/common/chrome_switches.h"
#include "ui/base/ui_base_paths.h"
#if defined(OS_POSIX)
#include <stdio.h>
#include <unistd.h>
#endif
namespace diagnostics {
// This is a minimalistic interface to wrap the platform console.
class SimpleConsole {
public:
enum Color {
DEFAULT,
RED,
GREEN,
};
virtual ~SimpleConsole() {}
// Init must be called before using any other method. If it returns
// false there will be no console output.
virtual bool Init() = 0;
// Writes a string to the console with the current color.
virtual bool Write(const base::string16& text) = 0;
// Called when the program is about to exit.
virtual void OnQuit() = 0;
// Sets the foreground text color.
virtual bool SetColor(Color color) = 0;
// Create an appropriate SimpleConsole instance. May return NULL if there is
// no implementation for the current platform.
static SimpleConsole* Create();
};
#if defined(OS_WIN)
namespace {
// Wrapper for the windows console operating in high-level IO mode.
class WinConsole : public SimpleConsole {
public:
// The ctor allocates a console. This avoids having to ask the user to start
// chrome from a command prompt.
WinConsole()
: std_out_(INVALID_HANDLE_VALUE),
std_in_(INVALID_HANDLE_VALUE) {
::AllocConsole();
}
~WinConsole() override { ::FreeConsole(); }
bool Init() override { return SetIOHandles(); }
bool Write(const base::string16& txt) override {
DWORD sz = txt.size();
return (TRUE == ::WriteConsoleW(std_out_, txt.c_str(), sz, &sz, NULL));
}
// Reads a string from the console. Internally it is limited to 256
// characters.
void OnQuit() override {
// Block here so the user can see the results.
SetColor(SimpleConsole::DEFAULT);
Write(L"Press [enter] to continue\n");
wchar_t buf[256];
DWORD read = arraysize(buf);
::ReadConsoleW(std_in_, buf, read, &read, NULL);
}
// Sets the foreground and background color.
bool SetColor(Color color) override {
uint16_t color_combo = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
FOREGROUND_INTENSITY;
switch (color) {
case RED:
color_combo = FOREGROUND_RED | FOREGROUND_INTENSITY;
break;
case GREEN:
color_combo = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
break;
case DEFAULT:
break;
default:
NOTREACHED();
}
return (TRUE == ::SetConsoleTextAttribute(std_out_, color_combo));
}
private:
bool SetIOHandles() {
std_out_ = ::GetStdHandle(STD_OUTPUT_HANDLE);
std_in_ = ::GetStdHandle(STD_INPUT_HANDLE);
return ((std_out_ != INVALID_HANDLE_VALUE) &&
(std_in_ != INVALID_HANDLE_VALUE));
}
// The input and output handles to the screen. They seem to be
// implemented as pipes but they have non-documented protocol.
HANDLE std_out_;
HANDLE std_in_;
DISALLOW_COPY_AND_ASSIGN(WinConsole);
};
} // namespace
SimpleConsole* SimpleConsole::Create() { return new WinConsole(); }
#elif defined(OS_POSIX)
namespace {
class PosixConsole : public SimpleConsole {
public:
PosixConsole() : use_color_(false) {}
bool Init() override {
// Technically, we should also check the terminal capabilities before using
// color, but in practice this is unlikely to be an issue.
use_color_ = isatty(STDOUT_FILENO);
return true;
}
bool Write(const base::string16& text) override {
// We're assuming that the terminal is using UTF-8 encoding.
printf("%s", base::UTF16ToUTF8(text).c_str());
return true;
}
void OnQuit() override {
// The "press enter to continue" prompt isn't very unixy, so only do that on
// Windows.
}
bool SetColor(Color color) override {
if (!use_color_)
return false;
const char* code = "\033[m";
switch (color) {
case RED:
code = "\033[1;31m";
break;
case GREEN:
code = "\033[1;32m";
break;
case DEFAULT:
break;
default:
NOTREACHED();
}
printf("%s", code);
return true;
}
private:
bool use_color_;
DISALLOW_COPY_AND_ASSIGN(PosixConsole);
};
} // namespace
SimpleConsole* SimpleConsole::Create() { return new PosixConsole(); }
#else // !defined(OS_WIN) && !defined(OS_POSIX)
SimpleConsole* SimpleConsole::Create() { return NULL; }
#endif
///////////////////////////////////////////////////////////
// DiagnosticsWriter
DiagnosticsWriter::DiagnosticsWriter(FormatType format)
: failures_(0), format_(format) {
// Only create consoles for non-log output.
if (format_ != LOG) {
console_.reset(SimpleConsole::Create());
console_->Init();
}
}
DiagnosticsWriter::~DiagnosticsWriter() {
if (console_.get())
console_->OnQuit();
}
bool DiagnosticsWriter::WriteInfoLine(const std::string& info_text) {
if (format_ == LOG) {
LOG(WARNING) << info_text;
return true;
} else {
if (console_.get()) {
console_->SetColor(SimpleConsole::DEFAULT);
console_->Write(base::UTF8ToUTF16(info_text + "\n"));
}
}
return true;
}
void DiagnosticsWriter::OnTestFinished(int index, DiagnosticsModel* model) {
const DiagnosticsModel::TestInfo& test_info = model->GetTest(index);
bool success = (DiagnosticsModel::TEST_OK == test_info.GetResult());
WriteResult(success,
test_info.GetName(),
test_info.GetTitle(),
test_info.GetOutcomeCode(),
test_info.GetAdditionalInfo());
}
void DiagnosticsWriter::OnAllTestsDone(DiagnosticsModel* model) {
WriteInfoLine(
base::StringPrintf("Finished %d tests.", model->GetTestRunCount()));
}
void DiagnosticsWriter::OnRecoveryFinished(int index, DiagnosticsModel* model) {
const DiagnosticsModel::TestInfo& test_info = model->GetTest(index);
WriteInfoLine("Finished Recovery for: " + test_info.GetTitle());
}
void DiagnosticsWriter::OnAllRecoveryDone(DiagnosticsModel* model) {
WriteInfoLine("Finished All Recovery operations.");
}
bool DiagnosticsWriter::WriteResult(bool success,
const std::string& id,
const std::string& name,
int outcome_code,
const std::string& extra) {
std::string result;
SimpleConsole::Color color;
if (success) {
result = "[PASS] ";
color = SimpleConsole::GREEN;
} else {
color = SimpleConsole::RED;
result = "[FAIL] ";
failures_++;
}
if (format_ != LOG) {
if (console_.get()) {
console_->SetColor(color);
console_->Write(base::ASCIIToUTF16(result));
}
if (format_ == MACHINE) {
return WriteInfoLine(base::StringPrintf(
"%03d %s (%s)", outcome_code, id.c_str(), extra.c_str()));
} else {
return WriteInfoLine(name + "\n " + extra + "\n");
}
} else {
if (!success) {
// For log output, we only care about the tests that failed.
return WriteInfoLine(base::StringPrintf("%s%03d %s (%s)",
result.c_str(),
outcome_code,
id.c_str(),
extra.c_str()));
}
}
return true;
}
} // namespace diagnostics