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 |
|
|
#include <string> |
26 |
|
|
|
27 |
|
|
#include "crocoddyl/core/utils/exception.hpp" |
28 |
|
|
#include "random_generator.hpp" |
29 |
|
|
|
30 |
|
|
namespace crocoddyl { |
31 |
|
|
namespace unittest { |
32 |
|
|
|
33 |
|
|
class CaptureIOStream { |
34 |
|
|
public: |
35 |
|
16 |
CaptureIOStream() |
36 |
|
16 |
: m_oldStdOut(0), m_oldStdErr(0), m_capturing(false), m_init(false) { |
37 |
|
16 |
m_pipe[READ] = 0; |
38 |
|
16 |
m_pipe[WRITE] = 0; |
39 |
✗✓ |
16 |
if (pipe(m_pipe) == -1) { |
40 |
|
|
throw_pretty("Cannot create pipe."); |
41 |
|
|
} |
42 |
|
16 |
m_oldStdOut = dup(fileno(stdout)); |
43 |
|
16 |
m_oldStdErr = dup(fileno(stderr)); |
44 |
✓✗✗✓
|
16 |
if (m_oldStdOut == -1 || m_oldStdErr == -1) { |
45 |
|
|
throw_pretty("Cannot redirect stdout or stderr."); |
46 |
|
|
} |
47 |
|
|
|
48 |
|
16 |
m_init = true; |
49 |
|
16 |
} |
50 |
|
|
|
51 |
|
16 |
~CaptureIOStream() { |
52 |
✓✗ |
16 |
if (m_capturing) { |
53 |
|
16 |
endCapture(); |
54 |
|
|
} |
55 |
✓✗ |
16 |
if (m_oldStdOut > 0) close(m_oldStdOut); |
56 |
✓✗ |
16 |
if (m_oldStdErr > 0) close(m_oldStdErr); |
57 |
✓✗ |
16 |
if (m_pipe[READ] > 0) close(m_pipe[READ]); |
58 |
✓✗ |
16 |
if (m_pipe[WRITE] > 0) close(m_pipe[WRITE]); |
59 |
|
16 |
} |
60 |
|
|
|
61 |
|
24 |
void beginCapture() { |
62 |
✗✓ |
24 |
if (!m_init) return; |
63 |
✓✓ |
24 |
if (m_capturing) endCapture(); |
64 |
|
24 |
fflush(stdout); |
65 |
|
24 |
fflush(stderr); |
66 |
|
24 |
dup2(m_pipe[WRITE], fileno(stdout)); |
67 |
|
24 |
dup2(m_pipe[WRITE], fileno(stderr)); |
68 |
|
24 |
m_capturing = true; |
69 |
|
|
} |
70 |
|
|
|
71 |
|
48 |
bool endCapture() { |
72 |
✓✗ |
48 |
usleep(2000); |
73 |
✓✗✗✓
|
48 |
if (!m_init || !m_capturing) { |
74 |
|
|
return false; |
75 |
|
|
} |
76 |
✓✗ |
48 |
fflush(stdout); |
77 |
✓✗ |
48 |
fflush(stderr); |
78 |
|
48 |
dup2(m_oldStdOut, fileno(stdout)); |
79 |
|
48 |
dup2(m_oldStdErr, fileno(stderr)); |
80 |
✓✗ |
48 |
m_captured.clear(); |
81 |
|
|
|
82 |
|
|
// read the pipe |
83 |
|
48 |
ssize_t nb_read = 0; |
84 |
|
|
// Set timeout to 0.2 seconds |
85 |
|
|
struct timeval timeout; |
86 |
|
48 |
timeout.tv_sec = 0; |
87 |
|
48 |
timeout.tv_usec = 200000; |
88 |
|
|
// Initialize file descriptor sets |
89 |
|
|
fd_set read_fds, write_fds, except_fds; |
90 |
|
48 |
FD_ZERO(&read_fds); |
91 |
|
48 |
FD_ZERO(&write_fds); |
92 |
|
48 |
FD_ZERO(&except_fds); |
93 |
|
48 |
FD_SET(m_pipe[READ], &read_fds); |
94 |
|
|
// |
95 |
|
48 |
bool timed_out = false; |
96 |
|
|
|
97 |
✓✓ |
2112 |
while (!timed_out) { |
98 |
✓✗ |
2064 |
if (select(m_pipe[READ] + 1, &read_fds, &write_fds, &except_fds, |
99 |
✓✓ |
2064 |
&timeout) == 1) { |
100 |
|
|
// do the reading |
101 |
|
|
char buff[1]; |
102 |
✓✗ |
2016 |
nb_read = read(m_pipe[READ], buff, sizeof(buff)); |
103 |
✓✗ |
2016 |
if (nb_read > 0) { |
104 |
✓✗ |
2016 |
m_captured << *buff; |
105 |
|
|
} |
106 |
|
2016 |
timed_out = false; |
107 |
|
|
} else { |
108 |
|
|
// timeout or error |
109 |
|
48 |
timed_out = true; |
110 |
|
|
} |
111 |
|
|
} |
112 |
|
48 |
return true; |
113 |
|
|
} |
114 |
|
|
|
115 |
|
24 |
std::string str() const { return m_captured.str(); } |
116 |
|
|
|
117 |
|
|
private: |
118 |
|
|
enum PIPES { READ, WRITE }; |
119 |
|
|
int m_pipe[2]; |
120 |
|
|
int m_oldStdOut; |
121 |
|
|
int m_oldStdErr; |
122 |
|
|
bool m_capturing; |
123 |
|
|
bool m_init; |
124 |
|
|
std::ostringstream m_captured; |
125 |
|
|
}; |
126 |
|
|
|
127 |
|
|
std::string GetErrorMessages(boost::function<int(void)> function_with_errors) { |
128 |
|
|
CaptureIOStream capture_ios; |
129 |
|
|
capture_ios.beginCapture(); |
130 |
|
|
boost::execution_monitor monitor; |
131 |
|
|
try { |
132 |
|
|
monitor.execute(function_with_errors); |
133 |
|
|
} catch (...) { |
134 |
|
|
} |
135 |
|
|
capture_ios.endCapture(); |
136 |
|
|
return capture_ios.str(); |
137 |
|
|
} |
138 |
|
|
|
139 |
|
|
} // namespace unittest |
140 |
|
|
} // namespace crocoddyl |
141 |
|
|
|
142 |
|
|
#endif // CROCODDYL_UNITTEST_COMMON_HPP_ |