Hegel 0.11.4
Property-based testing for C++
Loading...
Searching...
No Matches
test_case.h
1#pragma once
2
3#include <functional>
4#include <future>
5#include <memory>
6#include <string_view>
7#include <thread>
8#include <type_traits>
9#include <utility>
10
11namespace hegel::impl::test_case {
12 struct TestCaseData;
13}
14
15namespace hegel::generators {
16 template <typename T> class Generator;
17}
18
19namespace hegel {
20
40 class TestCase {
41 public:
42 TestCase(const TestCase&) = delete;
43 TestCase& operator=(const TestCase&) = delete;
45 TestCase(TestCase&&) noexcept;
50 TestCase& operator=(TestCase&&) noexcept;
51 ~TestCase();
52
66 template <typename T> T draw(const generators::Generator<T>& gen) const;
67
81 template <typename T>
82 T draw(std::string_view name, const generators::Generator<T>& gen,
83 bool repeatable = false) const;
84
97 void assume(bool condition) const;
98
110 [[noreturn]] void reject() const;
111
129 void target(double score, std::string_view label = "") const;
130
150 void repeat(const std::function<void()>& body) const;
151
158 void note(std::string_view message) const;
159
176 TestCase clone() const;
177
199 template <typename F> auto spawn(F fn) const;
200
202 explicit TestCase(std::unique_ptr<impl::test_case::TestCaseData> data);
203
204 // Generators reach through this accessor to talk to the backend.
205 // Not part of the user-facing API.
206 impl::test_case::TestCaseData* data() const { return data_.get(); }
208
209 private:
210 std::unique_ptr<impl::test_case::TestCaseData> data_;
211 };
212
220 template <typename T> class Worker {
221 public:
223 Worker(std::thread thread, std::future<T> future)
224 : thread_(std::move(thread)), future_(std::move(future)) {}
227 Worker(Worker&&) noexcept = default;
228 Worker(const Worker&) = delete;
229 Worker& operator=(const Worker&) = delete;
230 ~Worker() {
231 if (thread_.joinable()) {
232 thread_.join();
233 }
234 }
235
244 T join() {
245 if (thread_.joinable()) {
246 thread_.join();
247 }
248 return future_.get();
249 }
250
251 private:
252 std::thread thread_;
253 std::future<T> future_;
254 };
255
256 template <typename F> auto TestCase::spawn(F fn) const {
257 using R = std::invoke_result_t<F, TestCase&>;
258 std::packaged_task<R()> task(
259 [fn = std::move(fn), cloned_tc = clone()]() mutable {
260 return fn(cloned_tc);
261 });
262 std::future<R> future = task.get_future();
263 return Worker<R>(std::thread(std::move(task)), std::move(future));
264 }
265
266} // namespace hegel
auto spawn(F fn) const
Run fn on a clone of this test case in a new thread.
Definition test_case.h:256
void repeat(const std::function< void()> &body) const
Run body in an engine-managed loop.
void reject() const
Reject the current test case unconditionally.
void assume(bool condition) const
Reject the current test case if condition is false.
void target(double score, std::string_view label="") const
Record a score for the engine's targeted-search phase to maximize.
TestCase clone() const
Creates a clone of this test case with an independent choice sequence forked from this test case.
TestCase(TestCase &&) noexcept
Move-constructs, transferring ownership of the underlying handle.
void note(std::string_view message) const
Record a message that will be printed on the final replay of a failing test case.
T draw(const generators::Generator< T > &gen) const
Draw a random value from a generator.
Definition core.h:264
Handle to a worker started by TestCase::spawn().
Definition test_case.h:220
T join()
Await the worker and return its result.
Definition test_case.h:244
Worker(Worker &&) noexcept=default
Move-constructs, transferring ownership of the running worker.
The base class of all generators.
Definition core.h:67
Hegel generators.
Definition core.h:17
Main namespace.
Definition config.h:29