• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
21 namespace chromeos {
22 
23 struct TestCase {
TestCasechromeos::TestCase24   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.
TestCasechromeos::TestCase31   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 
43 class ProcessWatcherExpectations {
44  public:
ProcessWatcherExpectations()45   ProcessWatcherExpectations() {}
46 
SetTestCase(const TestCase & test_case)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 
CheckExpectations(const std::string & data,ProcessOutputType type)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 
IsDone()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 
85 class ProcessOutputWatcherTest : public testing::Test {
86  public:
ProcessOutputWatcherTest()87   ProcessOutputWatcherTest() : output_watch_thread_started_(false),
88                                failed_(false) {
89   }
90 
~ProcessOutputWatcherTest()91   virtual ~ProcessOutputWatcherTest() {}
92 
TearDown()93   virtual void TearDown() OVERRIDE {
94     if (output_watch_thread_started_)
95       output_watch_thread_->Stop();
96   }
97 
StartWatch(int pt,int stop)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 
OnRead(ProcessOutputType type,const std::string & output)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:
VeryLongString()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 
RunTest(const std::vector<TestCase> & test_cases)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
TEST_F(ProcessOutputWatcherTest,DISABLED_OutputWatcher)181 TEST_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
TEST_F(ProcessOutputWatcherTest,DISABLED_SplitUTF8Character)197 TEST_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
TEST_F(ProcessOutputWatcherTest,DISABLED_SplitSoleUTF8Character)206 TEST_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
TEST_F(ProcessOutputWatcherTest,DISABLED_SplitUTF8CharacterLength3)215 TEST_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
TEST_F(ProcessOutputWatcherTest,DISABLED_SplitSoleUTF8CharacterThreeWays)224 TEST_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 
TEST_F(ProcessOutputWatcherTest,EndsWithThreeByteUTF8Character)233 TEST_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 
TEST_F(ProcessOutputWatcherTest,SoleThreeByteUTF8Character)240 TEST_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 
TEST_F(ProcessOutputWatcherTest,HasThreeByteUTF8Character)247 TEST_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 
TEST_F(ProcessOutputWatcherTest,MulitByteUTF8CharNullTerminated)255 TEST_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
TEST_F(ProcessOutputWatcherTest,DISABLED_MultipleMultiByteUTF8Characters)263 TEST_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 
TEST_F(ProcessOutputWatcherTest,ContainsInvalidUTF8)272 TEST_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
TEST_F(ProcessOutputWatcherTest,DISABLED_InvalidUTF8SeriesOfTrailingBytes)280 TEST_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 
TEST_F(ProcessOutputWatcherTest,EndsWithInvalidUTF8)288 TEST_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
TEST_F(ProcessOutputWatcherTest,DISABLED_FourByteUTF8)296 TEST_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
TEST_F(ProcessOutputWatcherTest,DISABLED_SendNull)307 TEST_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