| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /////////////////////////////////////////////////////////////////////////////// | ||
| 2 | // BSD 3-Clause License | ||
| 3 | // | ||
| 4 | // Copyright (C) 2019-2023, LAAS-CNRS, University of Edinburgh | ||
| 5 | // Copyright note valid unless otherwise stated in individual files. | ||
| 6 | // All rights reserved. | ||
| 7 | /////////////////////////////////////////////////////////////////////////////// | ||
| 8 | |||
| 9 | /** | ||
| 10 | * To be included last in the test_XXX.cpp, | ||
| 11 | * otherwise it interferes with pinocchio boost::variant. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #ifndef CROCODDYL_UNITTEST_COMMON_HPP_ | ||
| 15 | #define CROCODDYL_UNITTEST_COMMON_HPP_ | ||
| 16 | |||
| 17 | #include <fcntl.h> | ||
| 18 | #include <stdio.h> | ||
| 19 | #include <unistd.h> | ||
| 20 | |||
| 21 | #include <boost/function.hpp> | ||
| 22 | #include <boost/test/execution_monitor.hpp> // for execution_exception | ||
| 23 | #include <boost/test/included/unit_test.hpp> | ||
| 24 | #include <iterator> | ||
| 25 | |||
| 26 | #include "crocoddyl/core/utils/exception.hpp" | ||
| 27 | #include "random_generator.hpp" | ||
| 28 | |||
| 29 | namespace crocoddyl { | ||
| 30 | namespace unittest { | ||
| 31 | |||
| 32 | class CaptureIOStream { | ||
| 33 | public: | ||
| 34 | ✗ | CaptureIOStream() | |
| 35 | ✗ | : m_oldStdOut(0), m_oldStdErr(0), m_capturing(false), m_init(false) { | |
| 36 | ✗ | m_pipe[READ] = 0; | |
| 37 | ✗ | m_pipe[WRITE] = 0; | |
| 38 | ✗ | if (pipe(m_pipe) == -1) { | |
| 39 | ✗ | throw_pretty("Cannot create pipe."); | |
| 40 | } | ||
| 41 | ✗ | m_oldStdOut = dup(fileno(stdout)); | |
| 42 | ✗ | m_oldStdErr = dup(fileno(stderr)); | |
| 43 | ✗ | if (m_oldStdOut == -1 || m_oldStdErr == -1) { | |
| 44 | ✗ | throw_pretty("Cannot redirect stdout or stderr."); | |
| 45 | } | ||
| 46 | |||
| 47 | ✗ | m_init = true; | |
| 48 | ✗ | } | |
| 49 | |||
| 50 | ✗ | ~CaptureIOStream() { | |
| 51 | ✗ | if (m_capturing) { | |
| 52 | ✗ | endCapture(); | |
| 53 | } | ||
| 54 | ✗ | if (m_oldStdOut > 0) close(m_oldStdOut); | |
| 55 | ✗ | if (m_oldStdErr > 0) close(m_oldStdErr); | |
| 56 | ✗ | if (m_pipe[READ] > 0) close(m_pipe[READ]); | |
| 57 | ✗ | if (m_pipe[WRITE] > 0) close(m_pipe[WRITE]); | |
| 58 | ✗ | } | |
| 59 | |||
| 60 | ✗ | void beginCapture() { | |
| 61 | ✗ | if (!m_init) return; | |
| 62 | ✗ | if (m_capturing) endCapture(); | |
| 63 | ✗ | fflush(stdout); | |
| 64 | ✗ | fflush(stderr); | |
| 65 | ✗ | dup2(m_pipe[WRITE], fileno(stdout)); | |
| 66 | ✗ | dup2(m_pipe[WRITE], fileno(stderr)); | |
| 67 | ✗ | m_capturing = true; | |
| 68 | } | ||
| 69 | |||
| 70 | ✗ | bool endCapture() { | |
| 71 | ✗ | usleep(2000); | |
| 72 | ✗ | if (!m_init || !m_capturing) { | |
| 73 | ✗ | return false; | |
| 74 | } | ||
| 75 | ✗ | fflush(stdout); | |
| 76 | ✗ | fflush(stderr); | |
| 77 | ✗ | dup2(m_oldStdOut, fileno(stdout)); | |
| 78 | ✗ | dup2(m_oldStdErr, fileno(stderr)); | |
| 79 | ✗ | m_captured.clear(); | |
| 80 | |||
| 81 | // read the pipe | ||
| 82 | ✗ | ssize_t nb_read = 0; | |
| 83 | // Set timeout to 0.2 seconds | ||
| 84 | struct timeval timeout; | ||
| 85 | ✗ | timeout.tv_sec = 0; | |
| 86 | ✗ | timeout.tv_usec = 200000; | |
| 87 | // Initialize file descriptor sets | ||
| 88 | fd_set read_fds, write_fds, except_fds; | ||
| 89 | ✗ | FD_ZERO(&read_fds); | |
| 90 | ✗ | FD_ZERO(&write_fds); | |
| 91 | ✗ | FD_ZERO(&except_fds); | |
| 92 | ✗ | FD_SET(m_pipe[READ], &read_fds); | |
| 93 | // | ||
| 94 | ✗ | bool timed_out = false; | |
| 95 | |||
| 96 | ✗ | while (!timed_out) { | |
| 97 | ✗ | if (select(m_pipe[READ] + 1, &read_fds, &write_fds, &except_fds, | |
| 98 | ✗ | &timeout) == 1) { | |
| 99 | // do the reading | ||
| 100 | char buff[1]; | ||
| 101 | ✗ | nb_read = read(m_pipe[READ], buff, sizeof(buff)); | |
| 102 | ✗ | if (nb_read > 0) { | |
| 103 | ✗ | m_captured << *buff; | |
| 104 | } | ||
| 105 | ✗ | timed_out = false; | |
| 106 | } else { | ||
| 107 | // timeout or error | ||
| 108 | ✗ | timed_out = true; | |
| 109 | } | ||
| 110 | } | ||
| 111 | ✗ | return true; | |
| 112 | } | ||
| 113 | |||
| 114 | ✗ | std::string str() const { return m_captured.str(); } | |
| 115 | |||
| 116 | private: | ||
| 117 | enum PIPES { READ, WRITE }; | ||
| 118 | int m_pipe[2]; | ||
| 119 | int m_oldStdOut; | ||
| 120 | int m_oldStdErr; | ||
| 121 | bool m_capturing; | ||
| 122 | bool m_init; | ||
| 123 | std::ostringstream m_captured; | ||
| 124 | }; | ||
| 125 | |||
| 126 | ✗ | std::string GetErrorMessages(boost::function<int(void)> function_with_errors) { | |
| 127 | ✗ | CaptureIOStream capture_ios; | |
| 128 | ✗ | capture_ios.beginCapture(); | |
| 129 | ✗ | boost::execution_monitor monitor; | |
| 130 | try { | ||
| 131 | ✗ | monitor.execute(function_with_errors); | |
| 132 | ✗ | } catch (...) { | |
| 133 | ✗ | } | |
| 134 | ✗ | capture_ios.endCapture(); | |
| 135 | ✗ | return capture_ios.str(); | |
| 136 | ✗ | } | |
| 137 | |||
| 138 | } // namespace unittest | ||
| 139 | } // namespace crocoddyl | ||
| 140 | |||
| 141 | #endif // CROCODDYL_UNITTEST_COMMON_HPP_ | ||
| 142 |