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