| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 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 "blimp/test/fake_engine/fake_engine.h" | |
| 6 | |
| 7 #include <fcntl.h> | |
| 8 #include <sys/socket.h> | |
| 9 #include <sys/un.h> | |
| 10 | |
| 11 #include "base/command_line.h" | |
| 12 #include "base/files/file_path.h" | |
| 13 #include "base/files/file_util.h" | |
| 14 #include "base/synchronization/waitable_event.h" | |
| 15 #include "base/test/launcher/unit_test_launcher.h" | |
| 16 #include "base/test/test_suite.h" | |
| 17 #include "blimp/test/fake_engine/proto/engine.grpc.pb.h" | |
| 18 #include "blimp/test/fake_engine/proto/lifetime.grpc.pb.h" | |
| 19 #include "blimp/test/fake_engine/proto/logging.grpc.pb.h" | |
| 20 #include "testing/gmock/include/gmock/gmock.h" | |
| 21 #include "testing/gtest/include/gtest/gtest.h" | |
| 22 #include "third_party/grpc/include/grpc++/create_channel_posix.h" | |
| 23 #include "third_party/grpc/include/grpc++/grpc++.h" | |
| 24 #include "third_party/grpc/include/grpc++/server_posix.h" | |
| 25 | |
| 26 using ::testing::_; | |
| 27 | |
| 28 namespace blimp { | |
| 29 | |
| 30 namespace { | |
| 31 | |
| 32 bool SocketPair(int* fd1, int* fd2) { | |
| 33 int pipe_fds[2]; | |
| 34 if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds) != 0) { | |
| 35 PLOG(ERROR) << "socketpair()"; | |
| 36 return false; | |
| 37 } | |
| 38 | |
| 39 // Set both ends to be non-blocking. | |
| 40 if (fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK) == -1 || | |
| 41 fcntl(pipe_fds[1], F_SETFL, O_NONBLOCK) == -1) { | |
| 42 PLOG(ERROR) << "fcntl(O_NONBLOCK)"; | |
| 43 if (IGNORE_EINTR(close(pipe_fds[0])) < 0) | |
| 44 PLOG(ERROR) << "close"; | |
| 45 if (IGNORE_EINTR(close(pipe_fds[1])) < 0) | |
| 46 PLOG(ERROR) << "close"; | |
| 47 return false; | |
| 48 } | |
| 49 | |
| 50 *fd1 = pipe_fds[0]; | |
| 51 *fd2 = pipe_fds[1]; | |
| 52 | |
| 53 return true; | |
| 54 } | |
| 55 | |
| 56 class MockLifetimeService : public Lifetime::Service { | |
| 57 public: | |
| 58 MOCK_METHOD3(EngineReady, grpc::Status( | |
| 59 grpc::ServerContext*, const EngineReadyRequest*, EngineReadyResponse*)); | |
| 60 }; | |
| 61 | |
| 62 class MockLoggingService : public Logging::Service { | |
| 63 public: | |
| 64 MOCK_METHOD3(Log, grpc::Status( | |
| 65 grpc::ServerContext*, const LogRequest*, LogResponse*)); | |
| 66 }; | |
| 67 | |
| 68 // The FakeEngineAppTest suite sets up a simple interface resembling the | |
| 69 // Service, which is required by the Fake Engine. This allows testing whether | |
| 70 // the Fake Engine serves its purpose of simulating the real Engine as spawned | |
| 71 // by the Service. | |
| 72 class FakeEngineAppTest : public testing::Test { | |
| 73 public: | |
| 74 FakeEngineAppTest() | |
| 75 : engine_ready_(base::WaitableEvent::ResetPolicy::MANUAL, | |
| 76 base::WaitableEvent::InitialState::NOT_SIGNALED) {} | |
| 77 | |
| 78 protected: | |
| 79 void SetUp() override { | |
| 80 CHECK(SocketPair(&rendering_server_connect_fd_, &engine_listen_fd_)); | |
| 81 CHECK(SocketPair(&rendering_server_listen_fd_, &engine_connect_fd_)); | |
| 82 | |
| 83 grpc::ServerBuilder builder; | |
| 84 builder.RegisterService(&mock_lifetime_service_); | |
| 85 builder.RegisterService(&mock_logging_service_); | |
| 86 grpc_server_ = builder.BuildAndStart(); | |
| 87 | |
| 88 // TODO(xyzzyz): Remove fcntl when https://github.com/grpc/grpc/pull/8051 is | |
| 89 // merged and added to Chromium. | |
| 90 int flags = fcntl(rendering_server_listen_fd_, F_GETFL, 0); | |
| 91 CHECK_EQ(0, fcntl(rendering_server_listen_fd_, F_SETFL, | |
| 92 flags | O_NONBLOCK)); | |
| 93 | |
| 94 grpc::AddInsecureChannelFromFd(grpc_server_.get(), | |
| 95 rendering_server_listen_fd_); | |
| 96 } | |
| 97 | |
| 98 // This function spawns a Fake Engine as a child process, and sets up the file | |
| 99 // descriptor mappings for it, resembling the mappings in the Service | |
| 100 // environment. It also sets up the handler for the EngineReady function that | |
| 101 // will be called by the Fake Engine upon startup. | |
| 102 base::Process SpawnEngine() { | |
| 103 // Find the path of the Engine. | |
| 104 base::CommandLine current_cmd = *base::CommandLine::ForCurrentProcess(); | |
| 105 base::FilePath current_program_path = base::MakeAbsoluteFilePath( | |
| 106 current_cmd.GetProgram()); | |
| 107 base::FilePath current_dir = current_program_path.DirName(); | |
| 108 base::FilePath fake_engine_path = current_dir.Append("fake_engine_app"); | |
| 109 | |
| 110 base::CommandLine engine_cmd(fake_engine_path); | |
| 111 | |
| 112 base::LaunchOptions options; | |
| 113 base::FileHandleMappingVector fds_to_remap = { | |
| 114 { engine_listen_fd_, kEngineListenFd }, | |
| 115 { engine_connect_fd_, kRenderingServerListenFd } | |
| 116 }; | |
| 117 options.fds_to_remap = &fds_to_remap; | |
| 118 | |
| 119 ON_CALL(mock_lifetime_service_, EngineReady(_, _, _)) | |
| 120 .WillByDefault( | |
| 121 testing::Invoke(this, &FakeEngineAppTest::EngineReadyHandler)); | |
| 122 return base::LaunchProcess(engine_cmd, options); | |
| 123 } | |
| 124 | |
| 125 grpc::Status EngineReadyHandler( | |
| 126 grpc::ServerContext*, const EngineReadyRequest*, EngineReadyResponse*) { | |
| 127 engine_ready_.Signal(); | |
| 128 return grpc::Status::OK; | |
| 129 } | |
| 130 | |
| 131 bool WaitForEngineReady(base::TimeDelta timeout) { | |
| 132 return engine_ready_.TimedWait(timeout); | |
| 133 } | |
| 134 | |
| 135 std::unique_ptr<Engine::Stub> GetEngineStub() { | |
| 136 CHECK(!engine_channel_); | |
| 137 engine_channel_ = grpc::CreateInsecureChannelFromFd( | |
| 138 "fake_engine", rendering_server_connect_fd_); | |
| 139 CHECK(engine_channel_); | |
| 140 return Engine::NewStub(engine_channel_); | |
| 141 } | |
| 142 | |
| 143 private: | |
| 144 int engine_listen_fd_; | |
| 145 int rendering_server_connect_fd_; | |
| 146 | |
| 147 int rendering_server_listen_fd_; | |
| 148 int engine_connect_fd_; | |
| 149 | |
| 150 std::unique_ptr<grpc::Server> grpc_server_; | |
| 151 MockLifetimeService mock_lifetime_service_; | |
| 152 MockLoggingService mock_logging_service_; | |
| 153 | |
| 154 std::shared_ptr<grpc::Channel> engine_channel_; | |
| 155 base::WaitableEvent engine_ready_; | |
| 156 | |
| 157 DISALLOW_COPY_AND_ASSIGN(FakeEngineAppTest); | |
| 158 }; | |
| 159 | |
| 160 | |
| 161 TEST_F(FakeEngineAppTest, Basic) { | |
| 162 base::Process engine_process = SpawnEngine(); | |
| 163 EXPECT_TRUE(WaitForEngineReady(base::TimeDelta::FromSeconds(5))); | |
| 164 | |
| 165 std::unique_ptr<Engine::Stub> engine_stub = GetEngineStub(); | |
| 166 { | |
| 167 CheckHealthRequest request; | |
| 168 CheckHealthResponse response; | |
| 169 | |
| 170 grpc::ClientContext context; | |
| 171 grpc::Status status = engine_stub->CheckHealth( | |
| 172 &context, request, &response); | |
| 173 EXPECT_TRUE(status.ok()); | |
| 174 EXPECT_EQ(response.status(), CheckHealthResponse::OK); | |
| 175 } | |
| 176 | |
| 177 { | |
| 178 ShutDownRequest request; | |
| 179 ShutDownResponse response; | |
| 180 | |
| 181 grpc::ClientContext context; | |
| 182 grpc::CompletionQueue cq; | |
| 183 // We do async call, as the Fake Engine doesn't send any reply to the | |
| 184 // ShutDown RPC, so it makes no sense to wait for it. | |
| 185 auto rpc = engine_stub->AsyncShutDown(&context, request, &cq); | |
| 186 | |
| 187 EXPECT_TRUE(engine_process.WaitForExitWithTimeout( | |
| 188 base::TimeDelta::FromSeconds(5), nullptr /* exit_code */)); | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 class FakeEngineTestSuite : public base::TestSuite { | |
| 193 public: | |
| 194 FakeEngineTestSuite(int argc, char** argv) : base::TestSuite(argc, argv) {} | |
| 195 | |
| 196 protected: | |
| 197 void Initialize() override { | |
| 198 base::TestSuite::Initialize(); | |
| 199 } | |
| 200 | |
| 201 private: | |
| 202 DISALLOW_COPY_AND_ASSIGN(FakeEngineTestSuite); | |
| 203 }; | |
| 204 | |
| 205 } // namespace | |
| 206 | |
| 207 } // namespace blimp | |
| 208 | |
| 209 int main(int argc, char** argv) { | |
| 210 blimp::FakeEngineTestSuite test_suite(argc, argv); | |
| 211 return base::LaunchUnitTests( | |
| 212 argc, argv, | |
| 213 base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite))); | |
| 214 } | |
| OLD | NEW |