Module: Hegel
- Defined in:
- lib/hegel.rb,
lib/hegel/errors.rb,
lib/hegel/locate.rb,
lib/hegel/report.rb,
lib/hegel/runner.rb,
lib/hegel/version.rb,
lib/hegel/settings.rb,
lib/hegel/stateful.rb,
lib/hegel/draw_name.rb,
lib/hegel/generator.rb,
lib/hegel/lib_hegel.rb,
lib/hegel/test_case.rb,
lib/hegel/generators.rb,
lib/hegel/state_machine.rb,
lib/hegel/stateful/pool.rb,
lib/hegel/lib_hegel/real.rb,
lib/hegel/syntax/methods.rb,
lib/hegel/libhegel_version.rb,
sig/hegel.rbs
Overview
Hegel is a property-based testing library for Ruby. It drives libhegel, the native engine that also backs the Rust, Go, TypeScript, Java, OCaml, and C++ implementations of the Hegel protocol.
The gem is named hegeltest while the namespace and require path are
hegel, mirroring the Rust implementation: the published crate is
hegeltest and callers write use hegel::....
Defined Under Namespace
Modules: DrawName, Generators, LibHegel, Locate, Report, Runner, Settings, Stateful, Syntax Classes: AssumeFailed, Error, Generator, StateMachine, StopTest, TestCase
Constant Summary collapse
- FATAL_EXCEPTIONS =
Say the process is ending, not that a property failed. Every
rescue Exceptionin this library re-raises one of these before doing anything else: catching one as a counterexample would have the engine spend its shrink budget minimising an interrupt instead of letting the process exit, and NoMemoryError in particular must not be answered with another native call.Here rather than beside the run loop because two files now rescue Exception -- Hegel::Runner and Hegel::Stateful -- and this is the exception vocabulary both of them need, which is what this file is for.
[Interrupt, SignalException, SystemExit, NoMemoryError].freeze
- VERSION =
"0.1.0"- LIBHEGEL_VERSION =
The libhegel engine release these bindings target. This is independent of Hegel::VERSION (the gem's own release): hegel-go and hegel-typescript keep the two separate too, so bumping the pinned engine can land as its own commit without also releasing a new gem version.
"0.32.5"
Class Method Summary collapse
-
.default_impl ⇒ Object
The Hegel::LibHegel::Real instance #test uses by default, built once and reused: Hegel::LibHegel::Real#initialize opens the native library, and that should not happen again on every #test call in a process that calls it more than once.
-
.test(test_cases: nil, seed: nil, derandomize: nil, verbosity: nil, database: nil, database_key: nil, phases: nil, suppress_health_check: nil, report_multiple_failures: false, stateful_step_count: nil, output: $stderr, reproduce_failure: nil, impl: default_impl) {|arg0| ... } ⇒ nil
Runs
blockas a Hegel property, drawing test cases from thetcit yields (see Hegel::TestCase).
Class Method Details
.default_impl ⇒ Object
The Hegel::LibHegel::Real instance #test uses by default, built once and
reused: Hegel::LibHegel::Real#initialize opens the native library, and
that should not happen again on every #test call in a process that calls
it more than once. Building it here rather than at load time also means a
caller who always passes their own impl: never opens the library.
89 90 91 |
# File 'lib/hegel.rb', line 89 def default_impl @default_impl ||= LibHegel::Real.new end |
.test(test_cases: nil, seed: nil, derandomize: nil, verbosity: nil, database: nil, database_key: nil, phases: nil, suppress_health_check: nil, report_multiple_failures: false, stateful_step_count: nil, output: $stderr, reproduce_failure: nil, impl: default_impl) {|arg0| ... } ⇒ nil
Runs block as a Hegel property, drawing test cases from the tc it
yields (see Hegel::TestCase). Returns nil on a passing run. On a failing
run, writes a failure report to output (see Hegel::Report) and then
re-raises the exception the smallest failing case's body raised, class
and backtrace intact, so a host test framework reports it as its own
assertion failure rather than as a Hegel-specific one. Raises
Hegel::Error for a run-level failure instead of a property failure.
test_cases, seed, derandomize, verbosity, phases, and
suppress_health_check all default to nil, which means the same thing
for each of them: do not call the matching libhegel setter, and let the
engine's own default apply instead. See Hegel::Settings for the
keyword-to-setter mapping, the verbosity Symbols it accepts, and the
+phases+/+suppress_health_check+ Symbols each of those two accepts (as
an Array; an empty Array raises Hegel::Error rather than silently
meaning "none"). verbosity: :quiet also silences the failure report
itself, not just libhegel's own progress output.
database and database_key opt a run into libhegel's example
database: database_key is the switch, database only means something
alongside it. Left at their shared default (nil, nil), a run stores
nothing, matching every Hegel.test call before these two keywords
existed. Given a String, database_key scopes what a run stores and
replays -- make it unique to the property, since two properties sharing
a key share one replay scope. database then chooses the directory
(libhegel's own default, ./.hegel/examples/ outside CI, if left nil
alongside a key). Passing database without database_key raises
Hegel::Error. See docs/adr/0009 for the decision and the measurements
behind it.
report_multiple_failures defaults to false, not nil, unlike every
keyword above: see Hegel::Runner.run's own comment for why departing
from libhegel's own default (true) is itself the decision here.
stateful_step_count bounds how many rules a Hegel::Stateful.run call
applies per test case; left nil (the default) leaves libhegel's own
default of 50 in place. hegel.h documents it as needing to be at least
1; like +tc.target+'s label, that requirement is left to the engine
rather than re-checked here.
output (default $stderr) is where a failure report is written; a
caller passes its own IO to capture that report instead (tests do).
reproduce_failure, when given, replays the single case that blob
(printed at the end of an earlier failure report) encodes, instead of
starting a run: test_cases and the other run-shaping keywords above
have nothing to bound in that case. See Hegel::Runner.reproduce.
impl exists for tests: it lets Hegel::LibHegel::Fake stand in for the
real engine. Ordinary callers never pass it. Its default expression
(#default_impl) only runs when impl is not given, so a test that does
pass one never opens the native library at all.
75 76 77 78 79 80 81 82 |
# File 'lib/hegel.rb', line 75 def test(test_cases: nil, seed: nil, derandomize: nil, verbosity: nil, database: nil, database_key: nil, phases: nil, suppress_health_check: nil, report_multiple_failures: false, stateful_step_count: nil, output: $stderr, reproduce_failure: nil, impl: default_impl, &block) Runner.run(impl: impl, test_cases: test_cases, seed: seed, derandomize: derandomize, verbosity: verbosity, database: database, database_key: database_key, phases: phases, suppress_health_check: suppress_health_check, report_multiple_failures: report_multiple_failures, stateful_step_count: stateful_step_count, output: output, reproduce_failure: reproduce_failure, &block) end |