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