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 <queue>
8#include <string>
9#include <vector>
10
11#include "base/bind.h"
12#include "base/callback.h"
13#include "base/files/file_util.h"
14#include "base/message_loop/message_loop.h"
15#include "base/posix/eintr_wrapper.h"
16#include "base/run_loop.h"
17#include "base/strings/string_util.h"
18#include "base/threading/thread.h"
19#include "chromeos/process_proxy/process_output_watcher.h"
20
21namespace chromeos {
22
23struct TestCase {
24  TestCase(const std::string& input, bool send_terminating_null)
25      : input(input),
26        should_send_terminating_null(send_terminating_null),
27        expected_output(input) {}
28
29  // Conctructor for cases where the output is not expected to be the same as
30  // input.
31  TestCase(const std::string& input,
32           bool send_terminating_null,
33           const std::string& expected_output)
34      : input(input),
35        should_send_terminating_null(send_terminating_null),
36        expected_output(expected_output) {}
37
38  std::string input;
39  bool should_send_terminating_null;
40  std::string expected_output;
41};
42
43class ProcessWatcherExpectations {
44 public:
45  ProcessWatcherExpectations() {}
46
47  void SetTestCase(const TestCase& test_case) {
48    received_from_out_ = 0;
49
50    out_expectations_ = test_case.expected_output;
51    if (test_case.should_send_terminating_null)
52      out_expectations_.append(std::string("", 1));
53  }
54
55  bool CheckExpectations(const std::string& data, ProcessOutputType type) {
56    EXPECT_EQ(PROCESS_OUTPUT_TYPE_OUT, type);
57    if (type != PROCESS_OUTPUT_TYPE_OUT)
58      return false;
59
60    if (out_expectations_.length() == 0 && data.length() == 0)
61      return true;
62
63    EXPECT_LT(received_from_out_, out_expectations_.length());
64    if (received_from_out_ >= out_expectations_.length())
65      return false;
66
67    EXPECT_EQ(received_from_out_,
68              out_expectations_.find(data, received_from_out_));
69    if (received_from_out_ != out_expectations_.find(data, received_from_out_))
70      return false;
71
72    received_from_out_ += data.length();
73    return true;
74  }
75
76  bool IsDone() {
77    return received_from_out_ >= out_expectations_.length();
78  }
79
80 private:
81  std::string out_expectations_;
82  size_t received_from_out_;
83};
84
85class ProcessOutputWatcherTest : public testing::Test {
86 public:
87  ProcessOutputWatcherTest() : output_watch_thread_started_(false),
88                               failed_(false) {
89  }
90
91  virtual ~ProcessOutputWatcherTest() {}
92
93  virtual void TearDown() OVERRIDE {
94    if (output_watch_thread_started_)
95      output_watch_thread_->Stop();
96  }
97
98  void StartWatch(int pt, int stop) {
99    // This will delete itself.
100    ProcessOutputWatcher* crosh_watcher = new ProcessOutputWatcher(pt, stop,
101        base::Bind(&ProcessOutputWatcherTest::OnRead, base::Unretained(this)));
102    crosh_watcher->Start();
103  }
104
105  void OnRead(ProcessOutputType type, const std::string& output) {
106    ASSERT_FALSE(failed_);
107    failed_ = !expectations_.CheckExpectations(output, type);
108    if (failed_ || expectations_.IsDone()) {
109      ASSERT_FALSE(test_case_done_callback_.is_null());
110      message_loop_.PostTask(FROM_HERE, test_case_done_callback_);
111      test_case_done_callback_.Reset();
112    }
113  }
114
115 protected:
116  std::string VeryLongString() {
117    std::string result = "0123456789";
118    for (int i = 0; i < 8; i++)
119      result = result.append(result);
120    return result;
121  }
122
123  void RunTest(const std::vector<TestCase>& test_cases) {
124    ASSERT_FALSE(output_watch_thread_started_);
125    output_watch_thread_.reset(new base::Thread("ProcessOutpuWatchThread"));
126    output_watch_thread_started_ = output_watch_thread_->Start();
127    ASSERT_TRUE(output_watch_thread_started_);
128
129    int pt_pipe[2], stop_pipe[2];
130    ASSERT_FALSE(HANDLE_EINTR(pipe(pt_pipe)));
131    ASSERT_FALSE(HANDLE_EINTR(pipe(stop_pipe)));
132
133    output_watch_thread_->message_loop()->PostTask(
134        FROM_HERE,
135        base::Bind(&ProcessOutputWatcherTest::StartWatch,
136                   base::Unretained(this),
137                   pt_pipe[0],
138                   stop_pipe[0]));
139
140    for (size_t i = 0; i < test_cases.size(); i++) {
141      expectations_.SetTestCase(test_cases[i]);
142
143      base::RunLoop run_loop;
144      ASSERT_TRUE(test_case_done_callback_.is_null());
145      test_case_done_callback_ = run_loop.QuitClosure();
146
147      const std::string& test_str = test_cases[i].input;
148      // Let's make inputs not NULL terminated, unless other is specified in
149      // the test case.
150      ssize_t test_size = test_str.length() * sizeof(*test_str.c_str());
151      if (test_cases[i].should_send_terminating_null)
152        test_size += sizeof(*test_str.c_str());
153      EXPECT_EQ(test_size,
154                base::WriteFileDescriptor(pt_pipe[1], test_str.c_str(),
155                                          test_size));
156
157      run_loop.Run();
158      EXPECT_TRUE(expectations_.IsDone());
159      if (failed_)
160        break;
161    }
162
163    // Send stop signal. It is not important which string we send.
164    EXPECT_EQ(1, base::WriteFileDescriptor(stop_pipe[1], "q", 1));
165
166    EXPECT_NE(-1, IGNORE_EINTR(close(stop_pipe[1])));
167    EXPECT_NE(-1, IGNORE_EINTR(close(pt_pipe[1])));
168  }
169
170 private:
171  base::Closure test_case_done_callback_;
172  base::MessageLoop message_loop_;
173  scoped_ptr<base::Thread> output_watch_thread_;
174  bool output_watch_thread_started_;
175  bool failed_;
176  ProcessWatcherExpectations expectations_;
177  std::vector<TestCase> exp;
178};
179
180// http://crbug.com/396496
181TEST_F(ProcessOutputWatcherTest, DISABLED_OutputWatcher) {
182  std::vector<TestCase> test_cases;
183  test_cases.push_back(TestCase("t", false));
184  test_cases.push_back(TestCase("testing output\n", false));
185  test_cases.push_back(TestCase("testing error\n", false));
186  test_cases.push_back(TestCase("testing error1\n", false));
187  test_cases.push_back(TestCase("testing output1\n", false));
188  test_cases.push_back(TestCase("testing output2\n", false));
189  test_cases.push_back(TestCase("testing output3\n", false));
190  test_cases.push_back(TestCase(VeryLongString(), false));
191  test_cases.push_back(TestCase("testing error2\n", false));
192
193  RunTest(test_cases);
194}
195
196// http://crbug.com/396496
197TEST_F(ProcessOutputWatcherTest, DISABLED_SplitUTF8Character) {
198  std::vector<TestCase> test_cases;
199  test_cases.push_back(TestCase("test1\xc2", false, "test1"));
200  test_cases.push_back(TestCase("\xb5test1", false, "\xc2\xb5test1"));
201
202  RunTest(test_cases);
203}
204
205// http://crbug.com/396496
206TEST_F(ProcessOutputWatcherTest, DISABLED_SplitSoleUTF8Character) {
207  std::vector<TestCase> test_cases;
208  test_cases.push_back(TestCase("\xc2", false, ""));
209  test_cases.push_back(TestCase("\xb5", false, "\xc2\xb5"));
210
211  RunTest(test_cases);
212}
213
214// http://crbug.com/396496
215TEST_F(ProcessOutputWatcherTest, DISABLED_SplitUTF8CharacterLength3) {
216  std::vector<TestCase> test_cases;
217  test_cases.push_back(TestCase("test3\xe2\x82", false, "test3"));
218  test_cases.push_back(TestCase("\xac", false, "\xe2\x82\xac"));
219
220  RunTest(test_cases);
221}
222
223// http://crbug.com/396496
224TEST_F(ProcessOutputWatcherTest, DISABLED_SplitSoleUTF8CharacterThreeWays) {
225  std::vector<TestCase> test_cases;
226  test_cases.push_back(TestCase("\xe2", false, ""));
227  test_cases.push_back(TestCase("\x82", false, ""));
228  test_cases.push_back(TestCase("\xac", false, "\xe2\x82\xac"));
229
230  RunTest(test_cases);
231}
232
233TEST_F(ProcessOutputWatcherTest, EndsWithThreeByteUTF8Character) {
234  std::vector<TestCase> test_cases;
235  test_cases.push_back(TestCase("test\xe2\x82\xac", false, "test\xe2\x82\xac"));
236
237  RunTest(test_cases);
238}
239
240TEST_F(ProcessOutputWatcherTest, SoleThreeByteUTF8Character) {
241  std::vector<TestCase> test_cases;
242  test_cases.push_back(TestCase("\xe2\x82\xac", false, "\xe2\x82\xac"));
243
244  RunTest(test_cases);
245}
246
247TEST_F(ProcessOutputWatcherTest, HasThreeByteUTF8Character) {
248  std::vector<TestCase> test_cases;
249  test_cases.push_back(
250      TestCase("test\xe2\x82\xac_", false, "test\xe2\x82\xac_"));
251
252  RunTest(test_cases);
253}
254
255TEST_F(ProcessOutputWatcherTest, MulitByteUTF8CharNullTerminated) {
256  std::vector<TestCase> test_cases;
257  test_cases.push_back(TestCase("test\xe2\x82\xac", true, "test\xe2\x82\xac"));
258
259  RunTest(test_cases);
260}
261
262// http://crbug.com/396496
263TEST_F(ProcessOutputWatcherTest, DISABLED_MultipleMultiByteUTF8Characters) {
264  std::vector<TestCase> test_cases;
265  test_cases.push_back(
266      TestCase("test\xe2\x82\xac\xc2", false, "test\xe2\x82\xac"));
267  test_cases.push_back(TestCase("\xb5", false, "\xc2\xb5"));
268
269  RunTest(test_cases);
270}
271
272TEST_F(ProcessOutputWatcherTest, ContainsInvalidUTF8) {
273  std::vector<TestCase> test_cases;
274  test_cases.push_back(TestCase("\xc2_", false, "\xc2_"));
275
276  RunTest(test_cases);
277}
278
279// http://crbug.com/396496
280TEST_F(ProcessOutputWatcherTest, DISABLED_InvalidUTF8SeriesOfTrailingBytes) {
281  std::vector<TestCase> test_cases;
282  test_cases.push_back(TestCase("\x82\x82\x82", false, "\x82\x82\x82"));
283  test_cases.push_back(TestCase("\x82\x82\x82", false, "\x82\x82\x82"));
284
285  RunTest(test_cases);
286}
287
288TEST_F(ProcessOutputWatcherTest, EndsWithInvalidUTF8) {
289  std::vector<TestCase> test_cases;
290  test_cases.push_back(TestCase("\xff", false, "\xff"));
291
292  RunTest(test_cases);
293}
294
295// http://crbug.com/396496
296TEST_F(ProcessOutputWatcherTest, DISABLED_FourByteUTF8) {
297  std::vector<TestCase> test_cases;
298  test_cases.push_back(TestCase("\xf0\xa4\xad", false, ""));
299  test_cases.push_back(TestCase("\xa2", false, "\xf0\xa4\xad\xa2"));
300
301  RunTest(test_cases);
302}
303
304// Verifies that sending '\0' generates PROCESS_OUTPUT_TYPE_OUT event and does
305// not terminate output watcher.
306// http://crbug.com/396496
307TEST_F(ProcessOutputWatcherTest, DISABLED_SendNull) {
308  std::vector<TestCase> test_cases;
309  // This will send '\0' to output watcher.
310  test_cases.push_back(TestCase("", true));
311  // Let's verify that next input also gets detected (i.e. output watcher does
312  // not exit after seeing '\0' from previous test case).
313  test_cases.push_back(TestCase("a", true));
314
315  RunTest(test_cases);
316}
317
318}  // namespace chromeos
319