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 #ifndef _WIN32
31 # include <netinet/in.h>
32 # include <poll.h>
33 # include <sys/socket.h>
34 # include <unistd.h>
35 #else
36 # include <winsock2.h> // For struct timeval
37 # include <ws2tcpip.h> // Must be after winsock2.h because MS didn't put proper inclusion guards in their headers.
38 typedef unsigned long int nfds_t;
39 #endif
40 #include <stdlib.h>
41 #include <sys/types.h>
42 #include <time.h>
43 #include <errno.h>
44 
45 #include <iostream>
46 #include <functional>
47 
48 #include <gtest/gtest.h>
49 
50 // Mocks for select and poll. The build file specifies -Wl,--wrap for both of
51 // these, so the original symbols are available as __real_xxx and any uses of
52 // those symbols instead use __wrap_xxx
53 extern "C" {
54 // Mock for poll
55 int (*fake_poll)(struct pollfd *, nfds_t, int) = 0;
56 
57 int __wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout) {
58  if(fake_poll) {
59  return fake_poll(fds, nfds, timeout);
60  } else {
61  return 0;
62  }
63 }
64 
65 }
66 
67 int poll_calls = 0;
68 int poll_ret = 0;
69 int poll_errno = 0;
70 int poll_timeout = 0;
71 std::vector<pollfd> poll_fds;
72 int mock_poll(struct pollfd *fds, nfds_t nfds, int timeout) {
73  EXPECT_EQ(1, poll_calls);
74  poll_calls--;
75 
76  EXPECT_EQ(poll_fds.size(), nfds);
77  EXPECT_EQ(poll_timeout, timeout);
78 
79  for(nfds_t i=0; i<nfds && i<poll_fds.size(); i++) {
80  EXPECT_EQ(poll_fds[i].fd, fds[i].fd);
81  EXPECT_EQ(poll_fds[i].events, fds[i].events);
82  fds[i].revents = poll_fds[i].revents;
83  }
84 
85  // HACK: Sleep for the requested duration
86  // This can be thought of as a simulation of select() getting an event
87  // exactly at the end of the timeout window, but really it's just here
88  // because the dispatch loop has its own timer to determine if it should call
89  // select again, and we don't want multiple calls.
90  if(timeout > 0) {
91  timespec ts;
92  ts.tv_sec = timeout / 1000;
93  ts.tv_nsec = (timeout % 1000) * 1000000;
94  // Call nanosleep repeatedly until it returns 0 (success).
95  // On failure it will update ts with the remaining time to sleep.
96  int ret = 0;
97  do {
98 #ifndef _WIN32
99  ret = nanosleep(&ts, &ts);
100 #else
101  Sleep(timeout);
102 #endif
103  } while( ret != 0 && errno == EINTR);
104  }
105 
106  errno = poll_errno;
107  return poll_ret;
108 }
109 
110 void Expect_poll(std::vector<pollfd> fds, int timeout, int _errno, int ret) {
111  EXPECT_EQ(0, poll_calls) << "Test bug: Cannot expect more than one call to poll";
112  poll_calls = 1;
113  poll_ret = ret;
114  poll_errno = _errno;
115  poll_timeout = timeout;
116  poll_fds = fds;
117 }
118 
119 using namespace XmlRpc;
120 
121 class MockSource : public XmlRpcSource {
122 public:
123  MockSource(int fd)
124  : handleEvent_calls(0), last_event(0), event_result(0), close_calls(0) {
125  setfd(fd);
126  }
127 
128  virtual ~MockSource() {
129  }
130 
131  virtual unsigned handleEvent(unsigned eventType) {
132  handleEvent_calls++;
133  last_event = eventType;
134  return event_result;
135  }
136 
137  virtual void close() {
138  close_calls++;
139  }
140 
141  // variables used for mocking
143  unsigned last_event;
144  unsigned event_result;
145 
147 };
148 
149 #define EXPECT_CLOSE_CALLS(n) \
150  do { \
151  EXPECT_EQ(m.close_calls, n); \
152  m.close_calls = 0; \
153  } while (0)
154 
155 #define EXPECT_EVENTS(n) \
156  do { \
157  EXPECT_EQ(m.handleEvent_calls, n); \
158  m.handleEvent_calls = 0; \
159  } while (0)
160 
161 #define EXPECT_EVENT(event) \
162  do { \
163  EXPECT_EQ(m.last_event, event); \
164  EXPECT_EQ(m.handleEvent_calls, 1); \
165  m.handleEvent_calls = 0; \
166  } while (0)
167 
168 class MockSourceTest : public ::testing::Test {
169  protected:
170  MockSourceTest() : m(4) {
171 #ifndef _WIN32
172  pollfd f = { .fd = 4, .events = 0, .revents = 0 };
173 #else
174  pollfd f {};
175  f.fd = 4;
176  f.events = 0;
177  f.revents = 0;
178 #endif
179  fds.push_back(f);
180  }
181 
182  void SetUp() {
184  poll_calls = 0;
185  }
186 
187  void TearDown() {
188  EXPECT_EQ(0, poll_calls);
189  fake_poll = 0;
190  poll_calls = 0;
191  }
192 
195  std::vector<pollfd> fds;
196 };
197 
198 /*
199  * Use a socket to provide the requisite file descriptor
200  *
201  * Tests to perform on XmlRpcDispatch
202  * - Matrix of the following options:
203  * - Proper handling of setKeepOpen
204  * - Proper handling of deleteOnClose
205  * - Proper handling of return values from handleEvent
206  * - Proper handling of file descriptor states
207  * - Correct masking of events by the eventMask for the source
208  * - Correct handling of exceptional file descriptor states
209  * - These states seem to mostly be related to sending OOB data over TCP; I
210  * don't see a way to simulate them with pipes, but it should be possible
211  * to loop back a TCP connection and generate that condition directly
212  * - Check that the argument to handleEvent matches the event that was
213  * simulated
214  * - Check that handleEvent is not called if no event was triggered
215  * - Proper handling of timeout in XmlRpcDispatch work() method
216  * - Proper removal of sources from _sources when errors occur
217  * - If possible, trigger error return values from select(); maybe one of the
218  * following error cases from the select(2) man page:
219  * - Invalid file descriptor in set (already closed?)
220  * - Signal caught
221  * - nfds negative or invalid timeout
222  * - unable to allocate memory
223  * - Proper handling of multiple XmlRpcSource objects
224  * - Multiple events during a single work() cycle
225  * - Events delivered to the correct Source
226  */
227 
228 TEST_F(MockSourceTest, ReadEvent) {
229  m.event_result = XmlRpcDispatch::ReadableEvent;
230  dispatch.addSource(&m, XmlRpcDispatch::ReadableEvent);
231  EXPECT_EQ(dispatch._sources.size(), 1u);
232 
233  // Select returns not readable; expect no events.
234  fds[0].events = POLLIN;
235  fds[0].revents = 0;
236  Expect_poll(fds, 100, 0, 0);
237  dispatch.work(0.1);
238 
240  EXPECT_EVENTS(0);
241 
242  // Select returns readable, expect readable event
243  fds[0].events = POLLIN;
244  fds[0].revents = POLLIN;
245  Expect_poll(fds, 100, 0, 0);
246  dispatch.work(0.1);
249 }
250 
251 TEST_F(MockSourceTest, WriteEvent) {
252  m.setKeepOpen();
253  m.event_result = 0;
254  dispatch.addSource(&m, XmlRpcDispatch::WritableEvent);
255  EXPECT_EQ(dispatch._sources.size(), 1u);
256 
257  // Select returns writeable, expect one write event.
258  fds[0].events = POLLOUT;
259  fds[0].revents = POLLOUT;
260  Expect_poll(fds, 100, 0, 0);
261  dispatch.work(0.1);
263  // We have keepOpen set, so don't expect a close call
265  // However, even if keepOpen is set, we expect the socket to be removed from
266  // the sources list
267  EXPECT_EQ(dispatch._sources.size(), 0u);
268 
269  // Expect no more events. Since there's nothing in the dispatch list, we
270  // don't expect that select will be called.
271  dispatch.work(0.1);
273  EXPECT_EVENTS(0);
274 }
275 
276 TEST_F(MockSourceTest, NonWriteable) {
277  m.event_result = XmlRpcDispatch::WritableEvent;
278  dispatch.addSource(&m, XmlRpcDispatch::WritableEvent);
279  EXPECT_EQ(dispatch._sources.size(), 1u);
280 
281  // Select doesn't return writable.
282  fds[0].events = POLLOUT;
283  fds[0].revents = 0;
284  Expect_poll(fds, 100, 0, 0);
285  dispatch.work(0.1);
286  EXPECT_EVENTS(0);
288  EXPECT_EQ(dispatch._sources.size(), 1u);
289 }
290 
291 TEST_F(MockSourceTest, WriteClose) {
292  m.event_result = 0;
293  dispatch.addSource(&m, XmlRpcDispatch::WritableEvent);
294  EXPECT_EQ(dispatch._sources.size(), 1u);
295 
296  // Socket is always writeable. Expect 1 write event since we clear the write
297  // event flag after we write once
298  fds[0].events = POLLOUT;
299  fds[0].revents = POLLOUT;
300  Expect_poll(fds, 100, 0, 0);
301  dispatch.work(0.1);
303 
304  // Since we returned 0 from handleEvent and don't have keepOpen set, expect
305  // that the dispatch has called close() once and that the size of sources is
306  // now 0
308  EXPECT_EQ(dispatch._sources.size(), 0u);
309 
310  // Expect no more events. Since there's nothing in the dispatch list, we
311  // don't expect that select will be called.
312  dispatch.work(0.1);
314  EXPECT_EVENTS(0);
315 }
316 
317 TEST_F(MockSourceTest, Exception) {
318  m.event_result = XmlRpcDispatch::Exception;
319  dispatch.addSource(&m, XmlRpcDispatch::Exception);
320  EXPECT_EQ(dispatch._sources.size(), 1u);
321 
322  // Select returns no exception, so expect that the handler was not called.
323  fds[0].events = POLLPRI;
324  fds[0].revents = 0;
325  Expect_poll(fds, 100, 0, 0);
326  dispatch.work(0.1);
328  EXPECT_EVENTS(0);
329 
330  // Make exception, expect exception event.
331  fds[0].events = POLLPRI;
332  fds[0].revents = POLLPRI;
333  Expect_poll(fds, 100, 0, 0);
334  dispatch.work(0.1);
337 }
338 
339 // Test that dispatch works (or doesn't) with file descriptors above 1024
341  m.setfd(1025);
342  m.event_result = XmlRpcDispatch::WritableEvent;
343  dispatch.addSource(&m, XmlRpcDispatch::WritableEvent);
344  EXPECT_EQ(dispatch._sources.size(), 1u);
345 
346  // Make select return writable, expect 1 write event.
347  fds[0].fd = 1025;
348  fds[0].events = POLLOUT;
349  fds[0].revents = POLLOUT;
350  Expect_poll(fds, 100, 0, 0);
351  dispatch.work(0.1);
354  EXPECT_EQ(dispatch._sources.size(), 1u);
355 }
356 
357 int main(int argc, char **argv)
358 {
359  ::testing::InitGoogleTest(&argc, argv);
360  return RUN_ALL_TESTS();
361 }
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:60
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, Dirk Thomas
autogenerated on Mon Feb 28 2022 23:33:22