1// Copyright (c) 2012 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 <gtest/gtest.h>
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/message_loop/message_loop.h"
12#include "base/process/kill.h"
13#include "base/threading/thread.h"
14#include "chromeos/process_proxy/process_proxy_registry.h"
15
16namespace chromeos {
17
18namespace {
19
20// The test line must have all distinct characters.
21const char kTestLineToSend[] = "abcdefgh\n";
22const char kTestLineExpected[] = "abcdefgh\r\n";
23
24const char kCatCommand[] = "cat";
25const char kStdoutType[] = "stdout";
26const int kTestLineNum = 100;
27
28class TestRunner {
29 public:
30  virtual ~TestRunner() {}
31  virtual void SetupExpectations(pid_t pid) = 0;
32  virtual void OnSomeRead(pid_t pid, const std::string& type,
33                          const std::string& output) = 0;
34  virtual void StartRegistryTest(ProcessProxyRegistry* registry) = 0;
35
36 protected:
37  pid_t pid_;
38};
39
40class RegistryTestRunner : public TestRunner {
41 public:
42  virtual ~RegistryTestRunner() {}
43
44  virtual void SetupExpectations(pid_t pid) OVERRIDE {
45    pid_ = pid;
46    left_to_check_index_[0] = 0;
47    left_to_check_index_[1] = 0;
48    // We consider that a line processing has started if a value in
49    // left_to_check__[index] is set to 0, thus -2.
50    lines_left_ = 2 * kTestLineNum - 2;
51    expected_line_ = kTestLineExpected;
52  }
53
54  // Method to test validity of received input. We will receive two streams of
55  // the same data. (input will be echoed twice by the testing process). Each
56  // stream will contain the same string repeated |kTestLineNum| times. So we
57  // have to match 2 * |kTestLineNum| lines. The problem is the received lines
58  // from different streams may be interleaved (e.g. we may receive
59  // abc|abcdef|defgh|gh). To deal with that, we allow to test received text
60  // against two lines. The lines MUST NOT have two same characters for this
61  // algorithm to work.
62  virtual void OnSomeRead(pid_t pid, const std::string& type,
63                          const std::string& output) OVERRIDE {
64    EXPECT_EQ(type, kStdoutType);
65    EXPECT_EQ(pid_, pid);
66
67    bool valid = true;
68    for (size_t i = 0; i < output.length(); i++) {
69      // The character output[i] should be next in at least one of the lines we
70      // are testing.
71      valid = (ProcessReceivedCharacter(output[i], 0) ||
72               ProcessReceivedCharacter(output[i], 1));
73      EXPECT_TRUE(valid) << "Received: " << output;
74    }
75
76    if (!valid || TestSucceeded()) {
77      base::MessageLoop::current()->PostTask(FROM_HERE,
78                                             base::MessageLoop::QuitClosure());
79    }
80  }
81
82  virtual void StartRegistryTest(ProcessProxyRegistry* registry) OVERRIDE {
83    for (int i = 0; i < kTestLineNum; i++) {
84      EXPECT_TRUE(registry->SendInput(pid_, kTestLineToSend));
85    }
86  }
87
88 private:
89  bool ProcessReceivedCharacter(char received, size_t stream) {
90    if (stream >= arraysize(left_to_check_index_))
91      return false;
92    bool success = left_to_check_index_[stream] < expected_line_.length() &&
93        expected_line_[left_to_check_index_[stream]] == received;
94    if (success)
95      left_to_check_index_[stream]++;
96    if (left_to_check_index_[stream] == expected_line_.length() &&
97        lines_left_ > 0) {
98      // Take another line to test for this stream, if there are any lines left.
99      // If not, this stream is done.
100      left_to_check_index_[stream] = 0;
101      lines_left_--;
102    }
103    return success;
104  }
105
106  bool TestSucceeded() {
107    return left_to_check_index_[0] == expected_line_.length() &&
108        left_to_check_index_[1] == expected_line_.length() &&
109        lines_left_ == 0;
110  }
111
112  size_t left_to_check_index_[2];
113  size_t lines_left_;
114  std::string expected_line_;
115};
116
117class RegistryNotifiedOnProcessExitTestRunner : public TestRunner {
118 public:
119  virtual ~RegistryNotifiedOnProcessExitTestRunner() {}
120
121  virtual void SetupExpectations(pid_t pid) OVERRIDE {
122    output_received_ = false;
123    pid_ = pid;
124  }
125
126  virtual void OnSomeRead(pid_t pid, const std::string& type,
127                          const std::string& output) OVERRIDE {
128    EXPECT_EQ(pid_, pid);
129    if (!output_received_) {
130      output_received_ = true;
131      EXPECT_EQ(type, "stdout");
132      EXPECT_EQ(output, "p");
133      base::KillProcess(pid_, 0 , true);
134      return;
135    }
136    EXPECT_EQ("exit", type);
137    base::MessageLoop::current()->PostTask(FROM_HERE,
138                                           base::MessageLoop::QuitClosure());
139  }
140
141  virtual void StartRegistryTest(ProcessProxyRegistry* registry) OVERRIDE {
142    EXPECT_TRUE(registry->SendInput(pid_, "p"));
143  }
144
145 private:
146  bool output_received_;
147};
148
149class SigIntTestRunner : public TestRunner {
150 public:
151  virtual ~SigIntTestRunner() {}
152
153  virtual void SetupExpectations(pid_t pid) OVERRIDE {
154    pid_ = pid;
155  }
156
157  virtual void OnSomeRead(pid_t pid, const std::string& type,
158                          const std::string& output) OVERRIDE {
159    EXPECT_EQ(pid_, pid);
160    // We may receive ^C on stdout, but we don't care about that, as long as we
161    // eventually received exit event.
162    if (type == "exit") {
163      base::MessageLoop::current()->PostTask(FROM_HERE,
164                                             base::MessageLoop::QuitClosure());
165    }
166  }
167
168  virtual void StartRegistryTest(ProcessProxyRegistry* registry) OVERRIDE {
169    // Send SingInt and verify the process exited.
170    EXPECT_TRUE(registry->SendInput(pid_, "\003"));
171  }
172};
173
174}  // namespace
175
176class ProcessProxyTest : public testing::Test {
177 public:
178  ProcessProxyTest() {}
179  virtual ~ProcessProxyTest() {}
180
181 protected:
182  void InitRegistryTest() {
183    registry_ = ProcessProxyRegistry::Get();
184
185    EXPECT_TRUE(registry_->OpenProcess(
186                    kCatCommand, &pid_,
187                    base::Bind(&TestRunner::OnSomeRead,
188                               base::Unretained(test_runner_.get()))));
189
190    test_runner_->SetupExpectations(pid_);
191    test_runner_->StartRegistryTest(registry_);
192  }
193
194  void EndRegistryTest() {
195    registry_->CloseProcess(pid_);
196
197    base::TerminationStatus status = base::GetTerminationStatus(pid_, NULL);
198    EXPECT_NE(base::TERMINATION_STATUS_STILL_RUNNING, status);
199    if (status == base::TERMINATION_STATUS_STILL_RUNNING)
200      base::KillProcess(pid_, 0, true);
201
202    base::MessageLoop::current()->PostTask(FROM_HERE,
203                                           base::MessageLoop::QuitClosure());
204  }
205
206  void RunTest() {
207    base::MessageLoop::current()->PostTask(
208        FROM_HERE,
209        base::Bind(&ProcessProxyTest::InitRegistryTest,
210                   base::Unretained(this)));
211
212    // Wait until all data from output watcher is received (QuitTask will be
213    // fired on watcher thread).
214    base::MessageLoop::current()->Run();
215
216    base::MessageLoop::current()->PostTask(
217        FROM_HERE,
218        base::Bind(&ProcessProxyTest::EndRegistryTest,
219                   base::Unretained(this)));
220
221    // Wait until we clean up the process proxy.
222    base::MessageLoop::current()->Run();
223  }
224
225  scoped_ptr<TestRunner> test_runner_;
226
227 private:
228  ProcessProxyRegistry* registry_;
229  pid_t pid_;
230
231  base::MessageLoop message_loop_;
232};
233
234// Test will open new process that will run cat command, and verify data we
235// write to process gets echoed back.
236TEST_F(ProcessProxyTest, RegistryTest) {
237  test_runner_.reset(new RegistryTestRunner());
238  RunTest();
239}
240
241// Open new process, then kill it. Verifiy that we detect when the process dies.
242TEST_F(ProcessProxyTest, RegistryNotifiedOnProcessExit) {
243  test_runner_.reset(new RegistryNotifiedOnProcessExitTestRunner());
244  RunTest();
245}
246
247// Test verifies that \003 message send to process is processed as SigInt.
248// Timing out on the waterfall: http://crbug.com/115064
249TEST_F(ProcessProxyTest, DISABLED_SigInt) {
250  test_runner_.reset(new SigIntTestRunner());
251  RunTest();
252}
253
254}  // namespace chromeos
255