1 //===-- MainLoopTest.cpp ----------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "lldb/Host/MainLoop.h"
11 #include "lldb/Host/ConnectionFileDescriptor.h"
12 #include "lldb/Host/PseudoTerminal.h"
13 #include "lldb/Host/common/TCPSocket.h"
14 #include "gtest/gtest.h"
15 #include <future>
16 
17 using namespace lldb_private;
18 
19 namespace {
20 class MainLoopTest : public testing::Test {
21 public:
22   static void SetUpTestCase() {
23 #ifdef _MSC_VER
24     WSADATA data;
25     ASSERT_EQ(0, WSAStartup(MAKEWORD(2, 2), &data));
26 #endif
27   }
28 
29   static void TearDownTestCase() {
30 #ifdef _MSC_VER
31     ASSERT_EQ(0, WSACleanup());
32 #endif
33   }
34 
35   void SetUp() override {
36     bool child_processes_inherit = false;
37     Status error;
38     std::unique_ptr<TCPSocket> listen_socket_up(
39         new TCPSocket(true, child_processes_inherit));
40     ASSERT_TRUE(error.Success());
41     error = listen_socket_up->Listen("localhost:0", 5);
42     ASSERT_TRUE(error.Success());
43 
44     Socket *accept_socket;
45     std::future<Status> accept_error = std::async(std::launch::async, [&] {
46       return listen_socket_up->Accept(accept_socket);
47     });
48 
49     std::unique_ptr<TCPSocket> connect_socket_up(
50         new TCPSocket(true, child_processes_inherit));
51     error = connect_socket_up->Connect(
52         llvm::formatv("localhost:{0}", listen_socket_up->GetLocalPortNumber())
53             .str());
54     ASSERT_TRUE(error.Success());
55     ASSERT_TRUE(accept_error.get().Success());
56 
57     callback_count = 0;
58     socketpair[0] = std::move(connect_socket_up);
59     socketpair[1].reset(accept_socket);
60   }
61 
62   void TearDown() override {
63     socketpair[0].reset();
64     socketpair[1].reset();
65   }
66 
67 protected:
68   MainLoop::Callback make_callback() {
69     return [&](MainLoopBase &loop) {
70       ++callback_count;
71       loop.RequestTermination();
72     };
73   }
74   std::shared_ptr<Socket> socketpair[2];
75   unsigned callback_count;
76 };
77 } // namespace
78 
79 TEST_F(MainLoopTest, ReadObject) {
80   char X = 'X';
81   size_t len = sizeof(X);
82   ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());
83 
84   MainLoop loop;
85 
86   Status error;
87   auto handle = loop.RegisterReadObject(socketpair[1], make_callback(), error);
88   ASSERT_TRUE(error.Success());
89   ASSERT_TRUE(handle);
90   ASSERT_TRUE(loop.Run().Success());
91   ASSERT_EQ(1u, callback_count);
92 }
93 
94 TEST_F(MainLoopTest, TerminatesImmediately) {
95   char X = 'X';
96   size_t len = sizeof(X);
97   ASSERT_TRUE(socketpair[0]->Write(&X, len).Success());
98   ASSERT_TRUE(socketpair[1]->Write(&X, len).Success());
99 
100   MainLoop loop;
101   Status error;
102   auto handle0 = loop.RegisterReadObject(socketpair[0], make_callback(), error);
103   ASSERT_TRUE(error.Success());
104   auto handle1 = loop.RegisterReadObject(socketpair[1], make_callback(), error);
105   ASSERT_TRUE(error.Success());
106 
107   ASSERT_TRUE(loop.Run().Success());
108   ASSERT_EQ(1u, callback_count);
109 }
110 
111 #ifdef LLVM_ON_UNIX
112 TEST_F(MainLoopTest, DetectsEOF) {
113   PseudoTerminal term;
114   ASSERT_TRUE(term.OpenFirstAvailableMaster(O_RDWR, nullptr, 0));
115   ASSERT_TRUE(term.OpenSlave(O_RDWR | O_NOCTTY, nullptr, 0));
116   auto conn = llvm::make_unique<ConnectionFileDescriptor>(
117       term.ReleaseMasterFileDescriptor(), true);
118 
119   Status error;
120   MainLoop loop;
121   auto handle =
122       loop.RegisterReadObject(conn->GetReadObject(), make_callback(), error);
123   ASSERT_TRUE(error.Success());
124   term.CloseSlaveFileDescriptor();
125 
126   ASSERT_TRUE(loop.Run().Success());
127   ASSERT_EQ(1u, callback_count);
128 }
129 
130 TEST_F(MainLoopTest, Signal) {
131   MainLoop loop;
132   Status error;
133 
134   auto handle = loop.RegisterSignal(SIGUSR1, make_callback(), error);
135   ASSERT_TRUE(error.Success());
136   kill(getpid(), SIGUSR1);
137   ASSERT_TRUE(loop.Run().Success());
138   ASSERT_EQ(1u, callback_count);
139 }
140 #endif
141