Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
RELEASE_TYPE: patch

This patch bumps our pinned `libhegel` ([hegel-rust](hegeldev/hegel-rust)) from [0.35.0](https://github.com/hegeldev/hegel-rust/releases/tag/v0.35.0) to [0.37.5](https://github.com/hegeldev/hegel-rust/releases/tag/v0.37.5).
2 changes: 1 addition & 1 deletion cmake/libhegel.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

# libhegel release the bundled C header matches. Keep in sync with
# libhegel/hegel.h.
set(HEGEL_LIBHEGEL_VERSION "0.35.0"
set(HEGEL_LIBHEGEL_VERSION "0.37.5"
CACHE STRING "libhegel (hegeltest) release version to download")
set(HEGEL_LIBHEGEL_BASE_URL
"https://github.com/hegeldev/hegel-rust/releases/download/v${HEGEL_LIBHEGEL_VERSION}")
Expand Down
30 changes: 21 additions & 9 deletions libhegel/hegel.h
Original file line number Diff line number Diff line change
Expand Up @@ -793,13 +793,13 @@ typedef struct {

/*
A drawn time of day: `hour` in `[0, 23]`, `minute` and `second` in
`[0, 59]`, `microsecond` in `[0, 999999]`.
`[0, 59]`, `nanosecond` in `[0, 999999999]`.
*/
typedef struct {
uint8_t hour;
uint8_t minute;
uint8_t second;
uint32_t microsecond;
uint32_t nanosecond;
} hegel_time_t;

/*
Expand Down Expand Up @@ -865,6 +865,12 @@ const char* hegel_context_last_error(const hegel_context_t* ctx);
When a CI environment is detected (via `CI`, `GITHUB_ACTIONS`, and
similar variables) the defaults change: the database is disabled and
derandomization is enabled. Override either with the explicit setters.

When running inside Antithesis (detected via `ANTITHESIS_OUTPUT_DIR`)
the database is disabled and every health check is skipped. The database
can still be enabled with `hegel_settings_set_database`; the health
checks cannot be re-enabled, since Antithesis's thread pausing would trip
wall-clock checks such as `TooSlow` spuriously.
*/
hegel_result_t hegel_settings_new(hegel_context_t* ctx,
hegel_settings_t** out_settings);
Expand Down Expand Up @@ -1420,7 +1426,11 @@ hegel_result_t hegel_pool_free(hegel_context_t* ctx, hegel_pool_t* pool);
testing, sequential or concurrent: `num_rules` rules — each assigned to
a concurrency group by `rule_groups`, an array of group ids parallel to
`rule_names` — and `num_invariants` invariants, with names as
NUL-terminated UTF-8, plus concurrency bounds. Group ids are arbitrary
NUL-terminated UTF-8, plus concurrency bounds. `invariant_always_check`
is an array of `num_invariants` flags parallel to `invariant_names`
(NULL for all-false): `hegel_state_machine_should_check_invariant`
answers true unconditionally for a flagged invariant and samples the
rest. Group ids are arbitrary
(any value except `HEGEL_STATE_MACHINE_DONE`, which
`hegel_state_machine_next_group` reserves as its termination sentinel):
the machine has one concurrency group per distinct value of
Expand Down Expand Up @@ -1490,8 +1500,8 @@ hegel_result_t hegel_pool_free(hegel_context_t* ctx, hegel_pool_t* pool);
hegel_result_t hegel_new_state_machine(
hegel_context_t* ctx, hegel_test_case_t* tc, const char* const* rule_names,
const int64_t* rule_groups, size_t num_rules,
const char* const* invariant_names, size_t num_invariants,
int64_t min_concurrency, int64_t max_concurrency,
const char* const* invariant_names, const bool* invariant_always_check,
size_t num_invariants, int64_t min_concurrency, int64_t max_concurrency,
hegel_state_machine_t** out_state_machine, int64_t* out_concurrency);

/*
Expand Down Expand Up @@ -1589,10 +1599,12 @@ hegel_state_machine_rule_rejected(hegel_context_t* ctx, hegel_test_case_t* tc,

/*
Decide whether the caller should run invariant `invariant_index` at the
current join point, writing the decision into `*out_should_check`: a
current join point, writing the decision into `*out_should_check`: true
unconditionally (consuming no entropy) for an invariant whose
`invariant_always_check` flag was set at creation, otherwise a
recorded boolean draw that is true with probability
`1 / stateful_step_count`, so each invariant's expected number of
sampled runs over a full-length test case is one, regardless of the
`1 / stateful_step_count`, so each sampled invariant's expected number
of sampled runs over a full-length test case is one, regardless of the
step count. The caller owns the machine's guaranteed invariant checks —
its initial state, and its final state once
`hegel_state_machine_next_group` signals termination — and should run
Expand Down Expand Up @@ -1856,7 +1868,7 @@ hegel_result_t hegel_generate_date(hegel_context_t* ctx, hegel_test_case_t* tc,
/*
Parameters:
`min_value` / `max_value`: Inclusive bounds. Pass all-zeros and
`{23, 59, 59, 999999}` for the full day.
`{23, 59, 59, 999999999}` for the full day.

Returns `HEGEL_OK` or `HEGEL_E_STOP_TEST`.

Expand Down
8 changes: 4 additions & 4 deletions nix/flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@
# Prebuilt libhegel (Hegel's native engine) release. Keep the version and
# hashes in sync with cmake/libhegel.cmake and libhegel/hegel.h. Hashes
# are the SHA-256 sidecars published next to each release asset.
libhegelVersion = "0.35.0";
libhegelVersion = "0.37.5";
libhegelAssets = {
"x86_64-linux" = {
asset = "libhegel-linux-amd64.so";
sha256 = "3d2113a606896286e2d64c1fa383ae0c1c8a285cea8852ea87794305c9c932dd";
sha256 = "4ea6c9609e8d91b97ed41ae316b5772ded424f4a12f98c51bd4246818d6ec58e";
};
"aarch64-linux" = {
asset = "libhegel-linux-arm64.so";
sha256 = "33d2b276e7aa3b9c2e0beef384970ccc32b41bef9018b94ae582577f0ea28ecb";
sha256 = "f1ab270f45ed502d1fd89729c6cc8d8ac43b8b6bdbea1e706bf3c81824debf16";
};
"aarch64-darwin" = {
asset = "libhegel-darwin-arm64.dylib";
sha256 = "4a037fc978c46b170e8407a28b3300c1411fbe06755a47a5a507bd48dd9925d9";
sha256 = "297178eaadfba5b15ab5ee7db72f78a65b214c5b791bcb68aa839b2f68e2caa2";
};
};

Expand Down
9 changes: 6 additions & 3 deletions src/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ namespace hegel::impl {
hegel_time_t value{};
scope.raise_for_rc(
hegel_generate_time(scope.ctx, scope.tc, hegel_time_t{0, 0, 0, 0},
hegel_time_t{23, 59, 59, 999999}, &value),
hegel_time_t{23, 59, 59, 999999999}, &value),
"hegel_generate_time");
return value;
}
Expand All @@ -425,7 +425,7 @@ namespace hegel::impl {
DrawScope scope(tc);
hegel_datetime_t value{};
hegel_datetime_t min_value{{1, 1, 1}, {0, 0, 0, 0}};
hegel_datetime_t max_value{{9999, 12, 31}, {23, 59, 59, 999999}};
hegel_datetime_t max_value{{9999, 12, 31}, {23, 59, 59, 999999999}};
scope.raise_for_rc(hegel_generate_datetime(scope.ctx, scope.tc,
min_value, max_value,
&value),
Expand Down Expand Up @@ -700,10 +700,13 @@ namespace hegel::internal {
std::vector<char*> invariant_name_cstrings =
to_cstrings(invariant_names);

// A NULL `invariant_always_check` array leaves every invariant
// sampled at its default probability; the public API exposes no
// always-check flag.
scope.raise_for_rc(hegel_new_state_machine(
scope.ctx, scope.tc, rule_name_cstrings.data(),
rule_groups.data(), rule_names.size(),
invariant_name_cstrings.data(),
invariant_name_cstrings.data(), nullptr,
invariant_names.size(), min_concurrency,
max_concurrency, &handle_, &concurrency_),
"hegel_new_state_machine");
Expand Down
10 changes: 6 additions & 4 deletions src/generators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,15 @@ namespace hegel::generators {
}

/// ISO 8601 `HH:MM:SS[.ffffff]` — the fractional part is present
/// iff the microsecond is nonzero, matching `isoformat()`.
/// iff the microsecond is nonzero, matching `isoformat()`. The
/// engine draws nanosecond precision; the serialization keeps
/// microseconds.
std::string format_time(const hegel_time_t& t) {
char buf[24];
if (t.microsecond != 0) {
unsigned microsecond = static_cast<unsigned>(t.nanosecond / 1000);
if (microsecond != 0) {
std::snprintf(buf, sizeof(buf), "%02u:%02u:%02u.%06u", t.hour,
t.minute, t.second,
static_cast<unsigned>(t.microsecond));
t.minute, t.second, microsecond);
} else {
std::snprintf(buf, sizeof(buf), "%02u:%02u:%02u", t.hour,
t.minute, t.second);
Expand Down
7 changes: 5 additions & 2 deletions tests/test_stateful.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,11 @@ TEST(Pools, PoolsNoConsume) {
TEST(Pools, DrawFromEmptyPool) {
hegel::test([](hegel::TestCase& tc) {
hegel::stateful::Pool<int> pool = hegel::stateful::Pool<int>(tc);
tc.draw(hegel::stateful::values_reusable(pool));
// should not error just a test case rejection
// A draw from an empty reusable pool rejects the case. The boolean
// gate lets other cases complete, so the run stays satisfiable.
if (tc.draw(gs::booleans())) {
tc.draw(hegel::stateful::values_reusable(pool));
}
});
}

Expand Down
Loading