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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
|
// Copyright (c) 2012 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 <gtest/gtest.h>
#include <string>
#include <sys/wait.h>
#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/posix/eintr_wrapper.h"
#include "base/process_util.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "chromeos/process_proxy/process_proxy_registry.h"
namespace chromeos {
namespace {
// The test line must have all distinct characters.
const char kTestLineToSend[] = "abcdefgh\n";
const char kTestLineExpected[] = "abcdefgh\r\n";
const char kCatCommand[] = "cat";
const char kStdoutType[] = "stdout";
const int kTestLineNum = 100;
class TestRunner {
public:
virtual ~TestRunner() {}
virtual void SetupExpectations(pid_t pid) = 0;
virtual void OnSomeRead(pid_t pid, const std::string& type,
const std::string& output) = 0;
virtual void StartRegistryTest(ProcessProxyRegistry* registry) = 0;
protected:
pid_t pid_;
};
class RegistryTestRunner : public TestRunner {
public:
virtual ~RegistryTestRunner() {}
virtual void SetupExpectations(pid_t pid) OVERRIDE {
pid_ = pid;
left_to_check_index_[0] = 0;
left_to_check_index_[1] = 0;
// We consider that a line processing has started if a value in
// left_to_check__[index] is set to 0, thus -2.
lines_left_ = 2 * kTestLineNum - 2;
expected_line_ = kTestLineExpected;
}
// Method to test validity of received input. We will receive two streams of
// the same data. (input will be echoed twice by the testing process). Each
// stream will contain the same string repeated |kTestLineNum| times. So we
// have to match 2 * |kTestLineNum| lines. The problem is the received lines
// from different streams may be interleaved (e.g. we may receive
// abc|abcdef|defgh|gh). To deal with that, we allow to test received text
// against two lines. The lines MUST NOT have two same characters for this
// algorithm to work.
virtual void OnSomeRead(pid_t pid, const std::string& type,
const std::string& output) OVERRIDE {
EXPECT_EQ(type, kStdoutType);
EXPECT_EQ(pid_, pid);
bool valid = true;
for (size_t i = 0; i < output.length(); i++) {
// The character output[i] should be next in at least one of the lines we
// are testing.
valid = (ProcessReceivedCharacter(output[i], 0) ||
ProcessReceivedCharacter(output[i], 1));
EXPECT_TRUE(valid) << "Received: " << output;
}
if (!valid || TestSucceeded()) {
base::MessageLoop::current()->PostTask(FROM_HERE,
base::MessageLoop::QuitClosure());
}
}
virtual void StartRegistryTest(ProcessProxyRegistry* registry) OVERRIDE {
for (int i = 0; i < kTestLineNum; i++) {
EXPECT_TRUE(registry->SendInput(pid_, kTestLineToSend));
}
}
private:
bool ProcessReceivedCharacter(char received, size_t stream) {
if (stream >= arraysize(left_to_check_index_))
return false;
bool success = left_to_check_index_[stream] < expected_line_.length() &&
expected_line_[left_to_check_index_[stream]] == received;
if (success)
left_to_check_index_[stream]++;
if (left_to_check_index_[stream] == expected_line_.length() &&
lines_left_ > 0) {
// Take another line to test for this stream, if there are any lines left.
// If not, this stream is done.
left_to_check_index_[stream] = 0;
lines_left_--;
}
return success;
}
bool TestSucceeded() {
return left_to_check_index_[0] == expected_line_.length() &&
left_to_check_index_[1] == expected_line_.length() &&
lines_left_ == 0;
}
size_t left_to_check_index_[2];
size_t lines_left_;
std::string expected_line_;
};
class RegistryNotifiedOnProcessExitTestRunner : public TestRunner {
public:
virtual ~RegistryNotifiedOnProcessExitTestRunner() {}
virtual void SetupExpectations(pid_t pid) OVERRIDE {
output_received_ = false;
pid_ = pid;
}
virtual void OnSomeRead(pid_t pid, const std::string& type,
const std::string& output) OVERRIDE {
EXPECT_EQ(pid_, pid);
if (!output_received_) {
output_received_ = true;
EXPECT_EQ(type, "stdout");
EXPECT_EQ(output, "p");
base::KillProcess(pid_, 0 , true);
return;
}
EXPECT_EQ("exit", type);
base::MessageLoop::current()->PostTask(FROM_HERE,
base::MessageLoop::QuitClosure());
}
virtual void StartRegistryTest(ProcessProxyRegistry* registry) OVERRIDE {
EXPECT_TRUE(registry->SendInput(pid_, "p"));
}
private:
bool output_received_;
};
class SigIntTestRunner : public TestRunner {
public:
virtual ~SigIntTestRunner() {}
virtual void SetupExpectations(pid_t pid) OVERRIDE {
pid_ = pid;
}
virtual void OnSomeRead(pid_t pid, const std::string& type,
const std::string& output) OVERRIDE {
EXPECT_EQ(pid_, pid);
// We may receive ^C on stdout, but we don't care about that, as long as we
// eventually received exit event.
if (type == "exit") {
base::MessageLoop::current()->PostTask(FROM_HERE,
base::MessageLoop::QuitClosure());
}
}
virtual void StartRegistryTest(ProcessProxyRegistry* registry) OVERRIDE {
// Send SingInt and verify the process exited.
EXPECT_TRUE(registry->SendInput(pid_, "\003"));
}
};
} // namespace
class ProcessProxyTest : public testing::Test {
public:
ProcessProxyTest() {}
virtual ~ProcessProxyTest() {}
protected:
void InitRegistryTest() {
registry_ = ProcessProxyRegistry::Get();
EXPECT_TRUE(registry_->OpenProcess(
kCatCommand, &pid_,
base::Bind(&TestRunner::OnSomeRead,
base::Unretained(test_runner_.get()))));
test_runner_->SetupExpectations(pid_);
test_runner_->StartRegistryTest(registry_);
}
void EndRegistryTest() {
registry_->CloseProcess(pid_);
base::TerminationStatus status = base::GetTerminationStatus(pid_, NULL);
EXPECT_NE(base::TERMINATION_STATUS_STILL_RUNNING, status);
if (status == base::TERMINATION_STATUS_STILL_RUNNING)
base::KillProcess(pid_, 0, true);
base::MessageLoop::current()->PostTask(FROM_HERE,
base::MessageLoop::QuitClosure());
}
void RunTest() {
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&ProcessProxyTest::InitRegistryTest,
base::Unretained(this)));
// Wait until all data from output watcher is received (QuitTask will be
// fired on watcher thread).
base::MessageLoop::current()->Run();
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&ProcessProxyTest::EndRegistryTest,
base::Unretained(this)));
// Wait until we clean up the process proxy.
base::MessageLoop::current()->Run();
}
scoped_ptr<TestRunner> test_runner_;
private:
ProcessProxyRegistry* registry_;
pid_t pid_;
base::MessageLoop message_loop_;
};
// Test will open new process that will run cat command, and verify data we
// write to process gets echoed back.
TEST_F(ProcessProxyTest, RegistryTest) {
test_runner_.reset(new RegistryTestRunner());
RunTest();
}
// Open new process, then kill it. Verifiy that we detect when the process dies.
TEST_F(ProcessProxyTest, RegistryNotifiedOnProcessExit) {
test_runner_.reset(new RegistryNotifiedOnProcessExitTestRunner());
RunTest();
}
// Test verifies that \003 message send to process is processed as SigInt.
// Timing out on the waterfall: http://crbug.com/115064
TEST_F(ProcessProxyTest, DISABLED_SigInt) {
test_runner_.reset(new SigIntTestRunner());
RunTest();
}
} // namespace chromeos
|