]>
Commit | Line | Data |
---|---|---|
f51cf556 | 1 | // Copyright Antony Polukhin, 2016-2023. |
b32b8144 FG |
2 | // |
3 | // Distributed under the Boost Software License, Version 1.0. (See | |
4 | // accompanying file LICENSE_1_0.txt or copy at | |
5 | // http://www.boost.org/LICENSE_1_0.txt) | |
6 | ||
7 | #include <boost/stacktrace/stacktrace_fwd.hpp> | |
8 | ||
9 | #include <boost/stacktrace.hpp> | |
10 | #include <stdexcept> | |
11 | #include <iostream> | |
12 | #include <sstream> | |
92f5a8d4 TL |
13 | #include <cctype> |
14 | ||
b32b8144 FG |
15 | #include <boost/core/lightweight_test.hpp> |
16 | ||
17 | #include <boost/functional/hash.hpp> | |
18 | ||
92f5a8d4 | 19 | #include "test_impl.hpp" |
b32b8144 FG |
20 | |
21 | using boost::stacktrace::stacktrace; | |
22 | using boost::stacktrace::frame; | |
23 | ||
b32b8144 FG |
24 | |
25 | #if (defined(BOOST_GCC) && defined(BOOST_WINDOWS) && !defined(BOOST_STACKTRACE_USE_BACKTRACE) && !defined(BOOST_STACKTRACE_USE_ADDR2LINE)) \ | |
26 | || defined(BOOST_STACKTRACE_TEST_NO_DEBUG_AT_ALL) | |
27 | ||
92f5a8d4 | 28 | # define BOOST_STACKTRACE_TEST_SHOULD_OUTPUT_READABLE_NAMES 0 |
b32b8144 | 29 | #else |
92f5a8d4 | 30 | # define BOOST_STACKTRACE_TEST_SHOULD_OUTPUT_READABLE_NAMES 1 |
b32b8144 FG |
31 | #endif |
32 | ||
b32b8144 FG |
33 | void test_deeply_nested_namespaces() { |
34 | std::stringstream ss; | |
35 | ss << return_from_nested_namespaces(); | |
36 | std::cout << ss.str() << '\n'; | |
92f5a8d4 | 37 | #if BOOST_STACKTRACE_TEST_SHOULD_OUTPUT_READABLE_NAMES |
b32b8144 FG |
38 | BOOST_TEST(ss.str().find("main") != std::string::npos); |
39 | ||
40 | BOOST_TEST(ss.str().find("get_backtrace_from_nested_namespaces") != std::string::npos | |
41 | || ss.str().find("1# return_from_nested_namespaces") != std::string::npos); // GCC with -O1 has strange inlining, so this line is true while the prev one is false. | |
42 | ||
43 | BOOST_TEST(ss.str().find("return_from_nested_namespaces") != std::string::npos); | |
44 | #endif | |
45 | ||
46 | stacktrace ns1 = return_from_nested_namespaces(); | |
47 | BOOST_TEST(ns1 != return_from_nested_namespaces()); // Different addresses in test_deeply_nested_namespaces() function | |
48 | } | |
49 | ||
92f5a8d4 TL |
50 | std::size_t count_unprintable_chars(const std::string& s) { |
51 | std::size_t result = 0; | |
52 | for (std::size_t i = 0; i < s.size(); ++i) { | |
53 | result += (std::isprint(s[i]) ? 0 : 1); | |
54 | } | |
55 | ||
56 | return result; | |
57 | } | |
58 | ||
59 | void test_frames_string_data_validity() { | |
60 | stacktrace trace = return_from_nested_namespaces(); | |
61 | for (std::size_t i = 0; i < trace.size(); ++i) { | |
62 | BOOST_TEST_EQ(count_unprintable_chars(trace[i].source_file()), 0); | |
63 | BOOST_TEST_EQ(count_unprintable_chars(trace[i].name()), 0); | |
64 | } | |
65 | ||
66 | BOOST_TEST(to_string(trace).find('\0') == std::string::npos); | |
67 | } | |
68 | ||
b32b8144 FG |
69 | // Template parameter Depth is to produce different functions on each Depth. This simplifies debugging when one of the tests catches error |
70 | template <std::size_t Depth> | |
92f5a8d4 TL |
71 | void test_nested(bool print = true) { |
72 | std::pair<stacktrace, stacktrace> res = function_from_library(Depth, function_from_main_translation_unit); | |
b32b8144 FG |
73 | |
74 | std::stringstream ss1, ss2; | |
75 | ||
76 | ss1 << res.first; | |
77 | ss2 << res.second; | |
92f5a8d4 TL |
78 | if (print) { |
79 | std::cout << "'" << ss1.str() << "'\n\n" << ss2.str() << std::endl; | |
80 | } | |
b32b8144 FG |
81 | BOOST_TEST(!ss1.str().empty()); |
82 | BOOST_TEST(!ss2.str().empty()); | |
83 | ||
84 | BOOST_TEST(ss1.str().find(" 0# ") != std::string::npos); | |
85 | BOOST_TEST(ss2.str().find(" 0# ") != std::string::npos); | |
86 | ||
87 | BOOST_TEST(ss1.str().find(" 1# ") != std::string::npos); | |
88 | BOOST_TEST(ss2.str().find(" 1# ") != std::string::npos); | |
89 | ||
90 | BOOST_TEST(ss1.str().find(" in ") != std::string::npos); | |
91 | BOOST_TEST(ss2.str().find(" in ") != std::string::npos); | |
92 | ||
92f5a8d4 | 93 | #if BOOST_STACKTRACE_TEST_SHOULD_OUTPUT_READABLE_NAMES |
b32b8144 FG |
94 | BOOST_TEST(ss1.str().find("main") != std::string::npos); |
95 | BOOST_TEST(ss2.str().find("main") != std::string::npos); | |
96 | ||
92f5a8d4 TL |
97 | BOOST_TEST(ss1.str().find("function_from_library") != std::string::npos); |
98 | BOOST_TEST(ss2.str().find("function_from_library") != std::string::npos); | |
b32b8144 | 99 | |
92f5a8d4 TL |
100 | BOOST_TEST(ss1.str().find("function_from_main_translation_unit") != std::string::npos); |
101 | BOOST_TEST(ss2.str().find("function_from_main_translation_unit") != std::string::npos); | |
b32b8144 | 102 | #endif |
b32b8144 FG |
103 | } |
104 | ||
105 | template <class Bt> | |
106 | void test_comparisons_base(Bt nst, Bt st) { | |
107 | Bt cst(st); | |
108 | st = st; | |
109 | cst = cst; | |
110 | BOOST_TEST(nst); | |
111 | BOOST_TEST(st); | |
112 | #if !defined(BOOST_MSVC) && !defined(BOOST_STACKTRACE_USE_WINDBG) | |
113 | // This is very dependent on compiler and link flags. No sane way to make it work, because | |
114 | // BOOST_NOINLINE could be ignored by MSVC compiler if link-time optimization is enabled. | |
115 | BOOST_TEST(nst[0] != st[0]); | |
116 | #endif | |
117 | ||
118 | BOOST_TEST(nst != st); | |
119 | BOOST_TEST(st != nst); | |
120 | BOOST_TEST(st == st); | |
121 | BOOST_TEST(nst == nst); | |
122 | ||
123 | BOOST_TEST(nst != cst); | |
124 | BOOST_TEST(cst != nst); | |
125 | BOOST_TEST(cst == st); | |
126 | BOOST_TEST(cst == cst); | |
127 | ||
128 | BOOST_TEST(nst < st || nst > st); | |
129 | BOOST_TEST(st < nst || nst < st); | |
130 | BOOST_TEST(st <= st); | |
131 | BOOST_TEST(nst <= nst); | |
132 | BOOST_TEST(st >= st); | |
133 | BOOST_TEST(nst >= nst); | |
134 | ||
135 | BOOST_TEST(nst < cst || cst < nst); | |
136 | BOOST_TEST(nst > cst || cst > nst); | |
137 | ||
138 | ||
139 | BOOST_TEST(hash_value(nst) == hash_value(nst)); | |
140 | BOOST_TEST(hash_value(cst) == hash_value(st)); | |
141 | ||
142 | BOOST_TEST(hash_value(nst) != hash_value(cst)); | |
143 | BOOST_TEST(hash_value(st) != hash_value(nst)); | |
144 | } | |
145 | ||
146 | void test_comparisons() { | |
147 | stacktrace nst = return_from_nested_namespaces(); | |
148 | stacktrace st; | |
149 | test_comparisons_base(nst, st); | |
150 | } | |
151 | ||
152 | void test_iterators() { | |
153 | stacktrace st; | |
154 | ||
155 | BOOST_TEST(st.begin() == st.begin()); | |
156 | BOOST_TEST(st.cbegin() == st.cbegin()); | |
157 | BOOST_TEST(st.crbegin() == st.crbegin()); | |
158 | BOOST_TEST(st.rbegin() == st.rbegin()); | |
159 | ||
160 | BOOST_TEST(st.begin() + 1 == st.begin() + 1); | |
161 | BOOST_TEST(st.cbegin() + 1 == st.cbegin() + 1); | |
162 | BOOST_TEST(st.crbegin() + 1 == st.crbegin() + 1); | |
163 | BOOST_TEST(st.rbegin() + 1 == st.rbegin() + 1); | |
164 | ||
165 | BOOST_TEST(st.end() == st.end()); | |
166 | BOOST_TEST(st.cend() == st.cend()); | |
167 | BOOST_TEST(st.crend() == st.crend()); | |
168 | BOOST_TEST(st.rend() == st.rend()); | |
169 | ||
170 | BOOST_TEST(st.end() > st.begin()); | |
171 | BOOST_TEST(st.end() > st.cbegin()); | |
172 | BOOST_TEST(st.cend() > st.cbegin()); | |
173 | BOOST_TEST(st.cend() > st.begin()); | |
174 | ||
175 | BOOST_TEST(st.size() == static_cast<std::size_t>(st.end() - st.begin())); | |
176 | BOOST_TEST(st.size() == static_cast<std::size_t>(st.end() - st.cbegin())); | |
177 | BOOST_TEST(st.size() == static_cast<std::size_t>(st.cend() - st.cbegin())); | |
178 | BOOST_TEST(st.size() == static_cast<std::size_t>(st.cend() - st.begin())); | |
179 | ||
180 | BOOST_TEST(st.size() == static_cast<std::size_t>(std::distance(st.rbegin(), st.rend()))); | |
181 | BOOST_TEST(st.size() == static_cast<std::size_t>(std::distance(st.crbegin(), st.rend()))); | |
182 | BOOST_TEST(st.size() == static_cast<std::size_t>(std::distance(st.crbegin(), st.crend()))); | |
183 | BOOST_TEST(st.size() == static_cast<std::size_t>(std::distance(st.rbegin(), st.crend()))); | |
184 | ||
185 | ||
186 | boost::stacktrace::stacktrace::iterator it = st.begin(); | |
187 | ++ it; | |
188 | BOOST_TEST(it == st.begin() + 1); | |
189 | } | |
190 | ||
191 | void test_frame() { | |
192 | stacktrace nst = return_from_nested_namespaces(); | |
92f5a8d4 | 193 | stacktrace st = make_some_stacktrace1(); |
b32b8144 FG |
194 | |
195 | const std::size_t min_size = (nst.size() < st.size() ? nst.size() : st.size()); | |
196 | BOOST_TEST(min_size > 2); | |
197 | ||
198 | for (std::size_t i = 0; i < min_size; ++i) { | |
199 | BOOST_TEST(st[i] == st[i]); | |
200 | BOOST_TEST(st[i].source_file() == st[i].source_file()); | |
201 | BOOST_TEST(st[i].source_line() == st[i].source_line()); | |
202 | BOOST_TEST(st[i] <= st[i]); | |
203 | BOOST_TEST(st[i] >= st[i]); | |
204 | ||
205 | frame fv = nst[i]; | |
206 | BOOST_TEST(fv); | |
207 | if (i > 1 && i < min_size - 3) { // Begin ...and end of the trace may match, skipping | |
208 | BOOST_TEST(st[i] != fv); | |
209 | ||
210 | #if !(defined(BOOST_STACKTRACE_TEST_NO_DEBUG_AT_ALL) && defined(BOOST_MSVC)) | |
211 | // MSVC can not get function name withhout debug symbols even if it is exported | |
212 | BOOST_TEST(st[i].name() != fv.name()); | |
213 | BOOST_TEST(st[i] != fv); | |
214 | BOOST_TEST(st[i] < fv || st[i] > fv); | |
215 | BOOST_TEST(hash_value(st[i]) != hash_value(fv)); | |
216 | #endif | |
217 | ||
218 | if (st[i].source_line()) { | |
219 | BOOST_TEST(st[i].source_file() != fv.source_file() || st[i].source_line() != fv.source_line()); | |
220 | } | |
221 | BOOST_TEST(st[i]); | |
222 | } | |
223 | ||
224 | fv = st[i]; | |
225 | BOOST_TEST(hash_value(st[i]) == hash_value(fv)); | |
226 | } | |
227 | ||
228 | boost::stacktrace::frame empty_frame; | |
229 | BOOST_TEST(!empty_frame); | |
92f5a8d4 TL |
230 | BOOST_TEST_EQ(empty_frame.source_file(), ""); |
231 | BOOST_TEST_EQ(empty_frame.name(), ""); | |
232 | BOOST_TEST_EQ(empty_frame.source_line(), 0); | |
b32b8144 FG |
233 | } |
234 | ||
235 | // Template parameter bool BySkip is to produce different functions on each BySkip. This simplifies debugging when one of the tests catches error | |
236 | template <bool BySkip> | |
237 | void test_empty_basic_stacktrace() { | |
238 | typedef boost::stacktrace::stacktrace st_t; | |
239 | st_t st = BySkip ? st_t(100500, 1024) : st_t(0, 0); | |
240 | ||
241 | BOOST_TEST(!st); | |
242 | BOOST_TEST(st.empty()); | |
243 | BOOST_TEST(st.size() == 0); | |
244 | BOOST_TEST(st.begin() == st.end()); | |
245 | BOOST_TEST(st.cbegin() == st.end()); | |
246 | BOOST_TEST(st.cbegin() == st.cend()); | |
247 | BOOST_TEST(st.begin() == st.cend()); | |
248 | ||
249 | BOOST_TEST(st.rbegin() == st.rend()); | |
250 | BOOST_TEST(st.crbegin() == st.rend()); | |
251 | BOOST_TEST(st.crbegin() == st.crend()); | |
252 | BOOST_TEST(st.rbegin() == st.crend()); | |
253 | ||
254 | BOOST_TEST(hash_value(st) == hash_value(st_t(0, 0))); | |
255 | BOOST_TEST(st == st_t(0, 0)); | |
256 | BOOST_TEST(!(st < st_t(0, 0))); | |
257 | BOOST_TEST(!(st > st_t(0, 0))); | |
258 | } | |
259 | ||
260 | int main() { | |
261 | test_deeply_nested_namespaces(); | |
92f5a8d4 | 262 | test_frames_string_data_validity(); |
b32b8144 FG |
263 | test_nested<15>(); |
264 | test_comparisons(); | |
265 | test_iterators(); | |
266 | test_frame(); | |
267 | test_empty_basic_stacktrace<true>(); | |
268 | test_empty_basic_stacktrace<false>(); | |
269 | ||
92f5a8d4 TL |
270 | BOOST_TEST(&make_some_stacktrace1 != &make_some_stacktrace2); |
271 | boost::stacktrace::stacktrace b1 = make_some_stacktrace1(); | |
b32b8144 | 272 | BOOST_TEST(b1.size() == 4); |
92f5a8d4 | 273 | boost::stacktrace::stacktrace b2 = make_some_stacktrace2(); |
b32b8144 | 274 | BOOST_TEST(b2.size() == 4); |
92f5a8d4 | 275 | test_comparisons_base(make_some_stacktrace1(), make_some_stacktrace2()); |
b32b8144 | 276 | |
92f5a8d4 | 277 | test_nested<260>(false); |
b32b8144 FG |
278 | BOOST_TEST(boost::stacktrace::stacktrace(0, 1).size() == 1); |
279 | BOOST_TEST(boost::stacktrace::stacktrace(1, 1).size() == 1); | |
280 | ||
281 | return boost::report_errors(); | |
282 | } |