|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include <gtest/gtest.h> | 
|  | #include <stddef.h> | 
|  |  | 
|  | #include <string> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/location.h" | 
|  | #include "base/memory/scoped_ptr.h" | 
|  | #include "base/process/kill.h" | 
|  | #include "base/process/process.h" | 
|  | #include "base/single_thread_task_runner.h" | 
|  | #include "base/thread_task_runner_handle.h" | 
|  | #include "base/threading/thread.h" | 
|  | #include "chromeos/process_proxy/process_proxy_registry.h" | 
|  |  | 
|  | namespace chromeos { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // The test line must have all distinct characters. | 
|  | const char kTestLineToSend[] = "abcdefgh\n"; | 
|  | const char kTestLineExpected[] = "abcdefgh\r\n"; | 
|  |  | 
|  | const char kCatCommand[] = "cat"; | 
|  | const char kStdoutType[] = "stdout"; | 
|  | const int kTestLineNum = 100; | 
|  |  | 
|  | class TestRunner { | 
|  | public: | 
|  | virtual ~TestRunner() {} | 
|  | virtual void SetupExpectations(int terminal_id) = 0; | 
|  | virtual void OnSomeRead(int terminal_id, | 
|  | const std::string& type, | 
|  | const std::string& output) = 0; | 
|  | virtual void StartRegistryTest(ProcessProxyRegistry* registry) = 0; | 
|  |  | 
|  | protected: | 
|  | int terminal_id_; | 
|  | }; | 
|  |  | 
|  | class RegistryTestRunner : public TestRunner { | 
|  | public: | 
|  | ~RegistryTestRunner() override {} | 
|  |  | 
|  | void SetupExpectations(int terminal_id) override { | 
|  | terminal_id_ = terminal_id; | 
|  | left_to_check_index_[0] = 0; | 
|  | left_to_check_index_[1] = 0; | 
|  | // We consider that a line processing has started if a value in | 
|  | // left_to_check__[index] is set to 0, thus -2. | 
|  | lines_left_ = 2 * kTestLineNum - 2; | 
|  | expected_line_ = kTestLineExpected; | 
|  | } | 
|  |  | 
|  | // Method to test validity of received input. We will receive two streams of | 
|  | // the same data. (input will be echoed twice by the testing process). Each | 
|  | // stream will contain the same string repeated |kTestLineNum| times. So we | 
|  | // have to match 2 * |kTestLineNum| lines. The problem is the received lines | 
|  | // from different streams may be interleaved (e.g. we may receive | 
|  | // abc|abcdef|defgh|gh). To deal with that, we allow to test received text | 
|  | // against two lines. The lines MUST NOT have two same characters for this | 
|  | // algorithm to work. | 
|  | void OnSomeRead(int terminal_id, | 
|  | const std::string& type, | 
|  | const std::string& output) override { | 
|  | EXPECT_EQ(type, kStdoutType); | 
|  | EXPECT_EQ(terminal_id_, terminal_id); | 
|  |  | 
|  | bool valid = true; | 
|  | for (size_t i = 0; i < output.length(); i++) { | 
|  | // The character output[i] should be next in at least one of the lines we | 
|  | // are testing. | 
|  | valid = (ProcessReceivedCharacter(output[i], 0) || | 
|  | ProcessReceivedCharacter(output[i], 1)); | 
|  | EXPECT_TRUE(valid) << "Received: " << output; | 
|  | } | 
|  |  | 
|  | if (!valid || TestSucceeded()) { | 
|  | base::ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void StartRegistryTest(ProcessProxyRegistry* registry) override { | 
|  | for (int i = 0; i < kTestLineNum; i++) { | 
|  | EXPECT_TRUE(registry->SendInput(terminal_id_, kTestLineToSend)); | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool ProcessReceivedCharacter(char received, size_t stream) { | 
|  | if (stream >= arraysize(left_to_check_index_)) | 
|  | return false; | 
|  | bool success = left_to_check_index_[stream] < expected_line_.length() && | 
|  | expected_line_[left_to_check_index_[stream]] == received; | 
|  | if (success) | 
|  | left_to_check_index_[stream]++; | 
|  | if (left_to_check_index_[stream] == expected_line_.length() && | 
|  | lines_left_ > 0) { | 
|  | // Take another line to test for this stream, if there are any lines left. | 
|  | // If not, this stream is done. | 
|  | left_to_check_index_[stream] = 0; | 
|  | lines_left_--; | 
|  | } | 
|  | return success; | 
|  | } | 
|  |  | 
|  | bool TestSucceeded() { | 
|  | return left_to_check_index_[0] == expected_line_.length() && | 
|  | left_to_check_index_[1] == expected_line_.length() && | 
|  | lines_left_ == 0; | 
|  | } | 
|  |  | 
|  | size_t left_to_check_index_[2]; | 
|  | size_t lines_left_; | 
|  | std::string expected_line_; | 
|  | }; | 
|  |  | 
|  | class RegistryNotifiedOnProcessExitTestRunner : public TestRunner { | 
|  | public: | 
|  | ~RegistryNotifiedOnProcessExitTestRunner() override {} | 
|  |  | 
|  | void SetupExpectations(int terminal_id) override { | 
|  | output_received_ = false; | 
|  | terminal_id_ = terminal_id; | 
|  | } | 
|  |  | 
|  | void OnSomeRead(int terminal_id, | 
|  | const std::string& type, | 
|  | const std::string& output) override { | 
|  | EXPECT_EQ(terminal_id_, terminal_id); | 
|  | if (!output_received_) { | 
|  | output_received_ = true; | 
|  | EXPECT_EQ(type, "stdout"); | 
|  | EXPECT_EQ(output, "p"); | 
|  | base::Process process = | 
|  | base::Process::DeprecatedGetProcessFromHandle(terminal_id_); | 
|  | process.Terminate(0, true); | 
|  | return; | 
|  | } | 
|  | EXPECT_EQ("exit", type); | 
|  | base::ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); | 
|  | } | 
|  |  | 
|  | void StartRegistryTest(ProcessProxyRegistry* registry) override { | 
|  | EXPECT_TRUE(registry->SendInput(terminal_id_, "p")); | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool output_received_; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class ProcessProxyTest : public testing::Test { | 
|  | public: | 
|  | ProcessProxyTest() {} | 
|  | ~ProcessProxyTest() override {} | 
|  |  | 
|  | protected: | 
|  | void InitRegistryTest() { | 
|  | registry_ = ProcessProxyRegistry::Get(); | 
|  |  | 
|  | terminal_id_ = registry_->OpenProcess( | 
|  | kCatCommand, | 
|  | base::Bind(&ProcessProxyTest::HandleRead, base::Unretained(this))); | 
|  |  | 
|  | EXPECT_GE(terminal_id_, 0); | 
|  | test_runner_->SetupExpectations(terminal_id_); | 
|  | test_runner_->StartRegistryTest(registry_); | 
|  | } | 
|  |  | 
|  | void HandleRead(int terminal_id, | 
|  | const std::string& output_type, | 
|  | const std::string& output) { | 
|  | test_runner_->OnSomeRead(terminal_id, output_type, output); | 
|  | registry_->AckOutput(terminal_id); | 
|  | } | 
|  |  | 
|  | void EndRegistryTest() { | 
|  | registry_->CloseProcess(terminal_id_); | 
|  |  | 
|  | base::TerminationStatus status = | 
|  | base::GetTerminationStatus(terminal_id_, NULL); | 
|  | EXPECT_NE(base::TERMINATION_STATUS_STILL_RUNNING, status); | 
|  | if (status == base::TERMINATION_STATUS_STILL_RUNNING) { | 
|  | base::Process process = | 
|  | base::Process::DeprecatedGetProcessFromHandle(terminal_id_); | 
|  | process.Terminate(0, true); | 
|  | } | 
|  |  | 
|  | registry_->ShutDown(); | 
|  |  | 
|  | base::ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); | 
|  | } | 
|  |  | 
|  | void RunTest() { | 
|  | base::ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, base::Bind(&ProcessProxyTest::InitRegistryTest, | 
|  | base::Unretained(this))); | 
|  |  | 
|  | // Wait until all data from output watcher is received (QuitTask will be | 
|  | // fired on watcher thread). | 
|  | base::MessageLoop::current()->Run(); | 
|  |  | 
|  | base::ThreadTaskRunnerHandle::Get()->PostTask( | 
|  | FROM_HERE, | 
|  | base::Bind(&ProcessProxyTest::EndRegistryTest, base::Unretained(this))); | 
|  |  | 
|  | // Wait until we clean up the process proxy. | 
|  | base::MessageLoop::current()->Run(); | 
|  | } | 
|  |  | 
|  | scoped_ptr<TestRunner> test_runner_; | 
|  |  | 
|  | private: | 
|  | ProcessProxyRegistry* registry_; | 
|  | int terminal_id_; | 
|  |  | 
|  | base::MessageLoop message_loop_; | 
|  | }; | 
|  |  | 
|  | // Test will open new process that will run cat command, and verify data we | 
|  | // write to process gets echoed back. | 
|  | TEST_F(ProcessProxyTest, RegistryTest) { | 
|  | test_runner_.reset(new RegistryTestRunner()); | 
|  | RunTest(); | 
|  | } | 
|  |  | 
|  | // Open new process, then kill it. Verifiy that we detect when the process dies. | 
|  | TEST_F(ProcessProxyTest, RegistryNotifiedOnProcessExit) { | 
|  | test_runner_.reset(new RegistryNotifiedOnProcessExitTestRunner()); | 
|  | RunTest(); | 
|  | } | 
|  |  | 
|  | }  // namespace chromeos |