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/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
181TEST_F(ProcessOutputWatcherTest, 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
196TEST_F(ProcessOutputWatcherTest, SplitUTF8Character) {
197  std::vector<TestCase> test_cases;
198  test_cases.push_back(TestCase("test1\xc2", false, "test1"));
199  test_cases.push_back(TestCase("\xb5test1", false, "\xc2\xb5test1"));
200
201  RunTest(test_cases);
202}
203
204TEST_F(ProcessOutputWatcherTest, SplitSoleUTF8Character) {
205  std::vector<TestCase> test_cases;
206  test_cases.push_back(TestCase("\xc2", false, ""));
207  test_cases.push_back(TestCase("\xb5", false, "\xc2\xb5"));
208
209  RunTest(test_cases);
210}
211
212TEST_F(ProcessOutputWatcherTest, SplitUTF8CharacterLength3) {
213  std::vector<TestCase> test_cases;
214  test_cases.push_back(TestCase("test3\xe2\x82", false, "test3"));
215  test_cases.push_back(TestCase("\xac", false, "\xe2\x82\xac"));
216
217  RunTest(test_cases);
218}
219
220TEST_F(ProcessOutputWatcherTest, SplitSoleUTF8CharacterThreeWays) {
221  std::vector<TestCase> test_cases;
222  test_cases.push_back(TestCase("\xe2", false, ""));
223  test_cases.push_back(TestCase("\x82", false, ""));
224  test_cases.push_back(TestCase("\xac", false, "\xe2\x82\xac"));
225
226  RunTest(test_cases);
227}
228
229TEST_F(ProcessOutputWatcherTest, EndsWithThreeByteUTF8Character) {
230  std::vector<TestCase> test_cases;
231  test_cases.push_back(TestCase("test\xe2\x82\xac", false, "test\xe2\x82\xac"));
232
233  RunTest(test_cases);
234}
235
236TEST_F(ProcessOutputWatcherTest, SoleThreeByteUTF8Character) {
237  std::vector<TestCase> test_cases;
238  test_cases.push_back(TestCase("\xe2\x82\xac", false, "\xe2\x82\xac"));
239
240  RunTest(test_cases);
241}
242
243TEST_F(ProcessOutputWatcherTest, HasThreeByteUTF8Character) {
244  std::vector<TestCase> test_cases;
245  test_cases.push_back(
246      TestCase("test\xe2\x82\xac_", false, "test\xe2\x82\xac_"));
247
248  RunTest(test_cases);
249}
250
251TEST_F(ProcessOutputWatcherTest, MulitByteUTF8CharNullTerminated) {
252  std::vector<TestCase> test_cases;
253  test_cases.push_back(TestCase("test\xe2\x82\xac", true, "test\xe2\x82\xac"));
254
255  RunTest(test_cases);
256}
257
258TEST_F(ProcessOutputWatcherTest, MultipleMultiByteUTF8Characters) {
259  std::vector<TestCase> test_cases;
260  test_cases.push_back(
261      TestCase("test\xe2\x82\xac\xc2", false, "test\xe2\x82\xac"));
262  test_cases.push_back(TestCase("\xb5", false, "\xc2\xb5"));
263
264  RunTest(test_cases);
265}
266
267TEST_F(ProcessOutputWatcherTest, ContainsInvalidUTF8) {
268  std::vector<TestCase> test_cases;
269  test_cases.push_back(TestCase("\xc2_", false, "\xc2_"));
270
271  RunTest(test_cases);
272}
273
274TEST_F(ProcessOutputWatcherTest, InvalidUTF8SeriesOfTrailingBytes) {
275  std::vector<TestCase> test_cases;
276  test_cases.push_back(TestCase("\x82\x82\x82", false, "\x82\x82\x82"));
277  test_cases.push_back(TestCase("\x82\x82\x82", false, "\x82\x82\x82"));
278
279  RunTest(test_cases);
280}
281
282TEST_F(ProcessOutputWatcherTest, EndsWithInvalidUTF8) {
283  std::vector<TestCase> test_cases;
284  test_cases.push_back(TestCase("\xff", false, "\xff"));
285
286  RunTest(test_cases);
287}
288
289TEST_F(ProcessOutputWatcherTest, FourByteUTF8) {
290  std::vector<TestCase> test_cases;
291  test_cases.push_back(TestCase("\xf0\xa4\xad", false, ""));
292  test_cases.push_back(TestCase("\xa2", false, "\xf0\xa4\xad\xa2"));
293
294  RunTest(test_cases);
295};
296
297// Verifies that sending '\0' generates PROCESS_OUTPUT_TYPE_OUT event and does
298// not terminate output watcher.
299TEST_F(ProcessOutputWatcherTest, SendNull) {
300  std::vector<TestCase> test_cases;
301  // This will send '\0' to output watcher.
302  test_cases.push_back(TestCase("", true));
303  // Let's verify that next input also gets detected (i.e. output watcher does
304  // not exit after seeing '\0' from previous test case).
305  test_cases.push_back(TestCase("a", true));
306
307  RunTest(test_cases);
308};
309
310}  // namespace chromeos
311