Skip to content

Commit db2bdfe

Browse files
authored
CXX-1110 add CHECK_THROWS_WITH_CODE (#1300)
1 parent 30c55af commit db2bdfe

File tree

2 files changed

+257
-1
lines changed

2 files changed

+257
-1
lines changed

src/bsoncxx/test/catch.cpp

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,231 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#include <bsoncxx/test/catch.hh>
16+
17+
//
18+
19+
#include <system_error>
20+
1521
#include <bsoncxx/config/prelude.hpp>
1622

1723
#include <catch2/catch_session.hpp>
1824

1925
int BSONCXX_ABI_CDECL main(int argc, char* argv[]) {
2026
return Catch::Session().run(argc, argv);
2127
}
28+
29+
TEST_CASE("THROWS_WITH_CODE", "[bsoncxx][test]") {
30+
SECTION("basic") {
31+
CHECK_THROWS_WITH_CODE(
32+
throw std::system_error(std::make_error_code(std::errc::invalid_argument)),
33+
std::errc::invalid_argument);
34+
}
35+
36+
// TEST_CHECK is evaluated when a `std::system_error` exception is thrown as expected and is
37+
// used to evaluate the error code comparison check.
38+
int checked = 0;
39+
40+
// TEST_CHECK_THROWS_AS is evaluated when an unexpected exception type is thrown and is used to
41+
// trigger Catch test failure (always fails).
42+
int checked_throws_as = 0;
43+
44+
SECTION("Catch::TestFailureException") {
45+
#define TEST_CHECK(expr) \
46+
if (1) { \
47+
++checked; \
48+
FAIL("Catch::TestFailureException did not throw"); \
49+
} else \
50+
((void)0)
51+
52+
#define TEST_CHECK_THROWS_AS(expr, type) \
53+
if (1) { \
54+
++checked_throws_as; \
55+
CHECK_THROWS_AS(expr, Catch::TestFailureException); \
56+
} else \
57+
((void)0)
58+
59+
try {
60+
THROWS_WITH_CODE_IMPL(
61+
TEST_CHECK, throw Catch::TestFailureException(), std::errc::invalid_argument);
62+
} catch (const Catch::TestFailureException&) {
63+
SUCCEED("Catch::TestFailureException was propagated");
64+
} catch (...) {
65+
FAIL("unexpected exception was thrown");
66+
}
67+
68+
#undef TEST_CHECK
69+
#undef TEST_CHECK_THROWS_AS
70+
71+
CHECK(checked == 0);
72+
CHECK(checked_throws_as == 0);
73+
}
74+
75+
SECTION("Catch::TestSkipException") {
76+
#define TEST_CHECK(expr) \
77+
if (1) { \
78+
++checked; \
79+
FAIL("Catch::TestSkipException did not throw"); \
80+
} else \
81+
((void)0)
82+
83+
#define TEST_CHECK_THROWS_AS(expr, type) \
84+
if (1) { \
85+
++checked_throws_as; \
86+
CHECK_THROWS_AS(expr, Catch::TestSkipException); \
87+
} else \
88+
((void)0)
89+
90+
try {
91+
THROWS_WITH_CODE_IMPL(
92+
TEST_CHECK, throw Catch::TestSkipException(), std::errc::invalid_argument);
93+
} catch (const Catch::TestSkipException&) {
94+
SUCCEED("Catch::TestSkipException was propagated");
95+
} catch (...) {
96+
FAIL("unexpected exception was thrown");
97+
}
98+
99+
#undef TEST_CHECK
100+
#undef TEST_CHECK_THROWS_AS
101+
102+
CHECK(checked == 0);
103+
CHECK(checked_throws_as == 0);
104+
}
105+
106+
SECTION("unrelated") {
107+
struct unrelated {};
108+
109+
#define TEST_CHECK(expr) \
110+
if (1) { \
111+
++checked; \
112+
FAIL("unrelated exception did not throw"); \
113+
} else \
114+
((void)0)
115+
116+
#define TEST_CHECK_THROWS_AS(expr, type) \
117+
if (1) { \
118+
++checked_throws_as; \
119+
CHECK_THROWS_AS(expr, unrelated); \
120+
} else \
121+
((void)0)
122+
123+
THROWS_WITH_CODE_IMPL(TEST_CHECK, throw unrelated(), std::errc::invalid_argument);
124+
125+
#undef TEST_CHECK
126+
#undef TEST_CHECK_THROWS_AS
127+
128+
CHECK(checked == 0);
129+
CHECK(checked_throws_as == 1);
130+
}
131+
132+
SECTION("std::system_error") {
133+
#define TEST_CHECK(expr) \
134+
if (1) { \
135+
++checked; \
136+
CHECK(expr); \
137+
} else \
138+
((void)0)
139+
140+
#define TEST_CHECK_THROWS_AS(expr, type) \
141+
if (1) { \
142+
++checked_throws_as; \
143+
CHECK_THROWS_AS(expr, std::system_error); \
144+
} else \
145+
((void)0)
146+
147+
THROWS_WITH_CODE_IMPL(
148+
TEST_CHECK,
149+
throw std::system_error(std::make_error_code(std::errc::invalid_argument)),
150+
std::make_error_code(std::errc::invalid_argument));
151+
152+
#undef TEST_CHECK
153+
#undef TEST_CHECK_THROWS_AS
154+
155+
CHECK(checked == 1);
156+
CHECK(checked_throws_as == 0);
157+
}
158+
159+
SECTION("derived") {
160+
struct derived : std::system_error {
161+
using std::system_error::system_error;
162+
};
163+
164+
#define TEST_CHECK(expr) \
165+
if (1) { \
166+
++checked; \
167+
CHECK(expr); \
168+
} else \
169+
((void)0)
170+
171+
#define TEST_CHECK_THROWS_AS(expr, type) \
172+
if (1) { \
173+
++checked_throws_as; \
174+
CHECK_THROWS_AS(expr, std::system_error); \
175+
} else \
176+
((void)0)
177+
178+
THROWS_WITH_CODE_IMPL(TEST_CHECK,
179+
throw derived(std::make_error_code(std::errc::invalid_argument)),
180+
std::errc::invalid_argument);
181+
182+
#undef TEST_CHECK
183+
#undef TEST_CHECK_THROWS_AS
184+
185+
CHECK(checked == 1);
186+
CHECK(checked_throws_as == 0);
187+
}
188+
189+
SECTION("error code") {
190+
#define TEST_CHECK(expr) \
191+
if (1) { \
192+
++checked; \
193+
CHECK_FALSE(expr); /* invalid_argument != not_supported */ \
194+
} else \
195+
((void)0)
196+
197+
#define TEST_CHECK_THROWS_AS(expr, type) \
198+
if (1) { \
199+
++checked_throws_as; \
200+
CHECK_THROWS_AS(expr, std::system_error); \
201+
} else \
202+
((void)0)
203+
204+
THROWS_WITH_CODE_IMPL(
205+
TEST_CHECK,
206+
throw std::system_error(std::make_error_code(std::errc::invalid_argument)),
207+
std::errc::not_supported);
208+
209+
#undef TEST_CHECK
210+
#undef TEST_CHECK_THROWS_AS
211+
212+
CHECK(checked == 1);
213+
CHECK(checked_throws_as == 0);
214+
}
215+
216+
SECTION("error condition") {
217+
#define TEST_CHECK(expr) \
218+
if (1) { \
219+
++checked; \
220+
CHECK(expr); \
221+
} else \
222+
((void)0)
223+
224+
#define TEST_CHECK_THROWS_AS(expr, type) \
225+
if (1) { \
226+
++checked_throws_as; \
227+
CHECK_THROWS_AS(expr, std::system_error); \
228+
} else \
229+
((void)0)
230+
231+
THROWS_WITH_CODE_IMPL(
232+
TEST_CHECK,
233+
throw std::system_error(std::make_error_code(std::errc::invalid_argument)),
234+
std::make_error_condition(std::errc::invalid_argument));
235+
236+
#undef TEST_CHECK
237+
#undef TEST_CHECK_THROWS_AS
238+
239+
CHECK(checked == 1);
240+
CHECK(checked_throws_as == 0);
241+
}
242+
}

src/bsoncxx/test/catch.hh

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,44 @@
2727
#include <catch2/catch_test_macros.hpp> // TEST_CASE, SECTION, CHECK, etc.
2828
#include <catch2/catch_tostring.hpp> // Catch::StringMaker
2929

30+
#define THROWS_WITH_CODE_IMPL(_assertion, _expr, _code) \
31+
if (1) { \
32+
try { \
33+
(void)(_expr); \
34+
INFO("expected an exception to be thrown: " #_expr); \
35+
_assertion(false); \
36+
} catch (const Catch::TestFailureException&) { \
37+
throw; /* Propagate Catch exceptions. */ \
38+
} catch (const Catch::TestSkipException&) { \
39+
throw; /* Propagate Catch exceptions. */ \
40+
} catch (const std::system_error& ex) { \
41+
using std::make_error_code; \
42+
(void)ex; /* Avoid unused variable warnings. */ \
43+
_assertion(ex.code() == (_code)); \
44+
} catch (...) { \
45+
/* Reuse `*_THROWS_AS` to handle the unexpected exception type. */ \
46+
BSONCXX_CONCAT(_assertion, _THROWS_AS)(throw, std::system_error); \
47+
} \
48+
} else \
49+
((void)0)
50+
51+
#define CHECK_THROWS_WITH_CODE(_expr, _code) THROWS_WITH_CODE_IMPL(CHECK, _expr, _code)
52+
#define REQUIRE_THROWS_WITH_CODE(_expr, _code) THROWS_WITH_CODE_IMPL(REQUIRE, _expr, _code)
53+
3054
namespace Catch {
3155

32-
// Catch2 must be able to stringify documents, optionals, etc. if they're used in Catch2 macros.
56+
template <>
57+
struct StringMaker<std::error_condition> {
58+
static std::string convert(const std::error_condition& value) {
59+
std::string res;
60+
61+
res += value.category().name();
62+
res += ':';
63+
res += Catch::StringMaker<int>::convert(value.value());
64+
65+
return res;
66+
}
67+
};
3368

3469
template <>
3570
struct StringMaker<bsoncxx::oid> {

0 commit comments

Comments
 (0)