23 #include <system_error> 40 TEST_CASE(
"UnixSerialPort's open and configure a serial port on construction" 41 "and closes the port on destruction.",
"[UnixSerialPort]")
43 SECTION(
"Without hardware flow control (no errors).")
46 fakeit::Mock<UnixSyscalls> mock_sys;
48 fakeit::When(Method(mock_sys, open)).AlwaysReturn(3);
50 fakeit::When(Method(mock_sys, tcgetattr)).AlwaysDo(
51 [&](
auto fd,
auto termios_p)
54 std::memset(termios_p,
'\0',
sizeof(
struct termios));
59 fakeit::When(Method(mock_sys, tcsetattr)).AlwaysDo(
60 [&](
auto fd,
auto action,
auto termios_p)
64 std::memcpy(&tty, termios_p,
sizeof(
struct termios));
68 fakeit::When(Method(mock_sys, close)).AlwaysReturn(0);
76 fakeit::Verify(Method(mock_sys, open).Matching(
77 [](
auto path,
auto flags)
79 return std::string(path) ==
"/dev/ttyUSB0" &&
80 flags == (O_RDWR | O_NOCTTY | O_SYNC);
83 fakeit::Verify(Method(mock_sys, tcgetattr).Matching(
84 [&](
auto fd,
auto termios_p)
90 fakeit::Verify(Method(mock_sys, tcsetattr).Matching(
91 [](
auto fd,
auto action,
auto termios_p)
94 return fd == 3 && action == TCSANOW;
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);
122 fakeit::Verify(Method(mock_sys, close).Using(3)).Exactly(0);
125 fakeit::Verify(Method(mock_sys, close).Using(3)).Once();
127 SECTION(
"With hardware flow control (no errors).")
130 fakeit::Mock<UnixSyscalls> mock_sys;
132 fakeit::When(Method(mock_sys, open)).Return(3);
134 fakeit::When(Method(mock_sys, tcgetattr)).Do(
135 [&](
auto fd,
auto termios_p)
138 std::memset(termios_p,
'\0',
sizeof(
struct termios));
143 fakeit::When(Method(mock_sys, tcsetattr)).Do(
144 [&](
auto fd,
auto action,
auto termios_p)
148 std::memcpy(&tty, termios_p,
sizeof(
struct termios));
152 fakeit::When(Method(mock_sys, close)).Return(0);
156 "/dev/ttyUSB0", 9600,
160 fakeit::Verify(Method(mock_sys, open).Matching(
161 [](
auto path,
auto flags)
163 return std::string(path) ==
"/dev/ttyUSB0" &&
164 flags == (O_RDWR | O_NOCTTY | O_SYNC);
167 fakeit::Verify(Method(mock_sys, tcgetattr).Matching(
168 [&](
auto fd,
auto termios_p)
174 fakeit::Verify(Method(mock_sys, tcsetattr).Matching(
175 [](
auto fd,
auto action,
auto termios_p)
178 return fd == 3 && action == TCSANOW;
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);
206 fakeit::Verify(Method(mock_sys, close).Using(3)).Exactly(0);
209 fakeit::Verify(Method(mock_sys, close).Using(3)).Once();
211 SECTION(
"Emmits errors from 'open' system call.")
213 fakeit::Mock<UnixSyscalls> mock_sys;
214 fakeit::When(Method(mock_sys, open)).AlwaysReturn(-1);
215 std::array<int, 26> errors{{
244 for (
auto error : errors)
252 fakeit::Verify(Method(mock_sys, close).Using(3)).Exactly(0);
254 SECTION(
"Emmits errors from 'tcgetattr' system call, and closes the port.")
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{{
265 for (
auto error : errors)
273 fakeit::Verify(Method(mock_sys, close).Using(3)).Exactly(2);
275 SECTION(
"Emmits errors from 'tcsetattr' system call, and closes the port.")
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{{
290 for (
auto error : errors)
298 fakeit::Verify(Method(mock_sys, close).Using(3)).Exactly(5);
303 TEST_CASE(
"UnixSerialPort's open method configures the baud rate.",
307 fakeit::Mock<UnixSyscalls> mock_sys;
309 fakeit::When(Method(mock_sys, open)).AlwaysReturn(3);
311 fakeit::When(Method(mock_sys, tcgetattr)).AlwaysDo(
312 [&](
auto fd,
auto termios_p)
315 std::memset(termios_p,
'\0',
sizeof(
struct termios));
320 fakeit::When(Method(mock_sys, tcsetattr)).AlwaysDo(
321 [&](
auto fd,
auto action,
auto termios_p)
325 std::memcpy(&tty, termios_p,
sizeof(
struct termios));
329 fakeit::When(Method(mock_sys, close)).AlwaysReturn(0);
336 REQUIRE(cfgetispeed(&tty) == B0);
337 REQUIRE(cfgetospeed(&tty) == B0);
345 REQUIRE(cfgetispeed(&tty) == B50);
346 REQUIRE(cfgetospeed(&tty) == B50);
354 REQUIRE(cfgetispeed(&tty) == B75);
355 REQUIRE(cfgetospeed(&tty) == B75);
363 REQUIRE(cfgetispeed(&tty) == B110);
364 REQUIRE(cfgetospeed(&tty) == B110);
372 REQUIRE(cfgetispeed(&tty) == B134);
373 REQUIRE(cfgetospeed(&tty) == B134);
381 REQUIRE(cfgetispeed(&tty) == B134);
382 REQUIRE(cfgetospeed(&tty) == B134);
390 REQUIRE(cfgetispeed(&tty) == B150);
391 REQUIRE(cfgetospeed(&tty) == B150);
399 REQUIRE(cfgetispeed(&tty) == B200);
400 REQUIRE(cfgetospeed(&tty) == B200);
408 REQUIRE(cfgetispeed(&tty) == B300);
409 REQUIRE(cfgetospeed(&tty) == B300);
417 REQUIRE(cfgetispeed(&tty) == B600);
418 REQUIRE(cfgetospeed(&tty) == B600);
423 "/dev/ttyUSB0", 1200,
426 REQUIRE(cfgetispeed(&tty) == B1200);
427 REQUIRE(cfgetospeed(&tty) == B1200);
432 "/dev/ttyUSB0", 1800,
435 REQUIRE(cfgetispeed(&tty) == B1800);
436 REQUIRE(cfgetospeed(&tty) == B1800);
441 "/dev/ttyUSB0", 2400,
444 REQUIRE(cfgetispeed(&tty) == B2400);
445 REQUIRE(cfgetospeed(&tty) == B2400);
450 "/dev/ttyUSB0", 4800,
453 REQUIRE(cfgetispeed(&tty) == B4800);
454 REQUIRE(cfgetospeed(&tty) == B4800);
459 "/dev/ttyUSB0", 9600,
462 REQUIRE(cfgetispeed(&tty) == B9600);
463 REQUIRE(cfgetospeed(&tty) == B9600);
468 "/dev/ttyUSB0", 19200,
471 REQUIRE(cfgetispeed(&tty) == B19200);
472 REQUIRE(cfgetospeed(&tty) == B19200);
477 "/dev/ttyUSB0", 38400,
480 REQUIRE(cfgetispeed(&tty) == B38400);
481 REQUIRE(cfgetospeed(&tty) == B38400);
486 "/dev/ttyUSB0", 57600,
489 REQUIRE(cfgetispeed(&tty) == B57600);
490 REQUIRE(cfgetospeed(&tty) == B57600);
492 SECTION(
"115200 bps")
495 "/dev/ttyUSB0", 115200,
498 REQUIRE(cfgetispeed(&tty) == B115200);
499 REQUIRE(cfgetospeed(&tty) == B115200);
501 SECTION(
"230400 bps")
504 "/dev/ttyUSB0", 230400,
507 REQUIRE(cfgetispeed(&tty) == B230400);
508 REQUIRE(cfgetospeed(&tty) == B230400);
510 SECTION(
"Throws error when given unsupported baud rate.")
519 mock_unique(mock_sys)),
"9601 bps is not a valid baud rate.");
524 TEST_CASE(
"UnixSerialPort's 'read' method receives data on the socket.",
528 fakeit::Mock<UnixSyscalls> mock_sys;
530 fakeit::When(Method(mock_sys, open)).AlwaysReturn(3);
532 fakeit::When(Method(mock_sys, tcgetattr)).AlwaysReturn(0);
534 fakeit::When(Method(mock_sys, tcsetattr)).AlwaysReturn(0);
536 fakeit::When(Method(mock_sys, close)).AlwaysReturn(0);
539 "/dev/ttyUSB0", 9600,
542 SECTION(
"Timeout, no data (no errors).")
546 fakeit::When(Method(mock_sys, poll)).Do(
547 [&](
auto fds_,
auto nfds,
auto timeout)
550 std::memcpy(&fds, fds_, nfds *
sizeof(fds));
554 REQUIRE(port.
read(250ms) == std::vector<uint8_t>());
556 fakeit::Verify(Method(mock_sys, poll).Matching(
557 [](
auto fds_,
auto nfds,
auto timeout)
560 return nfds == 1 && timeout == 250;
562 REQUIRE(fds.fd == 3);
563 REQUIRE(fds.events == POLLIN);
564 REQUIRE(fds.revents == 0);
566 SECTION(
"Poll error, close and reopen the serial port (no other errors).")
569 fakeit::When(Method(mock_sys, tcgetattr)).AlwaysDo(
570 [&](
auto fd,
auto termios_p)
573 std::memset(termios_p,
'\0',
sizeof(
struct termios));
578 fakeit::When(Method(mock_sys, tcsetattr)).AlwaysDo(
579 [&](
auto fd,
auto action,
auto termios_p)
583 std::memcpy(&tty, termios_p,
sizeof(
struct termios));
588 fakeit::When(Method(mock_sys, poll)).AlwaysDo(
589 [&](
auto fds_,
auto nfds,
auto timeout)
592 std::memcpy(&fds, fds_, nfds *
sizeof(fds));
593 fds_->revents = POLLERR;
597 REQUIRE(port.
read(250ms) == std::vector<uint8_t>());
599 fakeit::Verify(Method(mock_sys, open).Matching(
600 [](
auto path,
auto flags)
602 return std::string(path) ==
"/dev/ttyUSB0" &&
603 flags == (O_RDWR | O_NOCTTY | O_SYNC);
606 fakeit::Verify(Method(mock_sys, tcgetattr).Matching(
607 [&](
auto fd,
auto termios_p)
613 fakeit::Verify(Method(mock_sys, tcsetattr).Matching(
614 [](
auto fd,
auto action,
auto termios_p)
617 return fd == 3 && action == TCSANOW;
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);
645 fakeit::Verify(Method(mock_sys, close).Using(3)).Exactly(1);
647 SECTION(
"Data available (no errors).")
651 fakeit::When(Method(mock_sys, poll)).Do(
652 [&](
auto fds_,
auto nfds,
auto timeout)
655 std::memcpy(&fds, fds_, nfds *
sizeof(fds));
656 fds_->revents = POLLIN;
660 fakeit::When(Method(mock_sys, read)).Do(
661 [](
auto fd,
auto buf,
auto count)
665 std::vector<uint8_t> vec = {1, 3, 3, 7};
666 std::memcpy(buf, vec.data(), std::min(vec.size(), count));
668 return std::min(vec.size(), count);
671 REQUIRE(port.
read(250ms) == std::vector<uint8_t>({1, 3, 3, 7}));
673 fakeit::Verify(Method(mock_sys, poll).Matching(
674 [](
auto fds_,
auto nfds,
auto timeout)
677 return nfds == 1 && timeout == 250;
679 REQUIRE(fds.fd == 3);
680 REQUIRE(fds.events == POLLIN);
681 REQUIRE(fds.revents == 0);
683 fakeit::Verify(Method(mock_sys, read).Matching(
684 [](
auto fd,
auto buf,
auto count)
687 return fd == 3 && count >= 1024;
690 SECTION(
"Emmits errors from 'read' system call.")
694 fakeit::When(Method(mock_sys, poll)).AlwaysDo(
695 [&](
auto fds_,
auto nfds,
auto timeout)
698 std::memcpy(&fds, fds_, nfds *
sizeof(fds));
699 fds_->revents = POLLIN;
703 fakeit::When(Method(mock_sys, read)).AlwaysReturn(-1);
705 std::array<int, 7> errors{{
715 for (
auto error : errors)
718 REQUIRE_THROWS_AS(port.
read(250ms), std::system_error);
721 SECTION(
"Emmits errors from 'poll' system call.")
724 fakeit::When(Method(mock_sys, poll)).AlwaysReturn(-1);
726 std::array<int, 4> errors{{
733 for (
auto error : errors)
736 REQUIRE_THROWS_AS(port.
read(250ms), std::system_error);
742 TEST_CASE(
"UnixSerialPort's 'write' method sends data over the serial port.",
746 fakeit::Mock<UnixSyscalls> mock_sys;
748 fakeit::When(Method(mock_sys, open)).AlwaysReturn(3);
750 fakeit::When(Method(mock_sys, tcgetattr)).AlwaysReturn(0);
752 fakeit::When(Method(mock_sys, tcsetattr)).AlwaysReturn(0);
754 fakeit::When(Method(mock_sys, close)).Return(0);
757 "/dev/ttyUSB0", 9600,
760 SECTION(
"Without error.")
763 std::vector<uint8_t> written;
764 fakeit::When(Method(mock_sys, write)).Do(
765 [&](
auto fd,
auto buf,
auto count)
768 written.resize(count);
769 std::memcpy(written.data(), buf, count);
773 std::vector<uint8_t> vec = {1, 3, 3, 7};
776 fakeit::Verify(Method(mock_sys, write).Matching(
777 [](
auto fd,
auto buf,
auto count)
780 return fd == 3 && count == 4;
782 REQUIRE(written == vec);
784 SECTION(
"Could not write all data.")
787 fakeit::When(Method(mock_sys, write)).AlwaysReturn(3);
789 std::vector<uint8_t> vec = {1, 3, 3, 7};
791 REQUIRE_THROWS_WITH(port.
write(vec),
"Could only write 3 of 4 bytes.");
793 SECTION(
"Emits errors from 'write' system call.")
795 fakeit::When(Method(mock_sys, write)).AlwaysReturn(-1);
796 std::array<int, 11> errors{{
810 for (
auto error : errors)
813 REQUIRE_THROWS_AS(port.
write({1, 3, 3, 7}), std::system_error);
819 TEST_CASE(
"UnixSerialPort's are printable.",
"[UnixSerialPort]")
822 fakeit::Mock<UnixSyscalls> mock_sys;
824 fakeit::When(Method(mock_sys, open)).AlwaysReturn(3);
826 fakeit::When(Method(mock_sys, tcgetattr)).AlwaysDo(
827 [&](
auto fd,
auto termios_p)
830 std::memset(termios_p,
'\0',
sizeof(
struct termios));
835 fakeit::When(Method(mock_sys, tcsetattr)).AlwaysDo(
836 [&](
auto fd,
auto action,
auto termios_p)
840 std::memcpy(&tty, termios_p,
sizeof(
struct termios));
844 fakeit::When(Method(mock_sys, close)).AlwaysReturn(0);
845 SECTION(
"Without flow control.")
848 "/dev/ttyUSB0", 9600,
854 " device /dev/ttyUSB0;\n" 856 " flow_control no;\n" 859 SECTION(
"With flow control.")
862 "/dev/ttyUSB0", 9600,
868 " device /dev/ttyUSB0;\n" 870 " flow_control yes;\n" std::string str(const T &object)
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)