Directory: | ./ |
---|---|
File: | unittest/unittest_common.hpp |
Date: | 2025-01-16 08:47:40 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 52 | 63 | 82.5% |
Branches: | 31 | 80 | 38.8% |
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 | #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 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
|
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 |
2/4✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
|
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 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
16 | if (m_capturing) { |
53 | 16 | endCapture(); | |
54 | } | ||
55 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
16 | if (m_oldStdOut > 0) close(m_oldStdOut); |
56 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
16 | if (m_oldStdErr > 0) close(m_oldStdErr); |
57 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
16 | if (m_pipe[READ] > 0) close(m_pipe[READ]); |
58 |
1/2✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
|
16 | if (m_pipe[WRITE] > 0) close(m_pipe[WRITE]); |
59 | 16 | } | |
60 | |||
61 | 24 | void beginCapture() { | |
62 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | if (!m_init) return; |
63 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 16 times.
|
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 |
1/2✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
|
48 | usleep(2000); |
73 |
2/4✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 48 times.
|
48 | if (!m_init || !m_capturing) { |
74 | ✗ | return false; | |
75 | } | ||
76 |
1/2✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
|
48 | fflush(stdout); |
77 |
1/2✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
|
48 | fflush(stderr); |
78 | 48 | dup2(m_oldStdOut, fileno(stdout)); | |
79 | 48 | dup2(m_oldStdErr, fileno(stderr)); | |
80 |
1/2✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
|
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 |
2/2✓ Branch 0 taken 768 times.
✓ Branch 1 taken 48 times.
|
816 | FD_ZERO(&read_fds); |
91 |
2/2✓ Branch 0 taken 768 times.
✓ Branch 1 taken 48 times.
|
816 | FD_ZERO(&write_fds); |
92 |
2/2✓ Branch 0 taken 768 times.
✓ Branch 1 taken 48 times.
|
816 | FD_ZERO(&except_fds); |
93 | 48 | FD_SET(m_pipe[READ], &read_fds); | |
94 | // | ||
95 | 48 | bool timed_out = false; | |
96 | |||
97 |
2/2✓ Branch 0 taken 2064 times.
✓ Branch 1 taken 48 times.
|
2112 | while (!timed_out) { |
98 |
1/2✓ Branch 1 taken 2064 times.
✗ Branch 2 not taken.
|
2064 | if (select(m_pipe[READ] + 1, &read_fds, &write_fds, &except_fds, |
99 |
2/2✓ Branch 0 taken 2016 times.
✓ Branch 1 taken 48 times.
|
2064 | &timeout) == 1) { |
100 | // do the reading | ||
101 | char buff[1]; | ||
102 |
1/2✓ Branch 1 taken 2016 times.
✗ Branch 2 not taken.
|
2016 | nb_read = read(m_pipe[READ], buff, sizeof(buff)); |
103 |
1/2✓ Branch 0 taken 2016 times.
✗ Branch 1 not taken.
|
2016 | if (nb_read > 0) { |
104 |
1/2✓ Branch 1 taken 2016 times.
✗ Branch 2 not taken.
|
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_ | ||
143 |