mavtables  0.2.1
MAVLink router and firewall.
test_UnixSerialPort.cpp
Go to the documentation of this file.
1 // MAVLink router and firewall.
2 // Copyright (C) 2018 Michael R. Shannon <mrshannon.aerospace@gmail.com>
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 
17 
18 #include <chrono>
19 #include <cstdint>
20 #include <cstring>
21 #include <stdexcept>
22 #include <string>
23 #include <system_error>
24 
25 #include <catch.hpp>
26 #include <errno.h>
27 #include <fakeit.hpp>
28 
29 #include "PartialSendError.hpp"
30 #include "UnixSerialPort.hpp"
31 #include "UnixSyscalls.hpp"
32 #include "utility.hpp"
33 
34 #include "common.hpp"
35 
36 
37 using namespace std::chrono_literals;
38 
39 
40 TEST_CASE("UnixSerialPort's open and configure a serial port on construction"
41  "and closes the port on destruction.", "[UnixSerialPort]")
42 {
43  SECTION("Without hardware flow control (no errors).")
44  {
45  // Mock system calls.
46  fakeit::Mock<UnixSyscalls> mock_sys;
47  // Mock 'open'.
48  fakeit::When(Method(mock_sys, open)).AlwaysReturn(3);
49  // Mock 'tcgetattr'.
50  fakeit::When(Method(mock_sys, tcgetattr)).AlwaysDo(
51  [&](auto fd, auto termios_p)
52  {
53  (void)fd;
54  std::memset(termios_p, '\0', sizeof(struct termios));
55  return 0;
56  });
57  // Mock 'tcsetattr'.
58  struct termios tty;
59  fakeit::When(Method(mock_sys, tcsetattr)).AlwaysDo(
60  [&](auto fd, auto action, auto termios_p)
61  {
62  (void)fd;
63  (void)action;
64  std::memcpy(&tty, termios_p, sizeof(struct termios));
65  return 0;
66  });
67  // Mock 'close'.
68  fakeit::When(Method(mock_sys, close)).AlwaysReturn(0);
69  {
70  // Construct port.
71  UnixSerialPort port(
72  "/dev/ttyUSB0", 9600,
74  mock_unique(mock_sys));
75  // Verify 'open' system call.
76  fakeit::Verify(Method(mock_sys, open).Matching(
77  [](auto path, auto flags)
78  {
79  return std::string(path) == "/dev/ttyUSB0" &&
80  flags == (O_RDWR | O_NOCTTY | O_SYNC);
81  })).Once();
82  // Verify 'tcgetattr'.
83  fakeit::Verify(Method(mock_sys, tcgetattr).Matching(
84  [&](auto fd, auto termios_p)
85  {
86  (void)termios_p;
87  return fd == 3;
88  })).Once();
89  // Verify 'tcsetattr'.
90  fakeit::Verify(Method(mock_sys, tcsetattr).Matching(
91  [](auto fd, auto action, auto termios_p)
92  {
93  (void)termios_p;
94  return fd == 3 && action == TCSANOW;
95  })).Once();
96  REQUIRE(cfgetispeed(&tty) == B9600);
97  REQUIRE(cfgetospeed(&tty) == B9600);
98  REQUIRE((tty.c_cflag & CLOCAL) != 0);
99  REQUIRE((tty.c_cflag & CREAD) != 0);
100  REQUIRE((tty.c_cflag & PARENB) == 0);
101  REQUIRE((tty.c_cflag & CSTOPB) == 0);
102  REQUIRE((tty.c_cflag & CS8) != 0);
103  REQUIRE((tty.c_cflag & CRTSCTS) == 0);
104  REQUIRE((tty.c_lflag & ICANON) == 0);
105  REQUIRE((tty.c_lflag & ECHO) == 0);
106  REQUIRE((tty.c_lflag & ECHOE) == 0);
107  REQUIRE((tty.c_lflag & ISIG) == 0);
108  REQUIRE((tty.c_iflag & IGNBRK) == 0);
109  REQUIRE((tty.c_iflag & BRKINT) == 0);
110  REQUIRE((tty.c_iflag & PARMRK) == 0);
111  REQUIRE((tty.c_iflag & ISTRIP) == 0);
112  REQUIRE((tty.c_iflag & INLCR) == 0);
113  REQUIRE((tty.c_iflag & IGNCR) == 0);
114  REQUIRE((tty.c_iflag & ICRNL) == 0);
115  REQUIRE((tty.c_iflag & IXON) == 0);
116  REQUIRE((tty.c_iflag & IXOFF) == 0);
117  REQUIRE((tty.c_iflag & IXANY) == 0);
118  REQUIRE((tty.c_oflag & OPOST) == 0);
119  REQUIRE(tty.c_cc[VMIN] == 0);
120  REQUIRE(tty.c_cc[VTIME] == 0);
121  // Verify 'close'.
122  fakeit::Verify(Method(mock_sys, close).Using(3)).Exactly(0);
123  }
124  // Verify 'close'.
125  fakeit::Verify(Method(mock_sys, close).Using(3)).Once();
126  }
127  SECTION("With hardware flow control (no errors).")
128  {
129  // Mock system calls.
130  fakeit::Mock<UnixSyscalls> mock_sys;
131  // Mock 'open'.
132  fakeit::When(Method(mock_sys, open)).Return(3);
133  // Mock 'tcgetattr'.
134  fakeit::When(Method(mock_sys, tcgetattr)).Do(
135  [&](auto fd, auto termios_p)
136  {
137  (void)fd;
138  std::memset(termios_p, '\0', sizeof(struct termios));
139  return 0;
140  });
141  // Mock 'tcsetattr'.
142  struct termios tty;
143  fakeit::When(Method(mock_sys, tcsetattr)).Do(
144  [&](auto fd, auto action, auto termios_p)
145  {
146  (void)fd;
147  (void)action;
148  std::memcpy(&tty, termios_p, sizeof(struct termios));
149  return 0;
150  });
151  // Mock 'close'.
152  fakeit::When(Method(mock_sys, close)).Return(0);
153  {
154  // Construct port.
155  UnixSerialPort port(
156  "/dev/ttyUSB0", 9600,
158  mock_unique(mock_sys));
159  // Verify 'open' system call.
160  fakeit::Verify(Method(mock_sys, open).Matching(
161  [](auto path, auto flags)
162  {
163  return std::string(path) == "/dev/ttyUSB0" &&
164  flags == (O_RDWR | O_NOCTTY | O_SYNC);
165  })).Once();
166  // Verify 'tcgetattr'.
167  fakeit::Verify(Method(mock_sys, tcgetattr).Matching(
168  [&](auto fd, auto termios_p)
169  {
170  (void)termios_p;
171  return fd == 3;
172  })).Once();
173  // Verify 'tcsetattr'.
174  fakeit::Verify(Method(mock_sys, tcsetattr).Matching(
175  [](auto fd, auto action, auto termios_p)
176  {
177  (void)termios_p;
178  return fd == 3 && action == TCSANOW;
179  })).Once();
180  REQUIRE(cfgetispeed(&tty) == B9600);
181  REQUIRE(cfgetospeed(&tty) == B9600);
182  REQUIRE((tty.c_cflag & CLOCAL) != 0);
183  REQUIRE((tty.c_cflag & CREAD) != 0);
184  REQUIRE((tty.c_cflag & PARENB) == 0);
185  REQUIRE((tty.c_cflag & CSTOPB) == 0);
186  REQUIRE((tty.c_cflag & CS8) != 0);
187  REQUIRE((tty.c_cflag & CRTSCTS) != 0);
188  REQUIRE((tty.c_lflag & ICANON) == 0);
189  REQUIRE((tty.c_lflag & ECHO) == 0);
190  REQUIRE((tty.c_lflag & ECHOE) == 0);
191  REQUIRE((tty.c_lflag & ISIG) == 0);
192  REQUIRE((tty.c_iflag & IGNBRK) == 0);
193  REQUIRE((tty.c_iflag & BRKINT) == 0);
194  REQUIRE((tty.c_iflag & PARMRK) == 0);
195  REQUIRE((tty.c_iflag & ISTRIP) == 0);
196  REQUIRE((tty.c_iflag & INLCR) == 0);
197  REQUIRE((tty.c_iflag & IGNCR) == 0);
198  REQUIRE((tty.c_iflag & ICRNL) == 0);
199  REQUIRE((tty.c_iflag & IXON) == 0);
200  REQUIRE((tty.c_iflag & IXOFF) == 0);
201  REQUIRE((tty.c_iflag & IXANY) == 0);
202  REQUIRE((tty.c_oflag & OPOST) == 0);
203  REQUIRE(tty.c_cc[VMIN] == 0);
204  REQUIRE(tty.c_cc[VTIME] == 0);
205  // Verify 'close'.
206  fakeit::Verify(Method(mock_sys, close).Using(3)).Exactly(0);
207  }
208  // Verify 'close'.
209  fakeit::Verify(Method(mock_sys, close).Using(3)).Once();
210  }
211  SECTION("Emmits errors from 'open' system call.")
212  {
213  fakeit::Mock<UnixSyscalls> mock_sys;
214  fakeit::When(Method(mock_sys, open)).AlwaysReturn(-1);
215  std::array<int, 26> errors{{
216  EACCES,
217  EDQUOT,
218  EEXIST,
219  EFAULT,
220  EFBIG,
221  EINTR,
222  EINVAL,
223  EISDIR,
224  ELOOP,
225  EMFILE,
226  ENAMETOOLONG,
227  ENFILE,
228  ENODEV,
229  ENOENT,
230  ENOMEM,
231  ENOSPC,
232  ENOTDIR,
233  ENXIO,
234  EOPNOTSUPP,
235  EOVERFLOW,
236  EPERM,
237  EROFS,
238  ETXTBSY,
239  EWOULDBLOCK,
240  EBADF,
241  ENOTDIR
242  }};
243 
244  for (auto error : errors)
245  {
246  errno = error;
247  REQUIRE_THROWS_AS(UnixSerialPort(
248  "/dev/ttyUSB0", 9600, SerialPort::DEFAULT,
249  mock_unique(mock_sys)), std::system_error);
250  }
251 
252  fakeit::Verify(Method(mock_sys, close).Using(3)).Exactly(0);
253  }
254  SECTION("Emmits errors from 'tcgetattr' system call, and closes the port.")
255  {
256  fakeit::Mock<UnixSyscalls> mock_sys;
257  fakeit::When(Method(mock_sys, open)).AlwaysReturn(3);
258  fakeit::When(Method(mock_sys, close)).AlwaysReturn(0);
259  fakeit::When(Method(mock_sys, tcgetattr)).AlwaysReturn(-1);
260  std::array<int, 2> errors{{
261  EBADF,
262  ENOTTY
263  }};
264 
265  for (auto error : errors)
266  {
267  errno = error;
268  REQUIRE_THROWS_AS(UnixSerialPort(
269  "/dev/ttyUSB0", 9600, SerialPort::DEFAULT,
270  mock_unique(mock_sys)), std::system_error);
271  }
272 
273  fakeit::Verify(Method(mock_sys, close).Using(3)).Exactly(2);
274  }
275  SECTION("Emmits errors from 'tcsetattr' system call, and closes the port.")
276  {
277  fakeit::Mock<UnixSyscalls> mock_sys;
278  fakeit::When(Method(mock_sys, open)).AlwaysReturn(3);
279  fakeit::When(Method(mock_sys, close)).AlwaysReturn(0);
280  fakeit::When(Method(mock_sys, tcgetattr)).AlwaysReturn(0);
281  fakeit::When(Method(mock_sys, tcsetattr)).AlwaysReturn(-1);
282  std::array<int, 5> errors{{
283  EBADF,
284  EINTR,
285  EINVAL,
286  ENOTTY,
287  EIO
288  }};
289 
290  for (auto error : errors)
291  {
292  errno = error;
293  REQUIRE_THROWS_AS(UnixSerialPort(
294  "/dev/ttyUSB0", 9600, SerialPort::DEFAULT,
295  mock_unique(mock_sys)), std::system_error);
296  }
297 
298  fakeit::Verify(Method(mock_sys, close).Using(3)).Exactly(5);
299  }
300 }
301 
302 
303 TEST_CASE("UnixSerialPort's open method configures the baud rate.",
304  "[UnixSerialPort]")
305 {
306  // Mock system calls.
307  fakeit::Mock<UnixSyscalls> mock_sys;
308  // Mock 'open'.
309  fakeit::When(Method(mock_sys, open)).AlwaysReturn(3);
310  // Mock 'tcgetattr'.
311  fakeit::When(Method(mock_sys, tcgetattr)).AlwaysDo(
312  [&](auto fd, auto termios_p)
313  {
314  (void)fd;
315  std::memset(termios_p, '\0', sizeof(struct termios));
316  return 0;
317  });
318  // Mock 'tcsetattr'.
319  struct termios tty;
320  fakeit::When(Method(mock_sys, tcsetattr)).AlwaysDo(
321  [&](auto fd, auto action, auto termios_p)
322  {
323  (void)fd;
324  (void)action;
325  std::memcpy(&tty, termios_p, sizeof(struct termios));
326  return 0;
327  });
328  // Mock 'close'.
329  fakeit::When(Method(mock_sys, close)).AlwaysReturn(0);
330  SECTION("0 bps")
331  {
332  UnixSerialPort port(
333  "/dev/ttyUSB0", 0,
335  mock_unique(mock_sys));
336  REQUIRE(cfgetispeed(&tty) == B0);
337  REQUIRE(cfgetospeed(&tty) == B0);
338  }
339  SECTION("50 bps")
340  {
341  UnixSerialPort port(
342  "/dev/ttyUSB0", 50,
344  mock_unique(mock_sys));
345  REQUIRE(cfgetispeed(&tty) == B50);
346  REQUIRE(cfgetospeed(&tty) == B50);
347  }
348  SECTION("75 bps")
349  {
350  UnixSerialPort port(
351  "/dev/ttyUSB0", 75,
353  mock_unique(mock_sys));
354  REQUIRE(cfgetispeed(&tty) == B75);
355  REQUIRE(cfgetospeed(&tty) == B75);
356  }
357  SECTION("110 bps")
358  {
359  UnixSerialPort port(
360  "/dev/ttyUSB0", 110,
362  mock_unique(mock_sys));
363  REQUIRE(cfgetispeed(&tty) == B110);
364  REQUIRE(cfgetospeed(&tty) == B110);
365  }
366  SECTION("134 bps")
367  {
368  UnixSerialPort port(
369  "/dev/ttyUSB0", 134,
371  mock_unique(mock_sys));
372  REQUIRE(cfgetispeed(&tty) == B134);
373  REQUIRE(cfgetospeed(&tty) == B134);
374  }
375  SECTION("135 bps")
376  {
377  UnixSerialPort port(
378  "/dev/ttyUSB0", 135,
380  mock_unique(mock_sys));
381  REQUIRE(cfgetispeed(&tty) == B134);
382  REQUIRE(cfgetospeed(&tty) == B134);
383  }
384  SECTION("150 bps")
385  {
386  UnixSerialPort port(
387  "/dev/ttyUSB0", 150,
389  mock_unique(mock_sys));
390  REQUIRE(cfgetispeed(&tty) == B150);
391  REQUIRE(cfgetospeed(&tty) == B150);
392  }
393  SECTION("200 bps")
394  {
395  UnixSerialPort port(
396  "/dev/ttyUSB0", 200,
398  mock_unique(mock_sys));
399  REQUIRE(cfgetispeed(&tty) == B200);
400  REQUIRE(cfgetospeed(&tty) == B200);
401  }
402  SECTION("300 bps")
403  {
404  UnixSerialPort port(
405  "/dev/ttyUSB0", 300,
407  mock_unique(mock_sys));
408  REQUIRE(cfgetispeed(&tty) == B300);
409  REQUIRE(cfgetospeed(&tty) == B300);
410  }
411  SECTION("600 bps")
412  {
413  UnixSerialPort port(
414  "/dev/ttyUSB0", 600,
416  mock_unique(mock_sys));
417  REQUIRE(cfgetispeed(&tty) == B600);
418  REQUIRE(cfgetospeed(&tty) == B600);
419  }
420  SECTION("1200 bps")
421  {
422  UnixSerialPort port(
423  "/dev/ttyUSB0", 1200,
425  mock_unique(mock_sys));
426  REQUIRE(cfgetispeed(&tty) == B1200);
427  REQUIRE(cfgetospeed(&tty) == B1200);
428  }
429  SECTION("1800 bps")
430  {
431  UnixSerialPort port(
432  "/dev/ttyUSB0", 1800,
434  mock_unique(mock_sys));
435  REQUIRE(cfgetispeed(&tty) == B1800);
436  REQUIRE(cfgetospeed(&tty) == B1800);
437  }
438  SECTION("2400 bps")
439  {
440  UnixSerialPort port(
441  "/dev/ttyUSB0", 2400,
443  mock_unique(mock_sys));
444  REQUIRE(cfgetispeed(&tty) == B2400);
445  REQUIRE(cfgetospeed(&tty) == B2400);
446  }
447  SECTION("4800 bps")
448  {
449  UnixSerialPort port(
450  "/dev/ttyUSB0", 4800,
452  mock_unique(mock_sys));
453  REQUIRE(cfgetispeed(&tty) == B4800);
454  REQUIRE(cfgetospeed(&tty) == B4800);
455  }
456  SECTION("9600 bps")
457  {
458  UnixSerialPort port(
459  "/dev/ttyUSB0", 9600,
461  mock_unique(mock_sys));
462  REQUIRE(cfgetispeed(&tty) == B9600);
463  REQUIRE(cfgetospeed(&tty) == B9600);
464  }
465  SECTION("19200 bps")
466  {
467  UnixSerialPort port(
468  "/dev/ttyUSB0", 19200,
470  mock_unique(mock_sys));
471  REQUIRE(cfgetispeed(&tty) == B19200);
472  REQUIRE(cfgetospeed(&tty) == B19200);
473  }
474  SECTION("38400 bps")
475  {
476  UnixSerialPort port(
477  "/dev/ttyUSB0", 38400,
479  mock_unique(mock_sys));
480  REQUIRE(cfgetispeed(&tty) == B38400);
481  REQUIRE(cfgetospeed(&tty) == B38400);
482  }
483  SECTION("57600 bps")
484  {
485  UnixSerialPort port(
486  "/dev/ttyUSB0", 57600,
488  mock_unique(mock_sys));
489  REQUIRE(cfgetispeed(&tty) == B57600);
490  REQUIRE(cfgetospeed(&tty) == B57600);
491  }
492  SECTION("115200 bps")
493  {
494  UnixSerialPort port(
495  "/dev/ttyUSB0", 115200,
497  mock_unique(mock_sys));
498  REQUIRE(cfgetispeed(&tty) == B115200);
499  REQUIRE(cfgetospeed(&tty) == B115200);
500  }
501  SECTION("230400 bps")
502  {
503  UnixSerialPort port(
504  "/dev/ttyUSB0", 230400,
506  mock_unique(mock_sys));
507  REQUIRE(cfgetispeed(&tty) == B230400);
508  REQUIRE(cfgetospeed(&tty) == B230400);
509  }
510  SECTION("Throws error when given unsupported baud rate.")
511  {
512  REQUIRE_THROWS_AS(
514  "/dev/ttyUSB0", 9601, SerialPort::DEFAULT,
515  mock_unique(mock_sys)), std::invalid_argument);
516  REQUIRE_THROWS_WITH(
518  "/dev/ttyUSB0", 9601, SerialPort::DEFAULT,
519  mock_unique(mock_sys)), "9601 bps is not a valid baud rate.");
520  }
521 }
522 
523 
524 TEST_CASE("UnixSerialPort's 'read' method receives data on the socket.",
525  "[UnixSerialPort]")
526 {
527  // Mock system calls.
528  fakeit::Mock<UnixSyscalls> mock_sys;
529  // Mock 'open'.
530  fakeit::When(Method(mock_sys, open)).AlwaysReturn(3);
531  // Mock 'tcgetattr'.
532  fakeit::When(Method(mock_sys, tcgetattr)).AlwaysReturn(0);
533  // Mock 'tcsetattr'.
534  fakeit::When(Method(mock_sys, tcsetattr)).AlwaysReturn(0);
535  // Mock 'close'.
536  fakeit::When(Method(mock_sys, close)).AlwaysReturn(0);
537  // Construct port.
538  UnixSerialPort port(
539  "/dev/ttyUSB0", 9600,
541  mock_unique(mock_sys));
542  SECTION("Timeout, no data (no errors).")
543  {
544  // Mock 'poll'.
545  struct pollfd fds;
546  fakeit::When(Method(mock_sys, poll)).Do(
547  [&](auto fds_, auto nfds, auto timeout)
548  {
549  (void)timeout;
550  std::memcpy(&fds, fds_, nfds * sizeof(fds));
551  return 0;
552  });
553  // Test.
554  REQUIRE(port.read(250ms) == std::vector<uint8_t>());
555  // Verify 'poll'.
556  fakeit::Verify(Method(mock_sys, poll).Matching(
557  [](auto fds_, auto nfds, auto timeout)
558  {
559  (void)fds_;
560  return nfds == 1 && timeout == 250;
561  })).Once();
562  REQUIRE(fds.fd == 3);
563  REQUIRE(fds.events == POLLIN);
564  REQUIRE(fds.revents == 0);
565  }
566  SECTION("Poll error, close and reopen the serial port (no other errors).")
567  {
568  // Mock 'tcgetattr'.
569  fakeit::When(Method(mock_sys, tcgetattr)).AlwaysDo(
570  [&](auto fd, auto termios_p)
571  {
572  (void)fd;
573  std::memset(termios_p, '\0', sizeof(struct termios));
574  return 0;
575  });
576  // Mock 'tcsetattr'.
577  struct termios tty;
578  fakeit::When(Method(mock_sys, tcsetattr)).AlwaysDo(
579  [&](auto fd, auto action, auto termios_p)
580  {
581  (void)fd;
582  (void)action;
583  std::memcpy(&tty, termios_p, sizeof(struct termios));
584  return 0;
585  });
586  // Mock 'poll'.
587  struct pollfd fds;
588  fakeit::When(Method(mock_sys, poll)).AlwaysDo(
589  [&](auto fds_, auto nfds, auto timeout)
590  {
591  (void)timeout;
592  std::memcpy(&fds, fds_, nfds * sizeof(fds));
593  fds_->revents = POLLERR;
594  return 1;
595  });
596  // Test
597  REQUIRE(port.read(250ms) == std::vector<uint8_t>());
598  // Verify 'open' system call.
599  fakeit::Verify(Method(mock_sys, open).Matching(
600  [](auto path, auto flags)
601  {
602  return std::string(path) == "/dev/ttyUSB0" &&
603  flags == (O_RDWR | O_NOCTTY | O_SYNC);
604  })).Exactly(2);
605  // Verify 'tcgetattr'.
606  fakeit::Verify(Method(mock_sys, tcgetattr).Matching(
607  [&](auto fd, auto termios_p)
608  {
609  (void)termios_p;
610  return fd == 3;
611  })).Exactly(2);
612  // Verify 'tcsetattr'.
613  fakeit::Verify(Method(mock_sys, tcsetattr).Matching(
614  [](auto fd, auto action, auto termios_p)
615  {
616  (void)termios_p;
617  return fd == 3 && action == TCSANOW;
618  })).Exactly(2);
619  REQUIRE(cfgetispeed(&tty) == B9600);
620  REQUIRE(cfgetospeed(&tty) == B9600);
621  REQUIRE((tty.c_cflag & CLOCAL) != 0);
622  REQUIRE((tty.c_cflag & CREAD) != 0);
623  REQUIRE((tty.c_cflag & PARENB) == 0);
624  REQUIRE((tty.c_cflag & CSTOPB) == 0);
625  REQUIRE((tty.c_cflag & CS8) != 0);
626  REQUIRE((tty.c_cflag & CRTSCTS) == 0);
627  REQUIRE((tty.c_lflag & ICANON) == 0);
628  REQUIRE((tty.c_lflag & ECHO) == 0);
629  REQUIRE((tty.c_lflag & ECHOE) == 0);
630  REQUIRE((tty.c_lflag & ISIG) == 0);
631  REQUIRE((tty.c_iflag & IGNBRK) == 0);
632  REQUIRE((tty.c_iflag & BRKINT) == 0);
633  REQUIRE((tty.c_iflag & PARMRK) == 0);
634  REQUIRE((tty.c_iflag & ISTRIP) == 0);
635  REQUIRE((tty.c_iflag & INLCR) == 0);
636  REQUIRE((tty.c_iflag & IGNCR) == 0);
637  REQUIRE((tty.c_iflag & ICRNL) == 0);
638  REQUIRE((tty.c_iflag & IXON) == 0);
639  REQUIRE((tty.c_iflag & IXOFF) == 0);
640  REQUIRE((tty.c_iflag & IXANY) == 0);
641  REQUIRE((tty.c_oflag & OPOST) == 0);
642  REQUIRE(tty.c_cc[VMIN] == 0);
643  REQUIRE(tty.c_cc[VTIME] == 0);
644  // Verify 'close'.
645  fakeit::Verify(Method(mock_sys, close).Using(3)).Exactly(1);
646  }
647  SECTION("Data available (no errors).")
648  {
649  // Mock 'poll'.
650  struct pollfd fds;
651  fakeit::When(Method(mock_sys, poll)).Do(
652  [&](auto fds_, auto nfds, auto timeout)
653  {
654  (void)timeout;
655  std::memcpy(&fds, fds_, nfds * sizeof(fds));
656  fds_->revents = POLLIN;
657  return 1;
658  });
659  // Mock 'read'.
660  fakeit::When(Method(mock_sys, read)).Do(
661  [](auto fd, auto buf, auto count)
662  {
663  (void)fd;
664  // Write to buffer.
665  std::vector<uint8_t> vec = {1, 3, 3, 7};
666  std::memcpy(buf, vec.data(), std::min(vec.size(), count));
667  // Return number of received bytes.
668  return std::min(vec.size(), count);
669  });
670  // Test.
671  REQUIRE(port.read(250ms) == std::vector<uint8_t>({1, 3, 3, 7}));
672  // Verify 'poll'.
673  fakeit::Verify(Method(mock_sys, poll).Matching(
674  [](auto fds_, auto nfds, auto timeout)
675  {
676  (void)fds_;
677  return nfds == 1 && timeout == 250;
678  })).Once();
679  REQUIRE(fds.fd == 3);
680  REQUIRE(fds.events == POLLIN);
681  REQUIRE(fds.revents == 0);
682  // Verify 'read'.
683  fakeit::Verify(Method(mock_sys, read).Matching(
684  [](auto fd, auto buf, auto count)
685  {
686  (void)buf;
687  return fd == 3 && count >= 1024;
688  }));
689  }
690  SECTION("Emmits errors from 'read' system call.")
691  {
692  // Mock 'poll'.
693  struct pollfd fds;
694  fakeit::When(Method(mock_sys, poll)).AlwaysDo(
695  [&](auto fds_, auto nfds, auto timeout)
696  {
697  (void)timeout;
698  std::memcpy(&fds, fds_, nfds * sizeof(fds));
699  fds_->revents = POLLIN;
700  return 1;
701  });
702  // Mock 'read'.
703  fakeit::When(Method(mock_sys, read)).AlwaysReturn(-1);
704  // Test
705  std::array<int, 7> errors{{
706  EAGAIN,
707  EBADF,
708  EFAULT,
709  EINTR,
710  EINVAL,
711  EIO,
712  EISDIR
713  }};
714 
715  for (auto error : errors)
716  {
717  errno = error;
718  REQUIRE_THROWS_AS(port.read(250ms), std::system_error);
719  }
720  }
721  SECTION("Emmits errors from 'poll' system call.")
722  {
723  // Mock 'poll'.
724  fakeit::When(Method(mock_sys, poll)).AlwaysReturn(-1);
725  // Test
726  std::array<int, 4> errors{{
727  EFAULT,
728  EINTR,
729  EINVAL,
730  ENOMEM
731  }};
732 
733  for (auto error : errors)
734  {
735  errno = error;
736  REQUIRE_THROWS_AS(port.read(250ms), std::system_error);
737  }
738  }
739 }
740 
741 
742 TEST_CASE("UnixSerialPort's 'write' method sends data over the serial port.",
743  "[UnixSerialPort]")
744 {
745  // Mock system calls.
746  fakeit::Mock<UnixSyscalls> mock_sys;
747  // Mock 'open'.
748  fakeit::When(Method(mock_sys, open)).AlwaysReturn(3);
749  // Mock 'tcgetattr'.
750  fakeit::When(Method(mock_sys, tcgetattr)).AlwaysReturn(0);
751  // Mock 'tcsetattr'.
752  fakeit::When(Method(mock_sys, tcsetattr)).AlwaysReturn(0);
753  // Mock 'close'.
754  fakeit::When(Method(mock_sys, close)).Return(0);
755  // Construct port.
756  UnixSerialPort port(
757  "/dev/ttyUSB0", 9600,
759  mock_unique(mock_sys));
760  SECTION("Without error.")
761  {
762  // Mock 'write'.
763  std::vector<uint8_t> written;
764  fakeit::When(Method(mock_sys, write)).Do(
765  [&](auto fd, auto buf, auto count)
766  {
767  (void)fd;
768  written.resize(count);
769  std::memcpy(written.data(), buf, count);
770  return count;
771  });
772  // Test
773  std::vector<uint8_t> vec = {1, 3, 3, 7};
774  port.write(vec);
775  // Verify 'write'.
776  fakeit::Verify(Method(mock_sys, write).Matching(
777  [](auto fd, auto buf, auto count)
778  {
779  (void)buf;
780  return fd == 3 && count == 4;
781  })).Once();
782  REQUIRE(written == vec);
783  }
784  SECTION("Could not write all data.")
785  {
786  // Mock 'write'.
787  fakeit::When(Method(mock_sys, write)).AlwaysReturn(3);
788  // Test
789  std::vector<uint8_t> vec = {1, 3, 3, 7};
790  REQUIRE_THROWS_AS(port.write(vec), PartialSendError);
791  REQUIRE_THROWS_WITH(port.write(vec), "Could only write 3 of 4 bytes.");
792  }
793  SECTION("Emits errors from 'write' system call.")
794  {
795  fakeit::When(Method(mock_sys, write)).AlwaysReturn(-1);
796  std::array<int, 11> errors{{
797  EAGAIN,
798  EBADF,
799  EDESTADDRREQ,
800  EDQUOT,
801  EFAULT,
802  EFBIG,
803  EINTR,
804  EINVAL,
805  EIO,
806  ENOSPC,
807  EPIPE
808  }};
809 
810  for (auto error : errors)
811  {
812  errno = error;
813  REQUIRE_THROWS_AS(port.write({1, 3, 3, 7}), std::system_error);
814  }
815  }
816 }
817 
818 
819 TEST_CASE("UnixSerialPort's are printable.", "[UnixSerialPort]")
820 {
821  // Mock system calls.
822  fakeit::Mock<UnixSyscalls> mock_sys;
823  // Mock 'open'.
824  fakeit::When(Method(mock_sys, open)).AlwaysReturn(3);
825  // Mock 'tcgetattr'.
826  fakeit::When(Method(mock_sys, tcgetattr)).AlwaysDo(
827  [&](auto fd, auto termios_p)
828  {
829  (void)fd;
830  std::memset(termios_p, '\0', sizeof(struct termios));
831  return 0;
832  });
833  // Mock 'tcsetattr'.
834  struct termios tty;
835  fakeit::When(Method(mock_sys, tcsetattr)).AlwaysDo(
836  [&](auto fd, auto action, auto termios_p)
837  {
838  (void)fd;
839  (void)action;
840  std::memcpy(&tty, termios_p, sizeof(struct termios));
841  return 0;
842  });
843  // Mock 'close'.
844  fakeit::When(Method(mock_sys, close)).AlwaysReturn(0);
845  SECTION("Without flow control.")
846  {
847  UnixSerialPort port(
848  "/dev/ttyUSB0", 9600,
850  mock_unique(mock_sys));
851  REQUIRE(
852  str(port) ==
853  "serial {\n"
854  " device /dev/ttyUSB0;\n"
855  " baudrate 9600;\n"
856  " flow_control no;\n"
857  "}");
858  }
859  SECTION("With flow control.")
860  {
861  UnixSerialPort port(
862  "/dev/ttyUSB0", 9600,
864  mock_unique(mock_sys));
865  REQUIRE(
866  str(port) ==
867  "serial {\n"
868  " device /dev/ttyUSB0;\n"
869  " baudrate 9600;\n"
870  " flow_control yes;\n"
871  "}");
872  }
873 }
std::string str(const T &object)
Definition: utility.hpp:128
No special features.
Definition: SerialPort.hpp:61
Enable flow control.
Definition: SerialPort.hpp:62
virtual void write(const std::vector< uint8_t > &data) final
TEST_CASE("UnixSerialPort's open and configure a serial port on construction" "and closes the port on destruction.", "[UnixSerialPort]")
virtual std::vector< uint8_t > read(const std::chrono::nanoseconds &timeout=std::chrono::nanoseconds::zero()) final
std::unique_ptr< T > mock_unique(fakeit::Mock< T > &mock)
Definition: common.hpp:46