Hegel 0.13.1
Property-based testing for C++
Loading...
Searching...
No Matches
gtest.h
1#pragma once
2
31
32#include <gtest/gtest-spi.h>
33#include <gtest/gtest.h>
34
35#include "hegel.h"
36
37#include <functional>
38#include <optional>
39#include <stdexcept>
40#include <string>
41
42namespace hegel {
43
54 class GTestFailure : public std::runtime_error, public FailureOrigin {
55 public:
58 GTestFailure(std::string origin, const std::string& message)
59 : std::runtime_error(message), origin_(std::move(origin)) {}
60
62 std::string failure_origin() const override { return origin_; }
63
64 private:
65 std::string origin_;
66 };
67
69 namespace internal {
70 namespace gtest_hooks {
71
72 // "Suite.Name" of the GoogleTest test that runs now.
73 inline std::string current_test_name() {
74 const testing::TestInfo* info =
75 testing::UnitTest::GetInstance()->current_test_info();
76 if (info == nullptr) {
77 return {};
78 }
79 return std::string(info->test_suite_name()) + "." +
80 info->name();
81 }
82
83 // Where one failed assertion is written, or "<unknown>" for an
84 // assertion GoogleTest could not place.
85 inline std::string position(const testing::TestPartResult& part) {
86 if (part.file_name() == nullptr) {
87 return "<unknown>";
88 }
89 return std::string(part.file_name()) + ":" +
90 std::to_string(part.line_number());
91 }
92
93 struct Failures {
94 std::string message;
95 std::string origin;
96 };
97
98 inline void
99 raise_failures(const testing::TestPartResultArray& recorded) {
100 Failures out;
101 for (int i = 0; i < recorded.size(); i++) {
102 const testing::TestPartResult& part =
103 recorded.GetTestPartResult(i);
104 if (!part.failed()) {
105 continue;
106 }
107 if (!out.message.empty()) {
108 out.message += "\n";
109 out.origin += ", ";
110 }
111 out.message += position(part) + ": " + part.message();
112 out.origin += position(part);
113 }
114
115 if (!out.message.empty()) {
116 throw GTestFailure("hegel::GTestFailure at " + out.origin,
117 out.message);
118 }
119 }
120
121 // Thrown when an assertion fails fatally.
122 struct FatalFailure {};
123
124 // Records the assertions of one test case. Without this, ASSERT_*
125 // returns only from the function it is in and the test case
126 // continues.
127 class Reporter : public testing::ScopedFakeTestPartResultReporter {
128 public:
129 explicit Reporter(testing::TestPartResultArray* recorded)
130 : ScopedFakeTestPartResultReporter(
131 INTERCEPT_ONLY_CURRENT_THREAD, recorded) {}
132
133 void ReportTestPartResult(
134 // override makes ASSERT_* instantly throw
135 const testing::TestPartResult& result) override {
136 ScopedFakeTestPartResultReporter::ReportTestPartResult(
137 result);
138 if (result.fatally_failed()) {
139 throw FatalFailure{};
140 }
141 }
142 };
143
144 // Runs one test-case body and collects GoogleTest assertions, then
145 // raises them as one exception. A fatal failure stops the body
146 // immediately.
147 inline void run_case(const std::function<void()>& body) {
148 testing::TestPartResultArray recorded;
149 {
150 // Outside a GoogleTest test, no assertion is recorded.
151 std::optional<Reporter> reporter;
152 if (testing::UnitTest::GetInstance()->current_test_info() !=
153 nullptr) {
154 reporter.emplace(&recorded);
155 }
156 try {
157 body();
158 // NOLINTNEXTLINE(bugprone-empty-catch)
159 } catch (const FatalFailure&) {
160 // The failure is in recorded. Continue to
161 // raise_failures below.
162 }
163 }
164 raise_failures(recorded);
165 }
166
167 } // namespace gtest_hooks
168
169 [[maybe_unused]] static const bool hegel_gtest_hooks_installed =
170 install_framework_hooks(
171 {&gtest_hooks::current_test_name, &gtest_hooks::run_case});
172 } // namespace internal
174
175} // namespace hegel
GTestFailure(std::string origin, const std::string &message)
Definition gtest.h:58
std::string failure_origin() const override
Definition gtest.h:62
Main namespace.
Definition config.h:29