Class: Hegel::LibHegel::Real
- Inherits:
-
Object
- Object
- Hegel::LibHegel::Real
- Defined in:
- lib/hegel/lib_hegel/real.rb,
sig/hegel.rbs
Overview
Drives libhegel's C ABI through the ffi gem. Every other file works against the plain Ruby values and method calls this class exposes, so a future change to how the native call happens has exactly one file to change.
Opens the library and binds every function once, in #initialize, each as its own FFI::Function held in an instance variable (see #initialize's own comment for why this binds one callable per function rather than attach_function's usual class-body DSL). Each call below reuses that already-bound function rather than re-resolving the symbol.
Instance Method Summary collapse
-
#collection_free(ctx, collection) ⇒ nil
No-op when
collectionis nil, matching hegel_collection_free's documented no-op-on-NULL contract; not translated, for the same reason as #context_free. -
#collection_more(ctx, tc, collection) ⇒ Boolean
Returns whether libhegel wants another element; call in a loop, drawing the next element each time this is true, until it is false.
-
#collection_reject(ctx, tc, collection, why = nil) ⇒ nil
Tells libhegel the last element
collectionproduced is invalid. -
#context_free(ctx) ⇒ nil
No-op when
ctxis nil: libhegel documents hegel_context_free as a no-op on NULL, and a Ruby nil marshals to a NULL pointer for a :pointer argument here, so no separate nil check is needed on this side. -
#context_last_error(ctx) ⇒ String
Copies the message out of libhegel's own buffer into a Ruby String before returning, since the header documents that buffer as borrowed and invalidated by the next call taking the same context.
-
#context_new ⇒ Object
hegel_context_new never returns NULL (guaranteed by the header), so the handle returned here is always live.
-
#failure_free(ctx, f) ⇒ nil
No-op when
fis nil, matching hegel_failure_free's documented no-op-on-NULL contract; not translated, for the same reason as #context_free. -
#failure_origin(ctx, f) ⇒ String
Copies the origin string out of libhegel's own buffer before returning, since it is owned by the failure and only valid until #failure_free.
-
#failure_reproduction_blob(ctx, f) ⇒ String?
Returns nil when libhegel produced no reproduction blob for this failure, matching the header's documented NULL-on-that-case contract.
-
#generate_boolean(ctx, tc, p, forced, has_forced) ⇒ Boolean
Forcing has to agree with
p. -
#generate_bytes(ctx, tc, min_size, max_size) ⇒ String
Returns drawn bytes as a String, read via
lenthe same way #generate_string reads its own out-parameter: the header gives no NUL-termination guarantee for this buffer either, so the length is what makes the copy exact. -
#generate_bytes_result_free(ctx, result) ⇒ nil
No-op when
resultis nil, matching hegel_generate_bytes_result_free's documented no-op-on-NULL contract (also safe on an already-freed, zeroed struct); not translated, for the same reason as #context_free. -
#generate_date(ctx, tc, min_value, max_value) ⇒ Array[Integer]
hegel_generate_date.
-
#generate_datetime(ctx, tc, min_date, min_time, max_date, max_time) ⇒ [Array[Integer], Array[Integer]]
hegel_generate_datetime.
-
#generate_float(ctx, tc, width, min_value, max_value, allow_nan, allow_infinity, exclude_min, exclude_max, smallest_nonzero_magnitude) ⇒ Float
Returns a drawn double.
- #generate_integer(ctx, tc, min_value, max_value) ⇒ Integer
-
#generate_integer_big(ctx, tc, min_value, max_value) ⇒ Integer
hegel_generate_integer_big, for bounds that do not fit int64_t (see Hegel::Generators::IntegerGenerator#do_draw, which dispatches here instead of #generate_integer only when a bound is outside that range).
-
#generate_ipv4(ctx, tc) ⇒ String
hegel_generate_ipv4 writes into a caller-supplied fixed-length buffer instead of handing back a pointer through an out-parameter, unlike every generate_* call above: the header documents out_bytes as the address's 4 network-order bytes with no separate length to read, so the buffer's own size is the contract instead of a trailing len field.
-
#generate_ipv6(ctx, tc) ⇒ String
Same fixed-buffer shape as #generate_ipv4, sized for the header's documented 16 network-order bytes.
-
#generate_string(ctx, tc, generator) ⇒ String
Returns a drawn String, force-encoded as UTF-8 (this codec's alphabet), copying exactly the returned
lenbytes rather than reading a NUL-terminated buffer: the header documents it as not NUL-terminated and possibly containing interior NUL bytes, since the drawn alphabet can include U+0000. -
#generate_string_result_free(ctx, result) ⇒ nil
No-op when
resultis nil, matching hegel_generate_string_result_free's documented no-op-on-NULL contract (also safe on an already-freed, zeroed struct); not translated, for the same reason as #context_free. -
#generate_time(ctx, tc, min_value, max_value) ⇒ Array[Integer]
hegel_generate_time.
-
#generate_uuid(ctx, tc, version, has_version) ⇒ String
hegel_generate_uuid, returning the drawn UUID's 16 raw bytes.
-
#initialize(path = Hegel::Locate.resolve, io: $stderr) ⇒ Real
constructor
Opens
path(default: Hegel::Locate.resolve) and binds the functions this boundary calls. -
#mark_complete(ctx, tc, status, origin) ⇒ nil
originmust be non-nil only whenstatusis HEGEL_STATUS_INTERESTING, per the header; this layer neither builds nor validates that string, only passes through what the caller supplies. -
#new_collection(ctx, tc, min_size, max_size) ⇒ Object
Returns a caller-owned collection handle, released separately with #collection_free.
-
#new_pool(ctx, tc) ⇒ Object
Returns a caller-owned pool handle, released separately with #pool_free.
-
#new_state_machine(ctx, tc, rule_names, invariant_names) ⇒ Object
Returns a caller-owned state-machine handle, released separately with #state_machine_free.
-
#next_test_case(ctx, run) ⇒ Object?
Returns the next test case, or nil once the run has finished (the header documents *out_test_case as NULL at that point, with a HEGEL_OK result rather than an error).
-
#pool_add(ctx, tc, pool) ⇒ Integer
Returns a fresh variable id for the caller to associate with the value it just generated.
-
#pool_free(ctx, pool) ⇒ nil
No-op when
poolis nil, matching hegel_pool_free's documented no-op-on-NULL contract; not translated, for the same reason as #context_free. -
#pool_generate(ctx, tc, pool, consume) ⇒ Integer
Returns a variable id libhegel chose from
pool(and can shrink which one it chose). -
#run_free(ctx, run) ⇒ nil
No-op when
runis nil, matching hegel_run_free's documented no-op-on-NULL contract; not translated, for the same reason as #context_free. -
#run_result(ctx, run) ⇒ Object
Returns a caller-owned copy of the finished run's result, or raises HEGEL_E_NOT_COMPLETE (via LibHegel.check!) if the run has not finished.
-
#run_result_error(ctx, r) ⇒ String?
Returns nil when the run completed normally (PASSED or FAILED), matching the header's documented NULL-on-success contract for this out-parameter, distinct from an empty-string message.
-
#run_result_failure(ctx, r, index) ⇒ Object
indexmust be less than #run_result_failure_count's value, per the header. - #run_result_failure_count(ctx, r) ⇒ Integer
-
#run_result_free(ctx, r) ⇒ nil
No-op when
ris nil, matching hegel_run_result_free's documented no-op-on-NULL contract; not translated, for the same reason as #context_free. -
#run_result_status(ctx, r) ⇒ Integer
Returns the raw hegel_run_status_t value (HEGEL_RUN_STATUS_PASSED / _FAILED / _ERROR); this layer does not interpret it, matching how #mark_complete passes hegel_status_t values through unexamined.
-
#run_start(ctx, settings) ⇒ Object
settingscan be freed by the caller as soon as this call returns: the header documents that hegel_run_start copies the settings it is given rather than borrowing them. -
#settings_free(ctx, s) ⇒ nil
No-op when
sis nil, matching hegel_settings_free's documented no-op-on-NULL contract; not translated, for the same reason as #context_free: the header documents this call as always returning HEGEL_OK. -
#settings_new(ctx) ⇒ Object
Returns a settings handle initialized with libhegel's defaults, or raises the exception LibHegel.check! translates this call's result code to.
-
#settings_set_database(ctx, s, database) ⇒ nil
databasemay be nil (libhegel's own default path) or a String, including "" to disable the database. -
#settings_set_database_key(ctx, s, key) ⇒ nil
keymay be nil, which the header documents as clearing the key (the default); nil marshals to NULL for this :string argument, the same as #settings_set_database's own nilable database argument. - #settings_set_derandomize(ctx, s, derandomize) ⇒ nil
-
#settings_set_phases(ctx, s, phases) ⇒ nil
phasesis a bitwise OR of the HEGEL_PHASE_* constants. - #settings_set_report_multiple_failures(ctx, s, yes) ⇒ nil
- #settings_set_seed(ctx, s, seed, has_seed) ⇒ nil
- #settings_set_stateful_step_count(ctx, s, n) ⇒ nil
-
#settings_set_suppress_health_check(ctx, s, checks) ⇒ nil
checksis a bitwise OR of the HEGEL_HC_* constants. - #settings_set_test_cases(ctx, s, n) ⇒ nil
- #settings_set_verbosity(ctx, s, v) ⇒ nil
-
#start_span(ctx, tc, label) ⇒ nil
Opens a span labelled
label(one of the HEGEL_LABEL_* constants, or a caller-defined value that avoids them). -
#state_machine_free(ctx, state_machine) ⇒ nil
No-op when
state_machineis nil, matching hegel_state_machine_free's documented no-op-on-NULL contract; not translated, for the same reason as #context_free. -
#state_machine_next_rule(ctx, tc, state_machine) ⇒ Integer
Returns the index (in 0...num_rules) of the next stateful-testing rule to run, or HEGEL_STATE_MACHINE_DONE (-1) once +state_machine+'s step budget is exhausted -- returned as the raw sentinel value, not translated to nil.
-
#state_machine_rule_rejected(ctx, tc, state_machine) ⇒ nil
Reports the rule most recently returned by #state_machine_next_rule as rejected (an assumption failed before it completed), so it does not count toward the step budget.
-
#stop_span(ctx, tc, discard) ⇒ nil
Closes the most recently opened span.
-
#string_generator_domain(ctx, max_length) ⇒ Object
Returns a caller-owned string generator handle producing fully-qualified domain names, released the same way as #string_generator_text.
-
#string_generator_email(ctx) ⇒ Object
Returns a caller-owned string generator handle producing RFC 5321/5322 email addresses, released the same way as #string_generator_text.
-
#string_generator_free(ctx, generator) ⇒ nil
No-op when
generatoris nil, matching hegel_string_generator_free's documented no-op-on-NULL contract; not translated, for the same reason as #context_free. -
#string_generator_regex(ctx, pattern, fullmatch, alphabet = nil) ⇒ Object
Returns a caller-owned string generator handle matching
pattern(Python re syntax), released the same way as #string_generator_text: with #string_generator_free. -
#string_generator_text(ctx, min_size:, max_size:, codec: nil, min_codepoint: 0, max_codepoint: 0xFFFFFFFF) ⇒ Object
Returns a caller-owned string generator handle, released separately with #string_generator_free (or scoped with Hegel::TestCase#with_text_generator).
-
#string_generator_url(ctx) ⇒ Object
Returns a caller-owned string generator handle producing RFC 3986 http/https URLs, released the same way as #string_generator_text.
-
#target(ctx, tc, value, label) ⇒ nil
Records a numeric observation under
labelfor libhegel's own hill-climbing between generation rounds. -
#test_case_free(ctx, tc) ⇒ nil
No-op when
tcis nil, matching hegel_test_case_free's documented no-op-on-NULL contract; not translated, for the same reason as #context_free. -
#test_case_from_blob(ctx, settings, blob) ⇒ Object
Replays
blob(from #failure_reproduction_blob) againstsettingswith no run handle and no run loop involved, per the header. -
#version(ctx) ⇒ String
Returns the loaded engine's version string, or raises the exception LibHegel.check! translates this call's result code to.
Constructor Details
#initialize(path = Hegel::Locate.resolve, io: $stderr) ⇒ Real
Opens path (default: Hegel::Locate.resolve) and binds the
functions this boundary calls. Immediately after, opens a context
of its own to compare the loaded engine's version against
Hegel::LIBHEGEL_VERSION, warning on io (default $stderr) on a
mismatch; see LibHegel.warn_on_version_mismatch. io exists so a
test can capture the warning instead of writing to the real stderr.
attach_function's usual DSL binds a fixed library, named at
class-definition time, to methods it defines on a class or module
body. This class instead takes path as a constructor argument,
resolved fresh per instance, so attach_function's own per-instance
form is an anonymous module built fresh in #initialize. Measured on
ffi 1.17.4, arm64-darwin, over a failing property that shrinks: 777
ms for that form against 750 ms for binding each function directly
off a resolved symbol, which is what #bind below does: FFI::DynamicLibrary.open gives a handle to resolve
symbols against, and each call to #bind wraps one resolved symbol
as a callable FFI::Function, stored in its own instance variable
and invoked with #call by the method below it.
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/hegel/lib_hegel/real.rb', line 73 def initialize(path = Hegel::Locate.resolve, io: $stderr) @handle = FFI::DynamicLibrary.open(path, FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL) @hegel_context_new_fn = bind("hegel_context_new", [], :pointer) @hegel_context_free_fn = bind("hegel_context_free", [:pointer], :int32) @hegel_context_last_error_fn = bind("hegel_context_last_error", [:pointer], :pointer) @hegel_version_fn = bind("hegel_version", [:pointer, :pointer], :int32) @hegel_settings_new_fn = bind("hegel_settings_new", [:pointer, :pointer], :int32) @hegel_settings_free_fn = bind("hegel_settings_free", [:pointer, :pointer], :int32) @hegel_settings_set_test_cases_fn = bind( "hegel_settings_set_test_cases", [:pointer, :pointer, :uint64], :int32 ) @hegel_settings_set_verbosity_fn = bind( "hegel_settings_set_verbosity", [:pointer, :pointer, :uint32], :int32 ) @hegel_settings_set_seed_fn = bind( "hegel_settings_set_seed", [:pointer, :pointer, :uint64, :bool], :int32 ) @hegel_settings_set_derandomize_fn = bind( "hegel_settings_set_derandomize", [:pointer, :pointer, :bool], :int32 ) @hegel_settings_set_database_fn = bind( "hegel_settings_set_database", [:pointer, :pointer, :string], :int32 ) @hegel_settings_set_stateful_step_count_fn = bind( "hegel_settings_set_stateful_step_count", [:pointer, :pointer, :int64], :int32 ) @hegel_settings_set_report_multiple_failures_fn = bind( "hegel_settings_set_report_multiple_failures", [:pointer, :pointer, :bool], :int32 ) @hegel_settings_set_database_key_fn = bind( "hegel_settings_set_database_key", [:pointer, :pointer, :string], :int32 ) @hegel_settings_set_phases_fn = bind( "hegel_settings_set_phases", [:pointer, :pointer, :uint32], :int32 ) @hegel_settings_set_suppress_health_check_fn = bind( "hegel_settings_set_suppress_health_check", [:pointer, :pointer, :uint32], :int32 ) @hegel_run_start_fn = bind( "hegel_run_start", [:pointer, :pointer, :pointer, :pointer, :pointer], :int32 ) @hegel_next_test_case_fn = bind("hegel_next_test_case", [:pointer, :pointer, :pointer], :int32) @hegel_run_free_fn = bind("hegel_run_free", [:pointer, :pointer], :int32) @hegel_test_case_free_fn = bind("hegel_test_case_free", [:pointer, :pointer], :int32) @hegel_mark_complete_fn = bind("hegel_mark_complete", [:pointer, :pointer, :uint32, :string], :int32) @hegel_target_fn = bind("hegel_target", [:pointer, :pointer, :double, :string], :int32) @hegel_run_result_fn = bind("hegel_run_result", [:pointer, :pointer, :pointer], :int32) @hegel_run_result_free_fn = bind("hegel_run_result_free", [:pointer, :pointer], :int32) @hegel_run_result_status_fn = bind("hegel_run_result_status", [:pointer, :pointer, :pointer], :int32) @hegel_run_result_error_fn = bind("hegel_run_result_error", [:pointer, :pointer, :pointer], :int32) @hegel_run_result_failure_count_fn = bind( "hegel_run_result_failure_count", [:pointer, :pointer, :pointer], :int32 ) @hegel_run_result_failure_fn = bind( "hegel_run_result_failure", [:pointer, :pointer, :size_t, :pointer], :int32 ) @hegel_failure_free_fn = bind("hegel_failure_free", [:pointer, :pointer], :int32) @hegel_failure_origin_fn = bind("hegel_failure_origin", [:pointer, :pointer, :pointer], :int32) @hegel_failure_reproduction_blob_fn = bind( "hegel_failure_reproduction_blob", [:pointer, :pointer, :pointer], :int32 ) @hegel_test_case_from_blob_fn = bind( "hegel_test_case_from_blob", [:pointer, :pointer, :string, :pointer, :pointer, :pointer], :int32 ) @hegel_generate_boolean_fn = bind( "hegel_generate_boolean", [:pointer, :pointer, :double, :bool, :bool, :pointer], :int32 ) @hegel_generate_integer_fn = bind( "hegel_generate_integer", [:pointer, :pointer, :int64, :int64, :pointer], :int32 ) @hegel_generate_integer_big_fn = bind( "hegel_generate_integer_big", [:pointer, :pointer, :pointer, :size_t, :pointer, :size_t, :pointer, :size_t, :pointer], :int32 ) @hegel_start_span_fn = bind("hegel_start_span", [:pointer, :pointer, :uint64], :int32) @hegel_stop_span_fn = bind("hegel_stop_span", [:pointer, :pointer, :bool], :int32) @hegel_new_collection_fn = bind( "hegel_new_collection", [:pointer, :pointer, :uint64, :uint64, :pointer], :int32 ) @hegel_collection_more_fn = bind("hegel_collection_more", [:pointer, :pointer, :pointer, :pointer], :int32) @hegel_collection_reject_fn = bind( "hegel_collection_reject", [:pointer, :pointer, :pointer, :string], :int32 ) @hegel_collection_free_fn = bind("hegel_collection_free", [:pointer, :pointer], :int32) @hegel_new_pool_fn = bind("hegel_new_pool", [:pointer, :pointer, :pointer], :int32) @hegel_pool_add_fn = bind("hegel_pool_add", [:pointer, :pointer, :pointer, :pointer], :int32) @hegel_pool_generate_fn = bind( "hegel_pool_generate", [:pointer, :pointer, :pointer, :bool, :pointer], :int32 ) @hegel_pool_free_fn = bind("hegel_pool_free", [:pointer, :pointer], :int32) @hegel_new_state_machine_fn = bind( "hegel_new_state_machine", [:pointer, :pointer, :pointer, :size_t, :pointer, :size_t, :pointer], :int32 ) @hegel_state_machine_next_rule_fn = bind( "hegel_state_machine_next_rule", [:pointer, :pointer, :pointer, :pointer], :int32 ) @hegel_state_machine_rule_rejected_fn = bind( "hegel_state_machine_rule_rejected", [:pointer, :pointer, :pointer], :int32 ) @hegel_state_machine_free_fn = bind("hegel_state_machine_free", [:pointer, :pointer], :int32) @hegel_generate_float_fn = bind( "hegel_generate_float", [:pointer, :pointer, :uint32, :double, :double, :bool, :bool, :bool, :bool, :double, :pointer], :int32 ) # hegel_string_generator_text takes 14 arguments past ctx; the # last 8 (categories_len through exclude_characters_len) are the # category and explicit-character filter parameters this task # does not wire up (see #string_generator_text). @hegel_string_generator_text_fn = bind( "hegel_string_generator_text", [:pointer, :uint64, :uint64, :string, :uint32, :uint32, :pointer, :size_t, :pointer, :size_t, :pointer, :size_t, :pointer, :size_t, :pointer], :int32 ) @hegel_string_generator_free_fn = bind("hegel_string_generator_free", [:pointer, :pointer], :int32) @hegel_generate_string_fn = bind( "hegel_generate_string", [:pointer, :pointer, :pointer, :pointer], :int32 ) @hegel_generate_string_result_free_fn = bind( "hegel_generate_string_result_free", [:pointer, :pointer], :int32 ) @hegel_generate_bytes_fn = bind( "hegel_generate_bytes", [:pointer, :pointer, :uint64, :uint64, :pointer], :int32 ) @hegel_generate_bytes_result_free_fn = bind( "hegel_generate_bytes_result_free", [:pointer, :pointer], :int32 ) @hegel_string_generator_regex_fn = bind( "hegel_string_generator_regex", [:pointer, :string, :bool, :pointer, :pointer], :int32 ) @hegel_string_generator_email_fn = bind("hegel_string_generator_email", [:pointer, :pointer], :int32) @hegel_string_generator_url_fn = bind("hegel_string_generator_url", [:pointer, :pointer], :int32) @hegel_string_generator_domain_fn = bind( "hegel_string_generator_domain", [:pointer, :uint64, :pointer], :int32 ) @hegel_generate_ipv4_fn = bind("hegel_generate_ipv4", [:pointer, :pointer, :pointer], :int32) @hegel_generate_ipv6_fn = bind("hegel_generate_ipv6", [:pointer, :pointer, :pointer], :int32) @hegel_generate_uuid_fn = bind( "hegel_generate_uuid", [:pointer, :pointer, :uint8, :bool, :pointer], :int32 ) @hegel_generate_date_fn = bind( "hegel_generate_date", [:pointer, :pointer, DateStruct.by_value, DateStruct.by_value, :pointer], :int32 ) @hegel_generate_time_fn = bind( "hegel_generate_time", [:pointer, :pointer, TimeStruct.by_value, TimeStruct.by_value, :pointer], :int32 ) @hegel_generate_datetime_fn = bind( "hegel_generate_datetime", [:pointer, :pointer, DatetimeStruct.by_value, DatetimeStruct.by_value, :pointer], :int32 ) LibHegel.with_context(self) { |ctx| LibHegel.warn_on_version_mismatch(self, ctx, io: io) } end |
Instance Method Details
#collection_free(ctx, collection) ⇒ nil
No-op when collection is nil, matching hegel_collection_free's
documented no-op-on-NULL contract; not translated, for the same
reason as #context_free. Unlike every other *_free above, this
call takes no test-case handle: the header documents a collection
as independent of the test case and run it was created under.
645 646 647 648 |
# File 'lib/hegel/lib_hegel/real.rb', line 645 def collection_free(ctx, collection) @hegel_collection_free_fn.call(ctx, collection) nil end |
#collection_more(ctx, tc, collection) ⇒ Boolean
Returns whether libhegel wants another element; call in a loop, drawing the next element each time this is true, until it is false.
622 623 624 625 626 627 |
# File 'lib/hegel/lib_hegel/real.rb', line 622 def collection_more(ctx, tc, collection) out = FFI::MemoryPointer.new(:bool) code = @hegel_collection_more_fn.call(ctx, tc, collection, out) LibHegel.check!(self, ctx, code) out.read_uint8 != 0 end |
#collection_reject(ctx, tc, collection, why = nil) ⇒ nil
Tells libhegel the last element collection produced is invalid.
why is an optional human-readable reason (nil marshals to NULL
for this :string argument, which the header allows); the header
documents it as validated but reserved for future rejection
diagnostics, unused today.
634 635 636 637 638 |
# File 'lib/hegel/lib_hegel/real.rb', line 634 def collection_reject(ctx, tc, collection, why = nil) code = @hegel_collection_reject_fn.call(ctx, tc, collection, why) LibHegel.check!(self, ctx, code) nil end |
#context_free(ctx) ⇒ nil
No-op when ctx is nil: libhegel documents hegel_context_free as a
no-op on NULL, and a Ruby nil marshals to a NULL pointer for a
:pointer argument here, so no separate nil check is needed on this
side. The result code is not translated: the header documents this
call as always returning HEGEL_OK, so there is nothing to raise.
255 256 257 258 |
# File 'lib/hegel/lib_hegel/real.rb', line 255 def context_free(ctx) @hegel_context_free_fn.call(ctx) nil end |
#context_last_error(ctx) ⇒ String
Copies the message out of libhegel's own buffer into a Ruby String before returning, since the header documents that buffer as borrowed and invalidated by the next call taking the same context.
263 264 265 |
# File 'lib/hegel/lib_hegel/real.rb', line 263 def context_last_error(ctx) utf8(@hegel_context_last_error_fn.call(ctx)) end |
#context_new ⇒ Object
hegel_context_new never returns NULL (guaranteed by the header), so the handle returned here is always live.
246 247 248 |
# File 'lib/hegel/lib_hegel/real.rb', line 246 def context_new @hegel_context_new_fn.call end |
#failure_free(ctx, f) ⇒ nil
No-op when f is nil, matching hegel_failure_free's documented
no-op-on-NULL contract; not translated, for the same reason as
#context_free.
490 491 492 493 |
# File 'lib/hegel/lib_hegel/real.rb', line 490 def failure_free(ctx, f) @hegel_failure_free_fn.call(ctx, f) nil end |
#failure_origin(ctx, f) ⇒ String
Copies the origin string out of libhegel's own buffer before returning, since it is owned by the failure and only valid until #failure_free.
498 499 500 501 502 503 |
# File 'lib/hegel/lib_hegel/real.rb', line 498 def failure_origin(ctx, f) out = FFI::MemoryPointer.new(:pointer) code = @hegel_failure_origin_fn.call(ctx, f, out) LibHegel.check!(self, ctx, code) utf8(out.read_pointer) end |
#failure_reproduction_blob(ctx, f) ⇒ String?
Returns nil when libhegel produced no reproduction blob for this failure, matching the header's documented NULL-on-that-case contract. See #nullable_out_string for the shared ownership note.
508 509 510 511 512 513 |
# File 'lib/hegel/lib_hegel/real.rb', line 508 def failure_reproduction_blob(ctx, f) out = FFI::MemoryPointer.new(:pointer) code = @hegel_failure_reproduction_blob_fn.call(ctx, f, out) LibHegel.check!(self, ctx, code) nullable_out_string(out) end |
#generate_boolean(ctx, tc, p, forced, has_forced) ⇒ Boolean
Forcing has to agree with p. Measured against libhegel 0.32.5:
forcing true at p = 0.0 and forcing false at p = 1.0 both come back
HEGEL_E_INVALID_ARG ("generate_boolean: cannot force ..."), while
forcing either way succeeds at any p between them. The header
describes the two ends as yielding false and true without consuming
entropy, and says nothing about what forcing does against them, so
this is written down where a caller constructing a forced draw will
look for it.
544 545 546 547 548 549 |
# File 'lib/hegel/lib_hegel/real.rb', line 544 def generate_boolean(ctx, tc, p, forced, has_forced) out = FFI::MemoryPointer.new(:bool) code = @hegel_generate_boolean_fn.call(ctx, tc, p, forced, has_forced, out) LibHegel.check!(self, ctx, code) out.read_uint8 != 0 end |
#generate_bytes(ctx, tc, min_size, max_size) ⇒ String
Returns drawn bytes as a String, read via len the same way
#generate_string reads its own out-parameter: the header gives no
NUL-termination guarantee for this buffer either, so the length is
what makes the copy exact.
Unlike #generate_string, the result is never force-encoded. hegel_generate_bytes_result_t is documented as a byte buffer, not text, and FFI::Pointer#read_bytes already returns ASCII-8BIT (Encoding::BINARY), which is the encoding a byte string belongs in.
The native buffer is released in ensure via
#generate_bytes_result_free, for the same reason #generate_string
frees its own result from inside this file: ffi is confined to
this file, so the freeable handle never leaves this method. Freeing
is safe even when the draw above raised, for the same
zero-filled-allocation reason documented on #generate_string: the
header documents hegel_generate_bytes_result_free as safe on an
already-freed (zeroed) struct too.
853 854 855 856 857 858 859 860 |
# File 'lib/hegel/lib_hegel/real.rb', line 853 def generate_bytes(ctx, tc, min_size, max_size) out = RawResultStruct.new code = @hegel_generate_bytes_fn.call(ctx, tc, min_size, max_size, out) LibHegel.check!(self, ctx, code) out[:data].read_bytes(out[:len]) ensure generate_bytes_result_free(ctx, out) end |
#generate_bytes_result_free(ctx, result) ⇒ nil
No-op when result is nil, matching
hegel_generate_bytes_result_free's documented no-op-on-NULL
contract (also safe on an already-freed, zeroed struct); not
translated, for the same reason as #context_free.
866 867 868 869 |
# File 'lib/hegel/lib_hegel/real.rb', line 866 def generate_bytes_result_free(ctx, result) @hegel_generate_bytes_result_free_fn.call(ctx, result) nil end |
#generate_date(ctx, tc, min_value, max_value) ⇒ Array[Integer]
hegel_generate_date. +min_value+/+max_value+ are each a [year, month, day] Array, written by #date_struct into a DateStruct passed .by_value (see the comment on that class, above #initialize). Returns a [year, month, day] Array, the field-level shape Hegel::Generators::DatesGenerator builds its own Date from -- the same division of labor #generate_ipv4/#generate_uuid already follow, returning raw values for a generator one layer up to turn into the caller-facing type. This layer does not validate year/month/day itself: measured against libhegel 0.32.5, an invalid date (month 13, say) already comes back HEGEL_E_INVALID_ARG, translated by LibHegel.check! below, even though the header's Returns line for this call names only HEGEL_OK/HEGEL_E_STOP_TEST. #run_result_failure's own comment records the same measured behaviour for a different call.
974 975 976 977 978 979 |
# File 'lib/hegel/lib_hegel/real.rb', line 974 def generate_date(ctx, tc, min_value, max_value) out = DateStruct.new code = @hegel_generate_date_fn.call(ctx, tc, date_struct(min_value), date_struct(max_value), out) LibHegel.check!(self, ctx, code) read_date(out) end |
#generate_datetime(ctx, tc, min_date, min_time, max_date, max_time) ⇒ [Array[Integer], Array[Integer]]
hegel_generate_datetime. +min_date+/+max_date+ are each a [year, month, day] Array; +min_time+/+max_time+ are each an [hour, minute, second, microsecond] Array -- hegel_datetime_t is a hegel_date_t followed by a hegel_time_t (see DatetimeStruct's own layout, above #initialize). Returns a [[year, month, day], [hour, minute, second, microsecond]] pair, for Hegel::Generators::DatetimesGenerator to build its own Time from.
999 1000 1001 1002 1003 1004 1005 1006 |
# File 'lib/hegel/lib_hegel/real.rb', line 999 def generate_datetime(ctx, tc, min_date, min_time, max_date, max_time) out = DatetimeStruct.new code = @hegel_generate_datetime_fn.call( ctx, tc, datetime_struct(min_date, min_time), datetime_struct(max_date, max_time), out ) LibHegel.check!(self, ctx, code) [read_date(out[:date]), read_time(out[:time])] end |
#generate_float(ctx, tc, width, min_value, max_value, allow_nan, allow_infinity, exclude_min, exclude_max, smallest_nonzero_magnitude) ⇒ Float
Returns a drawn double. smallest_nonzero_magnitude must be
positive and finite; pass
HEGEL_FLOAT64_SMALLEST_NONZERO_MAGNITUDE_UNRESTRICTED for width
64 with no restriction, per the header.
764 765 766 767 768 769 770 771 |
# File 'lib/hegel/lib_hegel/real.rb', line 764 def generate_float(ctx, tc, width, min_value, max_value, allow_nan, allow_infinity, exclude_min, exclude_max, smallest_nonzero_magnitude) out = FFI::MemoryPointer.new(:double) code = @hegel_generate_float_fn.call(ctx, tc, width, min_value, max_value, allow_nan, allow_infinity, exclude_min, exclude_max, smallest_nonzero_magnitude, out) LibHegel.check!(self, ctx, code) out.read_double end |
#generate_integer(ctx, tc, min_value, max_value) ⇒ Integer
551 552 553 554 555 556 |
# File 'lib/hegel/lib_hegel/real.rb', line 551 def generate_integer(ctx, tc, min_value, max_value) out = FFI::MemoryPointer.new(:int64) code = @hegel_generate_integer_fn.call(ctx, tc, min_value, max_value, out) LibHegel.check!(self, ctx, code) out.read_int64 end |
#generate_integer_big(ctx, tc, min_value, max_value) ⇒ Integer
hegel_generate_integer_big, for bounds that do not fit int64_t (see Hegel::Generators::IntegerGenerator#do_draw, which dispatches here instead of #generate_integer only when a bound is outside that range). +min_value+/+max_value+ are encoded and the result decoded via LibHegel.encode_integer_le/.decode_integer_le, which own the two's-complement little-endian convention itself; this method only owns the buffer marshalling around it. min_value_ptr/max_value_ptr are declared :pointer, not :string: the encoded bytes routinely contain interior zero bytes (256 encodes as "\x00\x01"), which a NUL-terminated const char* argument would truncate. out_value's capacity is the larger of the two encoded bounds, per the header's "out_value_cap >= max(min_value_len, max_value_len) always succeeds"; the result is read back at its own reported out_value_len, not the buffer's full capacity, since decode_integer_le needs only that many bytes.
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 |
# File 'lib/hegel/lib_hegel/real.rb', line 573 def generate_integer_big(ctx, tc, min_value, max_value) min_bytes = LibHegel.encode_integer_le(min_value) max_bytes = LibHegel.encode_integer_le(max_value) min_value_ptr = bytes_to_pointer(min_bytes) max_value_ptr = bytes_to_pointer(max_bytes) cap = [min_bytes.bytesize, max_bytes.bytesize].max out_value = FFI::MemoryPointer.new(cap) out_value_len = FFI::MemoryPointer.new(:size_t) code = @hegel_generate_integer_big_fn.call( ctx, tc, min_value_ptr, min_bytes.bytesize, max_value_ptr, max_bytes.bytesize, out_value, cap, out_value_len ) LibHegel.check!(self, ctx, code) len = out_value_len.read_uint64 LibHegel.decode_integer_le(out_value.read_bytes(len)) end |
#generate_ipv4(ctx, tc) ⇒ String
hegel_generate_ipv4 writes into a caller-supplied fixed-length buffer instead of handing back a pointer through an out-parameter, unlike every generate_* call above: the header documents out_bytes as the address's 4 network-order bytes with no separate length to read, so the buffer's own size is the contract instead of a trailing len field. IPAddr conversion is left to the generator built on top of this call; this layer returns the raw bytes.
926 927 928 929 930 931 |
# File 'lib/hegel/lib_hegel/real.rb', line 926 def generate_ipv4(ctx, tc) out = FFI::MemoryPointer.new(4) code = @hegel_generate_ipv4_fn.call(ctx, tc, out) LibHegel.check!(self, ctx, code) out.read_bytes(4) end |
#generate_ipv6(ctx, tc) ⇒ String
Same fixed-buffer shape as #generate_ipv4, sized for the header's documented 16 network-order bytes.
935 936 937 938 939 940 |
# File 'lib/hegel/lib_hegel/real.rb', line 935 def generate_ipv6(ctx, tc) out = FFI::MemoryPointer.new(16) code = @hegel_generate_ipv6_fn.call(ctx, tc, out) LibHegel.check!(self, ctx, code) out.read_bytes(16) end |
#generate_string(ctx, tc, generator) ⇒ String
Returns a drawn String, force-encoded as UTF-8 (this codec's
alphabet), copying exactly the returned len bytes rather than
reading a NUL-terminated buffer: the header documents it as not
NUL-terminated and possibly containing interior NUL bytes, since
the drawn alphabet can include U+0000.
The native buffer is released in ensure via
#generate_string_result_free, called directly from here rather
than left to the caller: nothing outside this file may hold or
read the raw struct (ffi is confined to this file), so unlike
#new_collection or #string_generator_text, the freeable handle
here never leaves this method. Freeing is safe even when the draw
above raised: out starts zero-filled (FFI::Struct.new allocates
cleared memory) and libhegel only writes into it on success, so
the struct #generate_string_result_free sees here is always
either a completed draw or the all-zero state the header
documents as already safe to free.
817 818 819 820 821 822 823 824 |
# File 'lib/hegel/lib_hegel/real.rb', line 817 def generate_string(ctx, tc, generator) out = RawResultStruct.new code = @hegel_generate_string_fn.call(ctx, tc, generator, out) LibHegel.check!(self, ctx, code) out[:data].read_bytes(out[:len]).force_encoding(Encoding::UTF_8) ensure generate_string_result_free(ctx, out) end |
#generate_string_result_free(ctx, result) ⇒ nil
No-op when result is nil, matching
hegel_generate_string_result_free's documented no-op-on-NULL
contract (also safe on an already-freed, zeroed struct); not
translated, for the same reason as #context_free.
830 831 832 833 |
# File 'lib/hegel/lib_hegel/real.rb', line 830 def generate_string_result_free(ctx, result) @hegel_generate_string_result_free_fn.call(ctx, result) nil end |
#generate_time(ctx, tc, min_value, max_value) ⇒ Array[Integer]
hegel_generate_time. +min_value+/+max_value+ are each an [hour, minute, second, microsecond] Array; same struct-passing, return shape, and validation division of labor as #generate_date above, for Hegel::Generators::TimesGenerator.
985 986 987 988 989 990 |
# File 'lib/hegel/lib_hegel/real.rb', line 985 def generate_time(ctx, tc, min_value, max_value) out = TimeStruct.new code = @hegel_generate_time_fn.call(ctx, tc, time_struct(min_value), time_struct(max_value), out) LibHegel.check!(self, ctx, code) read_time(out) end |
#generate_uuid(ctx, tc, version, has_version) ⇒ String
hegel_generate_uuid, returning the drawn UUID's 16 raw bytes.
has_version true forces the RFC 4122 version nibble to version
(0..15) and the variant nibble to the RFC 4122 variant, per the
header; version is ignored (but still marshalled; pass 0) when
has_version is false. Converting the raw bytes to the standard
8-4-4-4-12 hex String is left to Hegel::Generators::UuidsGenerator,
the same division of labor #generate_ipv4/#generate_ipv6 already
follow for their own byte-to-address conversion. An out-of-range
version is not checked here: measured against libhegel 0.32.5, the
engine itself returns HEGEL_E_INVALID_ARG for one, which
LibHegel.check! already translates.
953 954 955 956 957 958 |
# File 'lib/hegel/lib_hegel/real.rb', line 953 def generate_uuid(ctx, tc, version, has_version) out = FFI::MemoryPointer.new(16) code = @hegel_generate_uuid_fn.call(ctx, tc, version, has_version, out) LibHegel.check!(self, ctx, code) out.read_bytes(16) end |
#mark_complete(ctx, tc, status, origin) ⇒ nil
origin must be non-nil only when status is
HEGEL_STATUS_INTERESTING, per the header; this layer neither builds
nor validates that string, only passes through what the caller
supplies. nil marshals to NULL for this :string argument.
408 409 410 411 412 |
# File 'lib/hegel/lib_hegel/real.rb', line 408 def mark_complete(ctx, tc, status, origin) code = @hegel_mark_complete_fn.call(ctx, tc, status, origin) LibHegel.check!(self, ctx, code) nil end |
#new_collection(ctx, tc, min_size, max_size) ⇒ Object
Returns a caller-owned collection handle, released separately
with #collection_free. Pass HEGEL_COLLECTION_MAX_SIZE_UNBOUNDED
as max_size for no upper bound, per the header.
612 613 614 615 616 617 |
# File 'lib/hegel/lib_hegel/real.rb', line 612 def new_collection(ctx, tc, min_size, max_size) out = FFI::MemoryPointer.new(:pointer) code = @hegel_new_collection_fn.call(ctx, tc, min_size, max_size, out) LibHegel.check!(self, ctx, code) out.read_pointer end |
#new_pool(ctx, tc) ⇒ Object
Returns a caller-owned pool handle, released separately with #pool_free. A pool tracks a set of variable ids libhegel can draw from and shrink over -- mostly used for stateful testing, where a rule acts on a value a previous rule generated; the caller keeps its own mapping from variable id to that value, per the header.
655 656 657 658 659 660 |
# File 'lib/hegel/lib_hegel/real.rb', line 655 def new_pool(ctx, tc) out = FFI::MemoryPointer.new(:pointer) code = @hegel_new_pool_fn.call(ctx, tc, out) LibHegel.check!(self, ctx, code) out.read_pointer end |
#new_state_machine(ctx, tc, rule_names, invariant_names) ⇒ Object
Returns a caller-owned state-machine handle, released separately
with #state_machine_free. rule_names and invariant_names are
each an Array of Ruby Strings, packed into the const char *const *
arguments hegel_new_state_machine expects by #pack_name_array; see
that method's own comment for how and why. Validating
rule_names as non-empty (the header's own requirement) is left
to the caller, the same division of labor #new_collection leaves
to the caller for its own min_size/max_size ordering.
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 |
# File 'lib/hegel/lib_hegel/real.rb', line 706 def new_state_machine(ctx, tc, rule_names, invariant_names) out = FFI::MemoryPointer.new(:pointer) # _rule_pointers / _invariant_pointers are unread past the call # below, the same shape #generate_integer_big's own # min_value_ptr/max_value_ptr already have; keeping them as local # variables here, not discarded inside #pack_name_array, is what # keeps the native buffers each one owns live through the call. # The leading underscore tells the linter that on purpose, the # same way it would for a block argument the block never reads. rule_names_ptr, _rule_pointers = pack_name_array(rule_names) invariant_names_ptr, _invariant_pointers = pack_name_array(invariant_names) code = @hegel_new_state_machine_fn.call( ctx, tc, rule_names_ptr, rule_names.size, invariant_names_ptr, invariant_names.size, out ) LibHegel.check!(self, ctx, code) out.read_pointer end |
#next_test_case(ctx, run) ⇒ Object?
Returns the next test case, or nil once the run has finished (the header documents *out_test_case as NULL at that point, with a HEGEL_OK result rather than an error).
380 381 382 383 384 385 386 |
# File 'lib/hegel/lib_hegel/real.rb', line 380 def next_test_case(ctx, run) out = FFI::MemoryPointer.new(:pointer) code = @hegel_next_test_case_fn.call(ctx, run, out) LibHegel.check!(self, ctx, code) ptr = out.read_pointer ptr.null? ? nil : ptr end |
#pool_add(ctx, tc, pool) ⇒ Integer
Returns a fresh variable id for the caller to associate with the value it just generated. The header documents the id as drawn from +tc+'s stream and recorded by value, not by pool position, so it stays stable across shrinking: deleting an earlier addition never renumbers the survivors.
667 668 669 670 671 672 |
# File 'lib/hegel/lib_hegel/real.rb', line 667 def pool_add(ctx, tc, pool) out = FFI::MemoryPointer.new(:int64) code = @hegel_pool_add_fn.call(ctx, tc, pool, out) LibHegel.check!(self, ctx, code) out.read_int64 end |
#pool_free(ctx, pool) ⇒ nil
No-op when pool is nil, matching hegel_pool_free's documented
no-op-on-NULL contract; not translated, for the same reason as
#context_free.
693 694 695 696 |
# File 'lib/hegel/lib_hegel/real.rb', line 693 def pool_free(ctx, pool) @hegel_pool_free_fn.call(ctx, pool) nil end |
#pool_generate(ctx, tc, pool, consume) ⇒ Integer
Returns a variable id libhegel chose from pool (and can shrink
which one it chose). consume true removes the drawn variable
from the pool; false leaves it. LibHegel.check! already translates
HEGEL_E_ASSUME -- what the header documents this call returning
when pool holds no variables -- to Hegel::AssumeFailed, the same
translation every other assumption failure gets, so no extra code
is needed here for that case; pinned by
test_real_pool_generate_on_an_empty_pool_raises_assume_failed in
test/hegel/test_lib_hegel.rb.
683 684 685 686 687 688 |
# File 'lib/hegel/lib_hegel/real.rb', line 683 def pool_generate(ctx, tc, pool, consume) out = FFI::MemoryPointer.new(:int64) code = @hegel_pool_generate_fn.call(ctx, tc, pool, consume, out) LibHegel.check!(self, ctx, code) out.read_int64 end |
#run_free(ctx, run) ⇒ nil
No-op when run is nil, matching hegel_run_free's documented
no-op-on-NULL contract; not translated, for the same reason as
#context_free.
391 392 393 394 |
# File 'lib/hegel/lib_hegel/real.rb', line 391 def run_free(ctx, run) @hegel_run_free_fn.call(ctx, run) nil end |
#run_result(ctx, run) ⇒ Object
Returns a caller-owned copy of the finished run's result, or raises
HEGEL_E_NOT_COMPLETE (via LibHegel.check!) if the run has not
finished. The header documents this copy as staying valid after
#run_free, so run can be freed as soon as this call returns; it
must be released separately, exactly once, with #run_result_free.
431 432 433 434 435 436 |
# File 'lib/hegel/lib_hegel/real.rb', line 431 def run_result(ctx, run) out = FFI::MemoryPointer.new(:pointer) code = @hegel_run_result_fn.call(ctx, run, out) LibHegel.check!(self, ctx, code) out.read_pointer end |
#run_result_error(ctx, r) ⇒ String?
Returns nil when the run completed normally (PASSED or FAILED), matching the header's documented NULL-on-success contract for this out-parameter, distinct from an empty-string message. See #nullable_out_string for the ownership note shared with #failure_reproduction_blob.
461 462 463 464 465 466 |
# File 'lib/hegel/lib_hegel/real.rb', line 461 def run_result_error(ctx, r) out = FFI::MemoryPointer.new(:pointer) code = @hegel_run_result_error_fn.call(ctx, r, out) LibHegel.check!(self, ctx, code) nullable_out_string(out) end |
#run_result_failure(ctx, r, index) ⇒ Object
index must be less than #run_result_failure_count's value, per the
header. Returns a caller-owned failure handle, released separately
with #failure_free. Measured against libhegel 0.32.5: an
out-of-range index comes back HEGEL_E_INVALID_ARG, even though
the header's Returns line for this call names only HEGEL_OK.
480 481 482 483 484 485 |
# File 'lib/hegel/lib_hegel/real.rb', line 480 def run_result_failure(ctx, r, index) out = FFI::MemoryPointer.new(:pointer) code = @hegel_run_result_failure_fn.call(ctx, r, index, out) LibHegel.check!(self, ctx, code) out.read_pointer end |
#run_result_failure_count(ctx, r) ⇒ Integer
468 469 470 471 472 473 |
# File 'lib/hegel/lib_hegel/real.rb', line 468 def run_result_failure_count(ctx, r) out = FFI::MemoryPointer.new(:size_t) code = @hegel_run_result_failure_count_fn.call(ctx, r, out) LibHegel.check!(self, ctx, code) out.read_uint64 end |
#run_result_free(ctx, r) ⇒ nil
No-op when r is nil, matching hegel_run_result_free's documented
no-op-on-NULL contract; not translated, for the same reason as
#context_free.
441 442 443 444 |
# File 'lib/hegel/lib_hegel/real.rb', line 441 def run_result_free(ctx, r) @hegel_run_result_free_fn.call(ctx, r) nil end |
#run_result_status(ctx, r) ⇒ Integer
Returns the raw hegel_run_status_t value (HEGEL_RUN_STATUS_PASSED / _FAILED / _ERROR); this layer does not interpret it, matching how #mark_complete passes hegel_status_t values through unexamined.
449 450 451 452 453 454 |
# File 'lib/hegel/lib_hegel/real.rb', line 449 def run_result_status(ctx, r) out = FFI::MemoryPointer.new(:int32) code = @hegel_run_result_status_fn.call(ctx, r, out) LibHegel.check!(self, ctx, code) out.read_int32 end |
#run_start(ctx, settings) ⇒ Object
settings can be freed by the caller as soon as this call returns:
the header documents that hegel_run_start copies the settings it is
given rather than borrowing them. callback and user_data are always
NULL here, which the header documents as leaving libhegel's output
on stderr; wiring a Ruby-backed callback is left to a later task.
370 371 372 373 374 375 |
# File 'lib/hegel/lib_hegel/real.rb', line 370 def run_start(ctx, settings) out = FFI::MemoryPointer.new(:pointer) code = @hegel_run_start_fn.call(ctx, settings, nil, nil, out) LibHegel.check!(self, ctx, code) out.read_pointer end |
#settings_free(ctx, s) ⇒ nil
No-op when s is nil, matching hegel_settings_free's documented
no-op-on-NULL contract; not translated, for the same reason as
#context_free: the header documents this call as always returning
HEGEL_OK.
290 291 292 293 |
# File 'lib/hegel/lib_hegel/real.rb', line 290 def settings_free(ctx, s) @hegel_settings_free_fn.call(ctx, s) nil end |
#settings_new(ctx) ⇒ Object
Returns a settings handle initialized with libhegel's defaults, or raises the exception LibHegel.check! translates this call's result code to.
279 280 281 282 283 284 |
# File 'lib/hegel/lib_hegel/real.rb', line 279 def settings_new(ctx) out = FFI::MemoryPointer.new(:pointer) code = @hegel_settings_new_fn.call(ctx, out) LibHegel.check!(self, ctx, code) out.read_pointer end |
#settings_set_database(ctx, s, database) ⇒ nil
database may be nil (libhegel's own default path) or a String,
including "" to disable the database. Declared :string below, so a
Ruby String marshals as a const char* to its bytes and nil marshals
to NULL, both directly -- no separate pointer to build here.
323 324 325 326 327 |
# File 'lib/hegel/lib_hegel/real.rb', line 323 def settings_set_database(ctx, s, database) code = @hegel_settings_set_database_fn.call(ctx, s, database) LibHegel.check!(self, ctx, code) nil end |
#settings_set_database_key(ctx, s, key) ⇒ nil
key may be nil, which the header documents as clearing the key
(the default); nil marshals to NULL for this :string argument, the
same as #settings_set_database's own nilable database argument.
344 345 346 347 348 |
# File 'lib/hegel/lib_hegel/real.rb', line 344 def settings_set_database_key(ctx, s, key) code = @hegel_settings_set_database_key_fn.call(ctx, s, key) LibHegel.check!(self, ctx, code) nil end |
#settings_set_derandomize(ctx, s, derandomize) ⇒ nil
313 314 315 316 317 |
# File 'lib/hegel/lib_hegel/real.rb', line 313 def settings_set_derandomize(ctx, s, derandomize) code = @hegel_settings_set_derandomize_fn.call(ctx, s, derandomize) LibHegel.check!(self, ctx, code) nil end |
#settings_set_phases(ctx, s, phases) ⇒ nil
phases is a bitwise OR of the HEGEL_PHASE_* constants.
351 352 353 354 355 |
# File 'lib/hegel/lib_hegel/real.rb', line 351 def settings_set_phases(ctx, s, phases) code = @hegel_settings_set_phases_fn.call(ctx, s, phases) LibHegel.check!(self, ctx, code) nil end |
#settings_set_report_multiple_failures(ctx, s, yes) ⇒ nil
335 336 337 338 339 |
# File 'lib/hegel/lib_hegel/real.rb', line 335 def settings_set_report_multiple_failures(ctx, s, yes) code = @hegel_settings_set_report_multiple_failures_fn.call(ctx, s, yes) LibHegel.check!(self, ctx, code) nil end |
#settings_set_seed(ctx, s, seed, has_seed) ⇒ nil
307 308 309 310 311 |
# File 'lib/hegel/lib_hegel/real.rb', line 307 def settings_set_seed(ctx, s, seed, has_seed) code = @hegel_settings_set_seed_fn.call(ctx, s, seed, has_seed) LibHegel.check!(self, ctx, code) nil end |
#settings_set_stateful_step_count(ctx, s, n) ⇒ nil
329 330 331 332 333 |
# File 'lib/hegel/lib_hegel/real.rb', line 329 def settings_set_stateful_step_count(ctx, s, n) code = @hegel_settings_set_stateful_step_count_fn.call(ctx, s, n) LibHegel.check!(self, ctx, code) nil end |
#settings_set_suppress_health_check(ctx, s, checks) ⇒ nil
checks is a bitwise OR of the HEGEL_HC_* constants. Each call
overwrites the previous suppressions, per the header.
359 360 361 362 363 |
# File 'lib/hegel/lib_hegel/real.rb', line 359 def settings_set_suppress_health_check(ctx, s, checks) code = @hegel_settings_set_suppress_health_check_fn.call(ctx, s, checks) LibHegel.check!(self, ctx, code) nil end |
#settings_set_test_cases(ctx, s, n) ⇒ nil
295 296 297 298 299 |
# File 'lib/hegel/lib_hegel/real.rb', line 295 def settings_set_test_cases(ctx, s, n) code = @hegel_settings_set_test_cases_fn.call(ctx, s, n) LibHegel.check!(self, ctx, code) nil end |
#settings_set_verbosity(ctx, s, v) ⇒ nil
301 302 303 304 305 |
# File 'lib/hegel/lib_hegel/real.rb', line 301 def settings_set_verbosity(ctx, s, v) code = @hegel_settings_set_verbosity_fn.call(ctx, s, v) LibHegel.check!(self, ctx, code) nil end |
#start_span(ctx, tc, label) ⇒ nil
Opens a span labelled label (one of the HEGEL_LABEL_* constants,
or a caller-defined value that avoids them). Must be paired with
exactly one #stop_span call, per the header.
595 596 597 598 599 |
# File 'lib/hegel/lib_hegel/real.rb', line 595 def start_span(ctx, tc, label) code = @hegel_start_span_fn.call(ctx, tc, label) LibHegel.check!(self, ctx, code) nil end |
#state_machine_free(ctx, state_machine) ⇒ nil
No-op when state_machine is nil, matching
hegel_state_machine_free's documented no-op-on-NULL contract; not
translated, for the same reason as #context_free.
755 756 757 758 |
# File 'lib/hegel/lib_hegel/real.rb', line 755 def state_machine_free(ctx, state_machine) @hegel_state_machine_free_fn.call(ctx, state_machine) nil end |
#state_machine_next_rule(ctx, tc, state_machine) ⇒ Integer
Returns the index (in 0...num_rules) of the next stateful-testing rule to run, or HEGEL_STATE_MACHINE_DONE (-1) once +state_machine+'s step budget is exhausted -- returned as the raw sentinel value, not translated to nil. Unlike #next_test_case's out-parameter, which is NULL (no value) at the equivalent boundary, the header documents this out-parameter as holding a real value, -1, at that point; a caller comparing against HEGEL_STATE_MACHINE_DONE is the layer that should decide what that value means, the same way #run_result_status hands back its raw HEGEL_RUN_STATUS_* value unexamined.
734 735 736 737 738 739 |
# File 'lib/hegel/lib_hegel/real.rb', line 734 def state_machine_next_rule(ctx, tc, state_machine) out = FFI::MemoryPointer.new(:int64) code = @hegel_state_machine_next_rule_fn.call(ctx, tc, state_machine, out) LibHegel.check!(self, ctx, code) out.read_int64 end |
#state_machine_rule_rejected(ctx, tc, state_machine) ⇒ nil
Reports the rule most recently returned by #state_machine_next_rule as rejected (an assumption failed before it completed), so it does not count toward the step budget. Raises HEGEL_E_INVALID_ARG (via LibHegel.check!, translated to Hegel::Error) when no rule is outstanding, per the header.
746 747 748 749 750 |
# File 'lib/hegel/lib_hegel/real.rb', line 746 def state_machine_rule_rejected(ctx, tc, state_machine) code = @hegel_state_machine_rule_rejected_fn.call(ctx, tc, state_machine) LibHegel.check!(self, ctx, code) nil end |
#stop_span(ctx, tc, discard) ⇒ nil
Closes the most recently opened span. discard true marks it
rejected, so libhegel retries from before the span opened.
603 604 605 606 607 |
# File 'lib/hegel/lib_hegel/real.rb', line 603 def stop_span(ctx, tc, discard) code = @hegel_stop_span_fn.call(ctx, tc, discard) LibHegel.check!(self, ctx, code) nil end |
#string_generator_domain(ctx, max_length) ⇒ Object
Returns a caller-owned string generator handle producing
fully-qualified domain names, released the same way as
#string_generator_text. max_length is the total FQDN length; the
header documents it as valid in 4..=255. This layer does not
validate that range itself, only translates the
HEGEL_E_INVALID_ARG the engine returns outside it, the same
division of labor #settings_set_database and every other setter
above already follows.
912 913 914 915 916 917 |
# File 'lib/hegel/lib_hegel/real.rb', line 912 def string_generator_domain(ctx, max_length) out = FFI::MemoryPointer.new(:pointer) code = @hegel_string_generator_domain_fn.call(ctx, max_length, out) LibHegel.check!(self, ctx, code) out.read_pointer end |
#string_generator_email(ctx) ⇒ Object
Returns a caller-owned string generator handle producing RFC 5321/5322 email addresses, released the same way as #string_generator_text.
888 889 890 891 892 893 |
# File 'lib/hegel/lib_hegel/real.rb', line 888 def string_generator_email(ctx) out = FFI::MemoryPointer.new(:pointer) code = @hegel_string_generator_email_fn.call(ctx, out) LibHegel.check!(self, ctx, code) out.read_pointer end |
#string_generator_free(ctx, generator) ⇒ nil
No-op when generator is nil, matching
hegel_string_generator_free's documented no-op-on-NULL contract;
not translated, for the same reason as #context_free.
795 796 797 798 |
# File 'lib/hegel/lib_hegel/real.rb', line 795 def string_generator_free(ctx, generator) @hegel_string_generator_free_fn.call(ctx, generator) nil end |
#string_generator_regex(ctx, pattern, fullmatch, alphabet = nil) ⇒ Object
Returns a caller-owned string generator handle matching pattern
(Python re syntax), released the same way as #string_generator_text:
with #string_generator_free. alphabet is an optional string
generator handle (built via #string_generator_text, scoped with
Hegel::TestCase#with_text_generator) whose character set constrains the
padding and wildcard characters; nil (the default) marshals to NULL,
the header's documented "no particular alphabet" case.
878 879 880 881 882 883 |
# File 'lib/hegel/lib_hegel/real.rb', line 878 def string_generator_regex(ctx, pattern, fullmatch, alphabet = nil) out = FFI::MemoryPointer.new(:pointer) code = @hegel_string_generator_regex_fn.call(ctx, pattern, fullmatch, alphabet, out) LibHegel.check!(self, ctx, code) out.read_pointer end |
#string_generator_text(ctx, min_size:, max_size:, codec: nil, min_codepoint: 0, max_codepoint: 0xFFFFFFFF) ⇒ Object
Returns a caller-owned string generator handle, released
separately with #string_generator_free (or scoped with
Hegel::TestCase#with_text_generator). categories,
exclude_categories, include_characters, and
exclude_characters are always passed as NULL/0 below: this call
only wires up the codepoint-range constraints of the 14-argument
bind; the category and explicit-character filter arguments are a
later generator's scope, layered on top of this same bind.
781 782 783 784 785 786 787 788 789 790 |
# File 'lib/hegel/lib_hegel/real.rb', line 781 def string_generator_text(ctx, min_size:, max_size:, codec: nil, min_codepoint: 0, max_codepoint: 0xFFFFFFFF) out = FFI::MemoryPointer.new(:pointer) code = @hegel_string_generator_text_fn.call( ctx, min_size, max_size, codec, min_codepoint, max_codepoint, nil, 0, nil, 0, nil, 0, nil, 0, out ) LibHegel.check!(self, ctx, code) out.read_pointer end |
#string_generator_url(ctx) ⇒ Object
Returns a caller-owned string generator handle producing RFC 3986 http/https URLs, released the same way as #string_generator_text.
897 898 899 900 901 902 |
# File 'lib/hegel/lib_hegel/real.rb', line 897 def string_generator_url(ctx) out = FFI::MemoryPointer.new(:pointer) code = @hegel_string_generator_url_fn.call(ctx, out) LibHegel.check!(self, ctx, code) out.read_pointer end |
#target(ctx, tc, value, label) ⇒ nil
Records a numeric observation under label for libhegel's own
hill-climbing between generation rounds. The header documents this
as a no-op unless HEGEL_PHASE_TARGET is enabled (the default), and
a label as recordable at most once per test case; neither is
checked here, matching how this layer leaves every other
argument-shape rule to the engine's own HEGEL_E_INVALID_ARG.
420 421 422 423 424 |
# File 'lib/hegel/lib_hegel/real.rb', line 420 def target(ctx, tc, value, label) code = @hegel_target_fn.call(ctx, tc, value, label) LibHegel.check!(self, ctx, code) nil end |
#test_case_free(ctx, tc) ⇒ nil
No-op when tc is nil, matching hegel_test_case_free's documented
no-op-on-NULL contract; not translated, for the same reason as
#context_free.
399 400 401 402 |
# File 'lib/hegel/lib_hegel/real.rb', line 399 def test_case_free(ctx, tc) @hegel_test_case_free_fn.call(ctx, tc) nil end |
#test_case_from_blob(ctx, settings, blob) ⇒ Object
Replays blob (from #failure_reproduction_blob) against settings
with no run handle and no run loop involved, per the header.
callback and user_data are always NULL here, for the same reason as
#run_start. blob is declared :string, the same as
#settings_set_database's own const char* argument. Raises
HEGEL_E_INVALID_ARG (via LibHegel.check!) for a blob that is
corrupt, non-UTF-8, or from an incompatible Hegel version.
A blob whose choices no longer match the caller's generators is a different case, and the header places it elsewhere: it "returns HEGEL_E_STOP_TEST from the draw that overruns", so the replay is built here and fails later, inside the body. Measured against 0.32.5, replaying a two-draw blob against a five-draw body builds fine and overruns at a draw.
529 530 531 532 533 534 |
# File 'lib/hegel/lib_hegel/real.rb', line 529 def test_case_from_blob(ctx, settings, blob) out = FFI::MemoryPointer.new(:pointer) code = @hegel_test_case_from_blob_fn.call(ctx, settings, blob, nil, nil, out) LibHegel.check!(self, ctx, code) out.read_pointer end |
#version(ctx) ⇒ String
Returns the loaded engine's version string, or raises the exception LibHegel.check! translates this call's result code to.
269 270 271 272 273 274 |
# File 'lib/hegel/lib_hegel/real.rb', line 269 def version(ctx) out = FFI::MemoryPointer.new(:pointer) code = @hegel_version_fn.call(ctx, out) LibHegel.check!(self, ctx, code) utf8(out.read_pointer) end |