test_dispatch.cpp
Go to the documentation of this file.
1 /*
2  * Unit tests for XmlRpc++
3  *
4  * Copyright (C) 2017, Zoox Inc
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  * Author: Austin Hendrix <austin@zoox.com>
21  *
22  */
23 
25 #include "xmlrpcpp/XmlRpcSource.h"
26 #include "xmlrpcpp/XmlRpcSocket.h"
27 #include "mock_socket.h"
28 
29 #include <fcntl.h>
30 #include <netinet/in.h>
31 #include <poll.h>
32 #include <stdlib.h>
33 #include <sys/socket.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36 #include <time.h>
37 #include <errno.h>
38 
39 #include <iostream>
40 #include <functional>
41 
42 #include <gtest/gtest.h>
43 
44 // Mocks for select and poll. The build file specifies -Wl,--wrap for both of
45 // these, so the original symbols are available as __real_xxx and any uses of
46 // those symbols instead use __wrap_xxx
47 extern "C" {
48 // Mock for poll
49 int (*fake_poll)(struct pollfd *, nfds_t, int) = 0;
50 
51 int __wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout) {
52  if(fake_poll) {
53  return fake_poll(fds, nfds, timeout);
54  } else {
55  return 0;
56  }
57 }
58 
59 }
60 
61 int poll_calls = 0;
62 int poll_ret = 0;
63 int poll_errno = 0;
64 int poll_timeout = 0;
65 std::vector<pollfd> poll_fds;
66 int mock_poll(struct pollfd *fds, nfds_t nfds, int timeout) {
67  EXPECT_EQ(1, poll_calls);
68  poll_calls--;
69 
70  EXPECT_EQ(poll_fds.size(), nfds);
71  EXPECT_EQ(poll_timeout, timeout);
72 
73  for(nfds_t i=0; i<nfds && i<poll_fds.size(); i++) {
74  EXPECT_EQ(poll_fds[i].fd, fds[i].fd);
75  EXPECT_EQ(poll_fds[i].events, fds[i].events);
76  fds[i].revents = poll_fds[i].revents;
77  }
78 
79  // HACK: Sleep for the requested duration
80  // This can be thought of as a simulation of select() getting an event
81  // exactly at the end of the timeout window, but really it's just here
82  // because the dispatch loop has its own timer to determine if it should call
83  // select again, and we don't want multiple calls.
84  if(timeout > 0) {
85  timespec ts;
86  ts.tv_sec = timeout / 1000;
87  ts.tv_nsec = (timeout % 1000) * 1000000;
88  // Call nanosleep repeatedly until it returns 0 (success).
89  // On failure it will update ts with the remaining time to sleep.
90  int ret = 0;
91  do {
92  ret = nanosleep(&ts, &ts);
93  } while( ret != 0 && errno == EINTR);
94  }
95 
96  errno = poll_errno;
97  return poll_ret;
98 }
99 
100 void Expect_poll(std::vector<pollfd> fds, int timeout, int _errno, int ret) {
101  EXPECT_EQ(0, poll_calls) << "Test bug: Cannot expect more than one call to poll";
102  poll_calls = 1;
103  poll_ret = ret;
104  poll_errno = _errno;
105  poll_timeout = timeout;
106  poll_fds = fds;
107 }
108 
109 using namespace XmlRpc;
110 
111 class MockSource : public XmlRpcSource {
112 public:
113  MockSource(int fd)
114  : handleEvent_calls(0), last_event(0), event_result(0), close_calls(0) {
115  setfd(fd);
116  }
117 
118  virtual ~MockSource() {
119  }
120 
121  virtual unsigned handleEvent(unsigned eventType) {
122  handleEvent_calls++;
123  last_event = eventType;
124  return event_result;
125  }
126 
127  virtual void close() {
128  close_calls++;
129  }
130 
131  // variables used for mocking
133  unsigned last_event;
134  unsigned event_result;
135 
137 };
138 
139 #define EXPECT_CLOSE_CALLS(n) \
140  do { \
141  EXPECT_EQ(m.close_calls, n); \
142  m.close_calls = 0; \
143  } while (0)
144 
145 #define EXPECT_EVENTS(n) \
146  do { \
147  EXPECT_EQ(m.handleEvent_calls, n); \
148  m.handleEvent_calls = 0; \
149  } while (0)
150 
151 #define EXPECT_EVENT(event) \
152  do { \
153  EXPECT_EQ(m.last_event, event); \
154  EXPECT_EQ(m.handleEvent_calls, 1); \
155  m.handleEvent_calls = 0; \
156  } while (0)
157 
158 class MockSourceTest : public ::testing::Test {
159  protected:
160  MockSourceTest() : m(4) {
161  pollfd f = { .fd = 4, .events = 0, .revents = 0 };
162  fds.push_back(f);
163  }
164 
165  void SetUp() {
167  poll_calls = 0;
168  }
169 
170  void TearDown() {
171  EXPECT_EQ(0, poll_calls);
172  fake_poll = 0;
173  poll_calls = 0;
174  }
175 
178  std::vector<pollfd> fds;
179 };
180 
181 /*
182  * Use a socket to provide the requisite file descriptor
183  *
184  * Tests to perform on XmlRpcDispatch
185  * - Matrix of the following options:
186  * - Proper handling of setKeepOpen
187  * - Proper handling of deleteOnClose
188  * - Proper handling of return values from handleEvent
189  * - Proper handling of file descriptor states
190  * - Correct masking of events by the eventMask for the source
191  * - Correct handling of exceptional file descriptor states
192  * - These states seem to mostly be related to sending OOB data over TCP; I
193  * don't see a way to simulate them with pipes, but it should be possible
194  * to loop back a TCP connection and generate that condition directly
195  * - Check that the argument to handleEvent matches the event that was
196  * simulated
197  * - Check that handleEvent is not called if no event was triggered
198  * - Proper handling of timeout in XmlRpcDispatch work() method
199  * - Proper removal of sources from _sources when errors occur
200  * - If possible, trigger error return values from select(); maybe one of the
201  * following error cases from the select(2) man page:
202  * - Invalid file descriptor in set (already closed?)
203  * - Signal caught
204  * - nfds negative or invalid timeout
205  * - unable to allocate memory
206  * - Proper handling of multiple XmlRpcSource objects
207  * - Multiple events during a single work() cycle
208  * - Events delivered to the correct Source
209  */
210 
211 TEST_F(MockSourceTest, ReadEvent) {
212  m.event_result = XmlRpcDispatch::ReadableEvent;
213  dispatch.addSource(&m, XmlRpcDispatch::ReadableEvent);
214  EXPECT_EQ(dispatch._sources.size(), 1u);
215 
216  // Select returns not readable; expect no events.
217  fds[0].events = POLLIN;
218  fds[0].revents = 0;
219  Expect_poll(fds, 100, 0, 0);
220  dispatch.work(0.1);
221 
223  EXPECT_EVENTS(0);
224 
225  // Select returns readable, expect readable event
226  fds[0].events = POLLIN;
227  fds[0].revents = POLLIN;
228  Expect_poll(fds, 100, 0, 0);
229  dispatch.work(0.1);
232 }
233 
234 TEST_F(MockSourceTest, WriteEvent) {
235  m.setKeepOpen();
236  m.event_result = 0;
237  dispatch.addSource(&m, XmlRpcDispatch::WritableEvent);
238  EXPECT_EQ(dispatch._sources.size(), 1u);
239 
240  // Select returns writeable, expect one write event.
241  fds[0].events = POLLOUT;
242  fds[0].revents = POLLOUT;
243  Expect_poll(fds, 100, 0, 0);
244  dispatch.work(0.1);
246  // We have keepOpen set, so don't expect a close call
248  // However, even if keepOpen is set, we expect the socket to be removed from
249  // the sources list
250  EXPECT_EQ(dispatch._sources.size(), 0u);
251 
252  // Expect no more events. Since there's nothing in the dispatch list, we
253  // don't expect that select will be called.
254  dispatch.work(0.1);
256  EXPECT_EVENTS(0);
257 }
258 
259 TEST_F(MockSourceTest, NonWriteable) {
260  m.event_result = XmlRpcDispatch::WritableEvent;
261  dispatch.addSource(&m, XmlRpcDispatch::WritableEvent);
262  EXPECT_EQ(dispatch._sources.size(), 1u);
263 
264  // Select doesn't return writable.
265  fds[0].events = POLLOUT;
266  fds[0].revents = 0;
267  Expect_poll(fds, 100, 0, 0);
268  dispatch.work(0.1);
269  EXPECT_EVENTS(0);
271  EXPECT_EQ(dispatch._sources.size(), 1u);
272 }
273 
274 TEST_F(MockSourceTest, WriteClose) {
275  m.event_result = 0;
276  dispatch.addSource(&m, XmlRpcDispatch::WritableEvent);
277  EXPECT_EQ(dispatch._sources.size(), 1u);
278 
279  // Socket is always writeable. Expect 1 write event since we clear the write
280  // event flag after we write once
281  fds[0].events = POLLOUT;
282  fds[0].revents = POLLOUT;
283  Expect_poll(fds, 100, 0, 0);
284  dispatch.work(0.1);
286 
287  // Since we returned 0 from handleEvent and don't have keepOpen set, expect
288  // that the dispatch has called close() once and that the size of sources is
289  // now 0
291  EXPECT_EQ(dispatch._sources.size(), 0u);
292 
293  // Expect no more events. Since there's nothing in the dispatch list, we
294  // don't expect that select will be called.
295  dispatch.work(0.1);
297  EXPECT_EVENTS(0);
298 }
299 
300 TEST_F(MockSourceTest, Exception) {
301  m.event_result = XmlRpcDispatch::Exception;
302  dispatch.addSource(&m, XmlRpcDispatch::Exception);
303  EXPECT_EQ(dispatch._sources.size(), 1u);
304 
305  // Select returns no exception, so expect that the handler was not called.
306  fds[0].events = POLLPRI;
307  fds[0].revents = 0;
308  Expect_poll(fds, 100, 0, 0);
309  dispatch.work(0.1);
311  EXPECT_EVENTS(0);
312 
313  // Make exception, expect exception event.
314  fds[0].events = POLLPRI;
315  fds[0].revents = POLLPRI;
316  Expect_poll(fds, 100, 0, 0);
317  dispatch.work(0.1);
320 }
321 
322 // Test that dispatch works (or doesn't) with file descriptors above 1024
324  m.setfd(1025);
325  m.event_result = XmlRpcDispatch::WritableEvent;
326  dispatch.addSource(&m, XmlRpcDispatch::WritableEvent);
327  EXPECT_EQ(dispatch._sources.size(), 1u);
328 
329  // Make select return writable, expect 1 write event.
330  fds[0].fd = 1025;
331  fds[0].events = POLLOUT;
332  fds[0].revents = POLLOUT;
333  Expect_poll(fds, 100, 0, 0);
334  dispatch.work(0.1);
337  EXPECT_EQ(dispatch._sources.size(), 1u);
338 }
339 
340 int main(int argc, char **argv)
341 {
342  ::testing::InitGoogleTest(&argc, argv);
343  return RUN_ALL_TESTS();
344 }
MockSource(int fd)
#define EXPECT_EVENTS(n)
connected/data can be written without blocking
int poll_calls
TEST_F(MockSourceTest, ReadEvent)
int poll_ret
int mock_poll(struct pollfd *fds, nfds_t nfds, int timeout)
void Expect_poll(std::vector< pollfd > fds, int timeout, int _errno, int ret)
unsigned last_event
An RPC source represents a file descriptor to monitor.
Definition: XmlRpcSource.h:16
std::deque< int > close_calls
Definition: mock_socket.cpp:61
int poll_errno
std::vector< pollfd > fds
virtual unsigned handleEvent(unsigned eventType)
Return true to continue monitoring this source.
unsigned event_result
#define EXPECT_CLOSE_CALLS(n)
int(* fake_poll)(struct pollfd *, nfds_t, int)=0
std::vector< pollfd > poll_fds
int main(int argc, char **argv)
out-of-band data has arrived
#define EXPECT_EVENT(event)
virtual void close()
Close the owned fd. If deleteOnClose was specified at construction, the object is deleted...
int __wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout)
virtual ~MockSource()
XmlRpcDispatch dispatch
int poll_timeout


xmlrpcpp
Author(s): Chris Morley, Konstantin Pilipchuk, Morgan Quigley, Austin Hendrix
autogenerated on Sun Feb 3 2019 03:29:51