| // Copyright (c) 2011 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_main.h" |
| |
| #if defined(OS_POSIX) |
| #include <stdio.h> |
| #include <unistd.h> |
| #endif |
| |
| #include <iostream> |
| |
| #include "app/app_paths.h" |
| #include "base/basictypes.h" |
| #include "base/command_line.h" |
| #include "base/i18n/icu_util.h" |
| #include "base/string_util.h" |
| #include "base/sys_string_conversions.h" |
| #include "base/time.h" |
| #include "base/utf_string_conversions.h" |
| #include "chrome/browser/diagnostics/diagnostics_model.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "ui/base/ui_base_paths.h" |
| |
| namespace { |
| // This is a minimalistic interface to wrap the platform console. This will be |
| // eventually replaced by a view that can be subclassed for each platform and |
| // that the approved look and feel. |
| class SimpleConsole { |
| public: |
| enum Color { |
| DEFAULT, |
| RED, |
| GREEN, |
| }; |
| |
| virtual ~SimpleConsole() { } |
| |
| // Init must be called before using any other method. If it returns |
| // false there would be no console output. |
| virtual bool Init() = 0; |
| |
| // Writes a string to the console with the current color. |
| virtual bool Write(const std::wstring& text) = 0; |
| |
| // Reads a string from the console. Internally it may be limited to 256 |
| // characters. |
| virtual bool Read(std::wstring* txt) = 0; |
| |
| // Sets the foreground and background 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) |
| // Wrapper for the windows console operating in high-level IO mode. |
| class WinConsole : public SimpleConsole { |
| public: |
| // The ctor allocates a console always. 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) { |
| } |
| |
| virtual ~WinConsole() { |
| ::FreeConsole(); |
| } |
| |
| virtual bool Init() { |
| ::AllocConsole(); |
| return SetIOHandles(); |
| } |
| |
| virtual bool Write(const std::wstring& txt) { |
| 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. |
| virtual bool Read(std::wstring* txt) { |
| wchar_t buf[256]; |
| DWORD read = sizeof(buf) - sizeof(buf[0]); |
| if (!::ReadConsoleW(std_in_, buf, read, &read, NULL)) |
| return false; |
| // Note that |read| is in bytes. |
| txt->assign(buf, read/2); |
| return true; |
| } |
| |
| // Sets the foreground and background color. |
| virtual bool SetColor(Color color) { |
| uint16 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); |
| }; |
| |
| SimpleConsole* SimpleConsole::Create() { |
| return new WinConsole(); |
| } |
| |
| #elif defined(OS_POSIX) |
| |
| class PosixConsole : public SimpleConsole { |
| public: |
| PosixConsole() : use_color_(false) { } |
| |
| virtual bool Init() { |
| // 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; |
| } |
| |
| virtual bool Write(const std::wstring& text) { |
| printf("%s", base::SysWideToNativeMB(text).c_str()); |
| return true; |
| } |
| |
| virtual bool Read(std::wstring* txt) { |
| std::string input; |
| if (!std::getline(std::cin, input)) { |
| std::cin.clear(); |
| return false; |
| } |
| *txt = UTF8ToWide(input); |
| return true; |
| } |
| |
| virtual bool SetColor(Color color) { |
| 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); |
| }; |
| |
| SimpleConsole* SimpleConsole::Create() { |
| return new PosixConsole(); |
| } |
| |
| #else // !defined(OS_WIN) && !defined(OS_POSIX) |
| |
| SimpleConsole* SimpleConsole::Create() { |
| return NULL; |
| } |
| #endif |
| |
| // This class wraps a SimpleConsole for the specific use case of |
| // writing the results of the diagnostic tests. |
| // TODO(cpu) figure out the localization strategy. |
| class TestWriter { |
| public: |
| // The |console| must be valid and properly initialized. This |
| // class does not own it. |
| explicit TestWriter(SimpleConsole* console) |
| : console_(console), |
| failures_(0) { |
| } |
| |
| // How many tests reported failure. |
| int failures() { return failures_; } |
| |
| // Write an informational line of text in white over black. |
| bool WriteInfoText(const std::wstring& txt) { |
| console_->SetColor(SimpleConsole::DEFAULT); |
| return console_->Write(txt); |
| } |
| |
| // Write a result block. It consist of two lines. The first line |
| // has [PASS] or [FAIL] with |name| and the second line has |
| // the text in |extra|. |
| bool WriteResult(bool success, const std::wstring& name, |
| const std::wstring& extra) { |
| if (success) { |
| console_->SetColor(SimpleConsole::GREEN); |
| console_->Write(L"[PASS] "); |
| } else { |
| console_->SetColor(SimpleConsole::RED); |
| console_->Write(L"[FAIL] "); |
| failures_++; |
| } |
| WriteInfoText(name + L"\n"); |
| std::wstring second_line(L" "); |
| second_line.append(extra); |
| return WriteInfoText(second_line + L"\n\n"); |
| } |
| |
| private: |
| |
| SimpleConsole* console_; |
| |
| // Keeps track of how many tests reported failure. |
| int failures_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestWriter); |
| }; |
| |
| std::wstring PrintableUSCurrentTime() { |
| base::Time::Exploded exploded = {0}; |
| base::Time::Now().UTCExplode(&exploded); |
| return StringPrintf(L"%d:%d:%d.%d:%d:%d", |
| exploded.year, exploded.month, exploded.day_of_month, |
| exploded.hour, exploded.minute, exploded.second); |
| } |
| |
| // This class is a basic test controller. In this design the view (TestWriter) |
| // and the model (DiagnosticsModel) do not talk to each other directly but they |
| // are mediated by the controller. This has a name: 'passive view'. |
| // More info at http://martinfowler.com/eaaDev/PassiveScreen.html |
| class TestController : public DiagnosticsModel::Observer { |
| public: |
| explicit TestController(TestWriter* writer) |
| : model_(NULL), |
| writer_(writer) { |
| } |
| |
| // Run all the diagnostics of |model| and invoke the view as the model |
| // callbacks arrive. |
| void Run(DiagnosticsModel* model) { |
| std::wstring title(L"Chrome Diagnostics Mode ("); |
| writer_->WriteInfoText(title.append(PrintableUSCurrentTime()) + L")\n"); |
| if (!model) { |
| writer_->WriteResult(false, L"Diagnostics start", L"model is null"); |
| return; |
| } |
| bool icu_result = icu_util::Initialize(); |
| if (!icu_result) { |
| writer_->WriteResult(false, L"Diagnostics start", L"ICU failure"); |
| return; |
| } |
| int count = model->GetTestAvailableCount(); |
| writer_->WriteInfoText(StringPrintf(L"%d available test(s)\n\n", count)); |
| model->RunAll(this); |
| } |
| |
| // Next four are overridden from DiagnosticsModel::Observer. |
| virtual void OnProgress(int id, int percent, DiagnosticsModel* model) { |
| } |
| |
| virtual void OnSkipped(int id, DiagnosticsModel* model) { |
| // TODO(cpu): display skipped tests. |
| } |
| |
| virtual void OnFinished(int id, DiagnosticsModel* model) { |
| // As each test completes we output the results. |
| ShowResult(model->GetTest(id)); |
| } |
| |
| virtual void OnDoneAll(DiagnosticsModel* model) { |
| if (writer_->failures() > 0) { |
| writer_->WriteInfoText(StringPrintf(L"DONE. %d failure(s)\n\n", |
| writer_->failures())); |
| } else { |
| writer_->WriteInfoText(L"DONE\n\n"); |
| } |
| } |
| |
| private: |
| void ShowResult(DiagnosticsModel::TestInfo& test_info) { |
| bool success = (DiagnosticsModel::TEST_OK == test_info.GetResult()); |
| writer_->WriteResult(success, UTF16ToWide(test_info.GetTitle()), |
| UTF16ToWide(test_info.GetAdditionalInfo())); |
| } |
| |
| DiagnosticsModel* model_; |
| TestWriter* writer_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestController); |
| }; |
| } // namespace |
| |
| // This entry point is called from ChromeMain() when very few things |
| // have been initialized. To wit: |
| // -(win) Breakpad |
| // -(macOS) base::EnableTerminationOnHeapCorruption() |
| // -(macOS) base::EnableTerminationOnOutOfMemory() |
| // -(all) RegisterInvalidParamHandler() |
| // -(all) base::AtExitManager::AtExitManager() |
| // -(macOS) base::ScopedNSAutoreleasePool |
| // -(posix) base::GlobalDescriptors::GetInstance()->Set(kPrimaryIPCChannel) |
| // -(linux) base::GlobalDescriptors::GetInstance()->Set(kCrashDumpSignal) |
| // -(posix) setlocale(LC_ALL,..) |
| // -(all) CommandLine::Init(); |
| |
| int DiagnosticsMain(const CommandLine& command_line) { |
| // If we can't initialize the console exit right away. |
| SimpleConsole* console = SimpleConsole::Create(); |
| if (!console || !console->Init()) |
| return 1; |
| |
| // We need to have the path providers registered. They both |
| // return void so there is no early error signal that we can use. |
| app::RegisterPathProvider(); |
| ui::RegisterPathProvider(); |
| chrome::RegisterPathProvider(); |
| |
| TestWriter writer(console); |
| DiagnosticsModel* model = MakeDiagnosticsModel(command_line); |
| TestController controller(&writer); |
| |
| // Run all the diagnostic tests. |
| controller.Run(model); |
| delete model; |
| |
| // The "press enter to continue" prompt isn't very unixy, so only do that on |
| // Windows. |
| #if defined(OS_WIN) |
| // Block here so the user can see the results. |
| writer.WriteInfoText(L"Press [enter] to continue\n"); |
| std::wstring txt; |
| console->Read(&txt); |
| #endif |
| delete console; |
| return 0; |
| } |