]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/test-mplexer.cc
Merge pull request #7942 from rgacogne/mplexer-unit-tests-debug
[thirdparty/pdns.git] / pdns / test-mplexer.cc
1
2 #define BOOST_TEST_DYN_LINK
3 #define BOOST_TEST_NO_MAIN
4
5 #include <thread>
6 #include <boost/test/unit_test.hpp>
7
8 #include "mplexer.hh"
9 #include "misc.hh"
10
11 BOOST_AUTO_TEST_SUITE(mplexer)
12
13 BOOST_AUTO_TEST_CASE(test_MPlexer) {
14 auto mplexer = std::unique_ptr<FDMultiplexer>(FDMultiplexer::getMultiplexerSilent());
15 BOOST_REQUIRE(mplexer != nullptr);
16
17 struct timeval now;
18 int ready = mplexer->run(&now, 100);
19 BOOST_CHECK_EQUAL(ready, 0);
20
21 std::vector<int> readyFDs;
22 mplexer->getAvailableFDs(readyFDs, 0);
23 BOOST_CHECK_EQUAL(readyFDs.size(), 0);
24
25 auto timeouts = mplexer->getTimeouts(now);
26 BOOST_CHECK_EQUAL(timeouts.size(), 0);
27
28 int pipes[2];
29 int res = pipe(pipes);
30 BOOST_REQUIRE_EQUAL(res, 0);
31 BOOST_REQUIRE_EQUAL(setNonBlocking(pipes[0]), true);
32 BOOST_REQUIRE_EQUAL(setNonBlocking(pipes[1]), true);
33
34 /* let's declare a TTD that expired 5s ago */
35 struct timeval ttd = now;
36 ttd.tv_sec -= 5;
37
38 bool writeCBCalled = false;
39 auto writeCB = [](int fd, FDMultiplexer::funcparam_t param) {
40 auto calledPtr = boost::any_cast<bool*>(param);
41 BOOST_REQUIRE(calledPtr != nullptr);
42 *calledPtr = true;
43 };
44 mplexer->addWriteFD(pipes[1],
45 writeCB,
46 &writeCBCalled,
47 &ttd);
48 /* we can't add it twice */
49 BOOST_CHECK_THROW(mplexer->addWriteFD(pipes[1],
50 writeCB,
51 &writeCBCalled,
52 &ttd),
53 FDMultiplexerException);
54
55 readyFDs.clear();
56 mplexer->getAvailableFDs(readyFDs, 0);
57 BOOST_REQUIRE_EQUAL(readyFDs.size(), 1);
58 BOOST_CHECK_EQUAL(readyFDs.at(0), pipes[1]);
59
60 ready = mplexer->run(&now, 100);
61 BOOST_CHECK_EQUAL(ready, 1);
62 BOOST_CHECK_EQUAL(writeCBCalled, true);
63
64 /* no read timeouts */
65 timeouts = mplexer->getTimeouts(now, false);
66 BOOST_CHECK_EQUAL(timeouts.size(), 0);
67 /* but we should have a write one */
68 timeouts = mplexer->getTimeouts(now, true);
69 BOOST_REQUIRE_EQUAL(timeouts.size(), 1);
70 BOOST_CHECK_EQUAL(timeouts.at(0).first, pipes[1]);
71
72 /* can't remove from the wrong type of FD */
73 BOOST_CHECK_THROW(mplexer->removeReadFD(pipes[1]), FDMultiplexerException);
74 mplexer->removeWriteFD(pipes[1]);
75 /* can't remove a non-existing FD */
76 BOOST_CHECK_THROW(mplexer->removeWriteFD(pipes[0]), FDMultiplexerException);
77 BOOST_CHECK_THROW(mplexer->removeWriteFD(pipes[1]), FDMultiplexerException);
78
79 readyFDs.clear();
80 mplexer->getAvailableFDs(readyFDs, 0);
81 BOOST_REQUIRE_EQUAL(readyFDs.size(), 0);
82
83 ready = mplexer->run(&now, 100);
84 BOOST_CHECK_EQUAL(ready, 0);
85
86 bool readCBCalled = false;
87 auto readCB = [](int fd, FDMultiplexer::funcparam_t param) {
88 auto calledPtr = boost::any_cast<bool*>(param);
89 BOOST_REQUIRE(calledPtr != nullptr);
90 *calledPtr = true;
91 };
92 mplexer->addReadFD(pipes[0],
93 readCB,
94 &readCBCalled,
95 &ttd);
96
97 /* not ready for reading yet */
98 readyFDs.clear();
99 mplexer->getAvailableFDs(readyFDs, 0);
100 BOOST_REQUIRE_EQUAL(readyFDs.size(), 0);
101
102 ready = mplexer->run(&now, 100);
103 BOOST_CHECK_EQUAL(ready, 0);
104 BOOST_CHECK_EQUAL(readCBCalled, false);
105
106 /* let's make the pipe readable */
107 BOOST_REQUIRE_EQUAL(write(pipes[1], "0", 1), 1);
108
109 readyFDs.clear();
110 mplexer->getAvailableFDs(readyFDs, 0);
111 BOOST_REQUIRE_EQUAL(readyFDs.size(), 1);
112 BOOST_CHECK_EQUAL(readyFDs.at(0), pipes[0]);
113
114 ready = mplexer->run(&now, 100);
115 BOOST_CHECK_EQUAL(ready, 1);
116 BOOST_CHECK_EQUAL(readCBCalled, true);
117
118 /* add back the write FD */
119 mplexer->addWriteFD(pipes[1],
120 writeCB,
121 &writeCBCalled,
122 &ttd);
123
124 /* both should be available */
125 readyFDs.clear();
126 mplexer->getAvailableFDs(readyFDs, 0);
127 if (readyFDs.size() == 1) {
128 /* something is wrong, we need some debug infos */
129 cerr<<"FDMultiPlexer implementation is "<<mplexer->getName()<<endl;
130 cerr<<"Watching "<<mplexer->getWatchedFDCount(false)<<" FDs for read and "<<mplexer->getWatchedFDCount(true)<<" for write"<<endl;
131 cerr<<"pipes[0] is "<<pipes[0]<<endl;
132 cerr<<"pipes[1] is "<<pipes[1]<<endl;
133 cerr<<"The file descripttor returned as ready is "<<readyFDs.at(0)<<endl;
134 char buffer[2];
135 ssize_t res = read(pipes[0], &buffer[0], sizeof(buffer));
136 int saved = errno;
137 cerr<<"Reading from pipes[0] returns "<<res<<endl;
138 if (res == -1) {
139 cerr<<"errno is "<<saved<<endl;
140 }
141 res = write(pipes[1], "0", 1);
142 saved = errno;
143 cerr<<"Writing to pipes[1] returns "<<res<<endl;
144 if (res == -1) {
145 cerr<<"errno is "<<saved<<endl;
146 }
147 }
148 BOOST_REQUIRE_EQUAL(readyFDs.size(), 2);
149
150 readCBCalled = false;
151 writeCBCalled = false;
152 ready = mplexer->run(&now, 100);
153 BOOST_CHECK_EQUAL(ready, 2);
154 BOOST_CHECK_EQUAL(readCBCalled, true);
155 BOOST_CHECK_EQUAL(writeCBCalled, true);
156
157 /* both the read and write FD should be reported */
158 timeouts = mplexer->getTimeouts(now, false);
159 BOOST_REQUIRE_EQUAL(timeouts.size(), 1);
160 BOOST_CHECK_EQUAL(timeouts.at(0).first, pipes[0]);
161 timeouts = mplexer->getTimeouts(now, true);
162 BOOST_REQUIRE_EQUAL(timeouts.size(), 1);
163 BOOST_CHECK_EQUAL(timeouts.at(0).first, pipes[1]);
164
165 struct timeval past = ttd;
166 /* so five seconds before the actual TTD */
167 past.tv_sec -= 5;
168
169 /* no read timeouts */
170 timeouts = mplexer->getTimeouts(past, false);
171 BOOST_CHECK_EQUAL(timeouts.size(), 0);
172 /* and we should not have a write one either */
173 timeouts = mplexer->getTimeouts(past, true);
174 BOOST_CHECK_EQUAL(timeouts.size(), 0);
175
176 /* update the timeouts to now, they should not be reported anymore */
177 mplexer->setReadTTD(pipes[0], now, 0);
178 mplexer->setWriteTTD(pipes[1], now, 0);
179 timeouts = mplexer->getTimeouts(now, false);
180 BOOST_REQUIRE_EQUAL(timeouts.size(), 0);
181 timeouts = mplexer->getTimeouts(now, true);
182 BOOST_REQUIRE_EQUAL(timeouts.size(), 0);
183
184 /* put it back into the past */
185 mplexer->setReadTTD(pipes[0], now, -5);
186 mplexer->setWriteTTD(pipes[1], now, -5);
187 timeouts = mplexer->getTimeouts(now, false);
188 BOOST_REQUIRE_EQUAL(timeouts.size(), 1);
189 BOOST_CHECK_EQUAL(timeouts.at(0).first, pipes[0]);
190 timeouts = mplexer->getTimeouts(now, true);
191 BOOST_REQUIRE_EQUAL(timeouts.size(), 1);
192 BOOST_CHECK_EQUAL(timeouts.at(0).first, pipes[1]);
193
194 mplexer->removeReadFD(pipes[0]);
195 mplexer->removeWriteFD(pipes[1]);
196
197 /* clean up */
198 close(pipes[0]);
199 close(pipes[1]);
200 }
201
202
203 BOOST_AUTO_TEST_SUITE_END()