1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
|
// Copyright (c) 2009 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"
#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/time.h"
#include "chrome/browser/diagnostics/diagnostics_model.h"
#include "chrome/common/chrome_paths.h"
#if defined(OS_WIN)
#include "base/win_util.h"
namespace {
// This is a minimalistic class to wrap the windows console operating
// in high-level IO mode. 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:
// The ctor allocates a console always. This avoids having to ask
// the user to start chrome from a command prompt.
SimpleConsole()
: std_out_(INVALID_HANDLE_VALUE),
std_in_(INVALID_HANDLE_VALUE) {
}
~SimpleConsole() {
::FreeConsole();
}
// Init must be called before using any other method. If it returns
// false there would be no console output.
bool Init() {
::AllocConsole();
return SetIOHandles();
}
// Writes a string to the console with the current color.
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.
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 backgroudn color. See |kRedFG| or |kGreenFG|
// below for examples how to combine colors.
bool SetColor(uint16 color_combo) {
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(SimpleConsole);
};
// 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) {
}
// Write an informational line of text in white over black.
bool WriteInfoText(const std::wstring& txt) {
console_->SetColor(kWhiteFG);
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(kGreenFG);
console_->Write(L"[PASS] ");
} else {
console_->SetColor(kRedFG);
console_->Write(L"[FAIL] ");
}
WriteInfoText(name + L"\n");
std::wstring second_line(L" ");
second_line.append(extra);
return WriteInfoText(second_line + L"\n\n");
}
private:
// constants for the colors we use.
static const uint16 kRedFG = FOREGROUND_RED|FOREGROUND_INTENSITY;
static const uint16 kGreenFG = FOREGROUND_GREEN|FOREGROUND_INTENSITY;
static const uint16 kWhiteFG = kRedFG | kGreenFG | FOREGROUND_BLUE;
SimpleConsole* console_;
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) : 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 overriden 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) {
writer_->WriteInfoText(L"DONE\n\n");
}
private:
void ShowResult(DiagnosticsModel::TestInfo& test_info) {
bool success = (DiagnosticsModel::TEST_OK == test_info.GetResult());
writer_->WriteResult(success, test_info.GetTitle(),
test_info.GetAdditionalInfo());
}
DiagnosticsModel* model_;
TestWriter* writer_;
DISALLOW_COPY_AND_ASSIGN(TestController);
};
}
// 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) Singleton<base::GlobalDescriptors>::Set(kPrimaryIPCChannel)
// -(linux) Singleton<base::GlobalDescriptors>::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;
if (!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();
chrome::RegisterPathProvider();
TestWriter writer(&console);
DiagnosticsModel* model = MakeDiagnosticsModel();
TestController controller(&writer);
// Run all the diagnostic tests.
controller.Run(model);
delete model;
// Block here so the user can see the results.
writer.WriteInfoText(L"Press [enter] to continue\n");
std::wstring txt;
console.Read(&txt);
return 0;
}
#else // defined(OS_WIN)
int DiagnosticsMain(const CommandLine& command_line) {
return 0;
}
#endif
|