hegeltest 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +22 -0
  3. data/CODE_OF_CONDUCT.md +10 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +264 -0
  6. data/Rakefile +19 -0
  7. data/docs/README.md +25 -0
  8. data/docs/adr/0001-bind-libhegel-through-fiddle.md +54 -0
  9. data/docs/adr/0002-ship-one-prebuilt-engine-per-platform-specific-gem.md +48 -0
  10. data/docs/adr/0003-publish-as-hegeltest-require-as-hegel.md +39 -0
  11. data/docs/adr/0004-expose-generators-through-a-mixin-with-keyword-options.md +42 -0
  12. data/docs/adr/0005-name-drawn-values-from-the-callers-source-with-prism.md +40 -0
  13. data/docs/adr/0006-verify-the-binding-in-seven-layers-with-full-coverage.md +51 -0
  14. data/docs/adr/0007-ship-a-thin-ruby-skill-shaped-for-donation.md +56 -0
  15. data/docs/adr/0008-revisit-the-binding-after-milestone-c-on-measurement.md +81 -0
  16. data/docs/adr/0009-turn-the-example-database-on-with-a-key.md +89 -0
  17. data/docs/adr/0010-declare-stateful-rules-with-a-class-macro.md +113 -0
  18. data/docs/adr/0011-let-the-test-case-own-every-pool-drawn-from-it.md +83 -0
  19. data/docs/adr/0012-build-a-failure-origin-from-the-callers-own-frame.md +72 -0
  20. data/docs/adr/0013-bind-libhegel-through-the-ffi-gem.md +102 -0
  21. data/docs/architecture.md +182 -0
  22. data/lib/hegel/draw_name.rb +109 -0
  23. data/lib/hegel/errors.rb +47 -0
  24. data/lib/hegel/generator.rb +98 -0
  25. data/lib/hegel/generators.rb +865 -0
  26. data/lib/hegel/lib_hegel/real.rb +1149 -0
  27. data/lib/hegel/lib_hegel.rb +269 -0
  28. data/lib/hegel/libhegel_version.rb +9 -0
  29. data/lib/hegel/locate.rb +188 -0
  30. data/lib/hegel/report.rb +87 -0
  31. data/lib/hegel/runner.rb +464 -0
  32. data/lib/hegel/settings.rb +164 -0
  33. data/lib/hegel/state_machine.rb +89 -0
  34. data/lib/hegel/stateful/pool.rb +111 -0
  35. data/lib/hegel/stateful.rb +120 -0
  36. data/lib/hegel/syntax/methods.rb +173 -0
  37. data/lib/hegel/test_case.rb +523 -0
  38. data/lib/hegel/version.rb +5 -0
  39. data/lib/hegel.rb +92 -0
  40. data/lib/hegeltest.rb +7 -0
  41. data/lib/tasks/libhegel.rake +112 -0
  42. data/lib/tasks/platform_gems.rake +111 -0
  43. data/sig/hegel.rbs +563 -0
  44. data/skills/hegel-ruby/SKILL.md +30 -0
  45. data/skills/hegel-ruby/references/ruby/reference.md +1210 -0
  46. metadata +113 -0
data/sig/hegel.rbs ADDED
@@ -0,0 +1,563 @@
1
+ module Hegel
2
+ VERSION: String
3
+ LIBHEGEL_VERSION: String
4
+ FATAL_EXCEPTIONS: Array[singleton(Exception)]
5
+
6
+ class Error < StandardError
7
+ end
8
+
9
+ class StopTest < Exception
10
+ end
11
+
12
+ class AssumeFailed < Exception
13
+ end
14
+
15
+ def self.test: (?test_cases: Integer?, ?seed: Integer?, ?derandomize: bool?, ?verbosity: Symbol?, ?database: String?, ?database_key: String?, ?phases: Array[Symbol]?, ?suppress_health_check: Array[Symbol]?, ?report_multiple_failures: bool, ?stateful_step_count: Integer?, ?output: untyped, ?reproduce_failure: String?, ?impl: untyped) { (TestCase) -> void } -> nil
16
+ def self.default_impl: () -> untyped
17
+
18
+ class TestCase
19
+ DEFAULT_DRAW_NAME: String
20
+ DRAW_CALLER_DEPTH: Integer
21
+
22
+ def initialize: (untyped impl, untyped ctx, untyped handle, ?record: bool) -> void
23
+ def entries: () -> Array[[:draw, String, untyped] | [:note, untyped]]?
24
+ def draw_integer: (Integer min_value, Integer max_value, ?label: String?) -> Integer
25
+ def draw_boolean: (?Float p, ?label: String?) -> bool
26
+ def draw: (Generator generator, ?label: String?) -> untyped
27
+ def assume: (boolish condition) -> void
28
+ def reject: () -> bot
29
+ def note: (?untyped? message) ?{ () -> untyped } -> void
30
+ def generate_integer: (Integer min_value, Integer max_value) -> Integer
31
+ def generate_integer_big: (Integer min_value, Integer max_value) -> Integer
32
+ def generate_boolean: (?Float p) -> bool
33
+ def generate_float: (Integer width, Float min_value, Float max_value, allow_nan: bool, allow_infinity: bool, exclude_min: bool, exclude_max: bool, smallest_nonzero_magnitude: Float) -> Float
34
+ def target: (Numeric value, ?label: String) -> nil
35
+ def start_span: (Integer label) -> untyped
36
+ def stop_span: (?discard: bool) -> untyped
37
+ def new_collection: (Integer min_size, Integer max_size) -> untyped
38
+ def collection_more: (untyped collection) -> bool
39
+ def collection_reject: (untyped collection, ?why: String?) -> untyped
40
+ def collection_free: (untyped collection) -> untyped
41
+ def with_text_generator: [R] (min_size: Integer, max_size: Integer, ?codec: String?, ?min_codepoint: Integer, ?max_codepoint: Integer) { (untyped) -> R } -> R
42
+ def with_regex_generator: [R] (String pattern, fullmatch: bool) { (untyped) -> R } -> R
43
+ def with_email_generator: [R] () { (untyped) -> R } -> R
44
+ def with_url_generator: [R] () { (untyped) -> R } -> R
45
+ def with_domain_generator: [R] (max_length: Integer) { (untyped) -> R } -> R
46
+ def generate_string: (untyped generator) -> String
47
+ def generate_bytes: (Integer min_size, Integer max_size) -> String
48
+ def generate_ipv4: () -> String
49
+ def generate_ipv6: () -> String
50
+ def generate_uuid: (Integer version, bool has_version) -> String
51
+ def string_generator_regex: (String pattern, bool fullmatch, ?untyped alphabet) -> untyped
52
+ def string_generator_email: () -> untyped
53
+ def string_generator_url: () -> untyped
54
+ def string_generator_domain: (Integer max_length) -> untyped
55
+ def string_generator_free: (untyped generator) -> nil
56
+ def generate_date: (Array[Integer] min_value, Array[Integer] max_value) -> Array[Integer]
57
+ def generate_time: (Array[Integer] min_value, Array[Integer] max_value) -> Array[Integer]
58
+ def generate_datetime: (Array[Integer] min_date, Array[Integer] min_time, Array[Integer] max_date, Array[Integer] max_time) -> [Array[Integer], Array[Integer]]
59
+ def new_pool: () -> untyped
60
+ def pool_add: (untyped pool) -> Integer
61
+ def pool_generate: (untyped pool, bool consume) -> Integer
62
+ def pool_free: (untyped pool) -> nil
63
+ def free_pools: () -> void
64
+ def new_state_machine: (Array[String] rule_names, Array[String] invariant_names) -> untyped
65
+ def state_machine_next_rule: (untyped state_machine) -> Integer
66
+ def state_machine_rule_rejected: (untyped state_machine) -> nil
67
+ def state_machine_free: (untyped state_machine) -> nil
68
+ end
69
+
70
+ class Generator
71
+ def do_draw: (untyped tc) -> untyped
72
+ def map: () { (untyped) -> untyped } -> Generator
73
+ def filter: () { (untyped) -> boolish } -> Generator
74
+
75
+ class Mapped < Generator
76
+ def initialize: (Generator source, Proc block) -> void
77
+ def do_draw: (untyped tc) -> untyped
78
+ end
79
+
80
+ class Filtered < Generator
81
+ MAX_ATTEMPTS: Integer
82
+
83
+ def initialize: (Generator source, Proc block) -> void
84
+ def do_draw: (untyped tc) -> untyped
85
+ end
86
+ end
87
+
88
+ module Generators
89
+ class BooleanGenerator < Generator
90
+ def initialize: (p: Float) -> void
91
+ def do_draw: (untyped tc) -> bool
92
+ end
93
+
94
+ class IntegerGenerator < Generator
95
+ INT64_MIN: Integer
96
+ INT64_MAX: Integer
97
+
98
+ def initialize: (min_value: Integer?, max_value: Integer?) -> void
99
+ def do_draw: (untyped tc) -> Integer
100
+ end
101
+
102
+ class FloatGenerator < Generator
103
+ WIDTH: Integer
104
+
105
+ def initialize: (min_value: Float?, max_value: Float?, allow_nan: bool, allow_infinity: bool, exclude_min: bool, exclude_max: bool) -> void
106
+ def do_draw: (untyped tc) -> Float
107
+ end
108
+
109
+ class TextGenerator < Generator
110
+ def initialize: (min_size: Integer, max_size: Integer?, codec: String?, min_codepoint: Integer?, max_codepoint: Integer?) -> void
111
+ def do_draw: (untyped tc) -> String
112
+ end
113
+
114
+ class ArrayGenerator < Generator
115
+ def initialize: (Generator elements, min_size: Integer, max_size: Integer?) -> void
116
+ def do_draw: (untyped tc) -> Array[untyped]
117
+
118
+ private
119
+
120
+ def draw_elements: (untyped tc, Integer max_size) -> Array[untyped]
121
+ def draw_element: (untyped tc) -> untyped
122
+ end
123
+
124
+ class JustGenerator < Generator
125
+ def initialize: (untyped value) -> void
126
+ def do_draw: (untyped tc) -> untyped
127
+ end
128
+
129
+ class SampledFromGenerator < Generator
130
+ def initialize: (untyped collection) -> void
131
+ def do_draw: (untyped tc) -> untyped
132
+ end
133
+
134
+ class OneOfGenerator < Generator
135
+ def initialize: (Array[Generator] generators) -> void
136
+ def do_draw: (untyped tc) -> untyped
137
+ end
138
+
139
+ class OptionalGenerator < Generator
140
+ def initialize: (Generator generator) -> void
141
+ def do_draw: (untyped tc) -> untyped
142
+ end
143
+
144
+ class TupleGenerator < Generator
145
+ def initialize: (Array[Generator] generators) -> void
146
+ def do_draw: (untyped tc) -> Array[untyped]
147
+ end
148
+
149
+ class SetGenerator < Generator
150
+ def initialize: (Generator elements, min_size: Integer, max_size: Integer?) -> void
151
+ def do_draw: (untyped tc) -> Set[untyped]
152
+
153
+ private
154
+
155
+ def draw_elements: (untyped tc, Integer max_size) -> Set[untyped]
156
+ def draw_element: (untyped tc) -> untyped
157
+ end
158
+
159
+ class HashGenerator < Generator
160
+ def initialize: (Generator keys, Generator values, min_size: Integer, max_size: Integer?) -> void
161
+ def do_draw: (untyped tc) -> Hash[untyped, untyped]
162
+
163
+ private
164
+
165
+ def draw_entries: (untyped tc, Integer max_size) -> Hash[untyped, untyped]
166
+ def draw_entry: (untyped tc) -> [untyped, untyped]
167
+ end
168
+
169
+ class CharactersGenerator < Generator
170
+ def initialize: (codec: String?, min_codepoint: Integer?, max_codepoint: Integer?) -> void
171
+ def do_draw: (untyped tc) -> String
172
+ end
173
+
174
+ class BinaryGenerator < Generator
175
+ def initialize: (min_size: Integer, max_size: Integer?) -> void
176
+ def do_draw: (untyped tc) -> String
177
+ end
178
+
179
+ class FromRegexGenerator < Generator
180
+ def initialize: (untyped pattern, fullmatch: bool) -> void
181
+ def do_draw: (untyped tc) -> String
182
+ end
183
+
184
+ class EmailsGenerator < Generator
185
+ def do_draw: (untyped tc) -> String
186
+ end
187
+
188
+ class UrlsGenerator < Generator
189
+ def do_draw: (untyped tc) -> String
190
+ end
191
+
192
+ class DomainsGenerator < Generator
193
+ def initialize: (max_length: Integer) -> void
194
+ def do_draw: (untyped tc) -> String
195
+ end
196
+
197
+ class IpAddressesGenerator < Generator
198
+ def initialize: (v4: bool, v6: bool) -> void
199
+ def do_draw: (untyped tc) -> untyped
200
+
201
+ private
202
+
203
+ def draw_address: (untyped tc) -> untyped
204
+ end
205
+
206
+ class UuidsGenerator < Generator
207
+ def initialize: (version: Integer?) -> void
208
+ def do_draw: (untyped tc) -> String
209
+ end
210
+
211
+ class DatesGenerator < Generator
212
+ MIN_DATE: untyped
213
+ MAX_DATE: untyped
214
+
215
+ def initialize: (min_value: untyped, max_value: untyped) -> void
216
+ def do_draw: (untyped tc) -> untyped
217
+ end
218
+
219
+ class TimesGenerator < Generator
220
+ MIDNIGHT: String
221
+ LAST_MICROSECOND: String
222
+ FORMAT: Regexp
223
+
224
+ def initialize: (min_value: String?, max_value: String?) -> void
225
+ def do_draw: (untyped tc) -> String
226
+
227
+ private
228
+
229
+ def parse: (untyped value, String name) -> Array[Integer]
230
+ end
231
+
232
+ class DatetimesGenerator < Generator
233
+ MIN_DATETIME: Time
234
+ MAX_DATETIME: Time
235
+
236
+ def initialize: (min_value: Time?, max_value: Time?) -> void
237
+ def do_draw: (untyped tc) -> Time
238
+ end
239
+
240
+ class CompositeGenerator < Generator
241
+ def initialize: () { (CompositeGenerator::BlockTestCase) -> untyped } -> void
242
+ def do_draw: (untyped tc) -> untyped
243
+
244
+ class BlockTestCase
245
+ def initialize: (untyped tc) -> void
246
+ def draw: (Generator generator) -> untyped
247
+ def draw_integer: (Integer min_value, Integer max_value) -> Integer
248
+ def draw_boolean: (?Float p) -> bool
249
+ end
250
+ end
251
+
252
+ class DeferredGenerator < Generator
253
+ def initialize: () -> void
254
+ def set: (Generator generator) -> void
255
+ def do_draw: (untyped tc) -> untyped
256
+ end
257
+
258
+ extend Syntax::Methods
259
+ end
260
+
261
+ class StateMachine
262
+ include Syntax::Methods
263
+
264
+ def self.rule: (Symbol | String name) { (TestCase) -> untyped } -> void
265
+ def self.invariant: (Symbol | String name) { (TestCase) -> untyped } -> void
266
+ def self.rule_definitions: () -> Hash[String, Proc]
267
+ def self.invariant_definitions: () -> Hash[String, Proc]
268
+ end
269
+
270
+ module Stateful
271
+ def self.run: (StateMachine machine, TestCase tc) -> void
272
+ def self.drive: (StateMachine machine, Hash[String, Proc] rules, Array[String] rule_names, Hash[String, Proc] invariants, untyped state_machine, TestCase tc) -> void
273
+ def self.apply_rule: (StateMachine machine, Proc block, Hash[String, Proc] invariants, untyped state_machine, TestCase tc) -> void
274
+ def self.run_invariants: (StateMachine machine, Hash[String, Proc] invariants, TestCase tc) -> void
275
+
276
+ class Pool
277
+ def initialize: (TestCase tc) -> void
278
+ def size: () -> Integer
279
+ def empty?: () -> bool
280
+ def add: (untyped value) -> Pool
281
+ def values_reusable: () -> ValuesReusable
282
+ def values_consumed: () -> ValuesConsumed
283
+
284
+ class ValuesReusable < Generator
285
+ def initialize: (untyped pool, Hash[Integer, untyped] values) -> void
286
+ def do_draw: (untyped tc) -> untyped
287
+ end
288
+
289
+ class ValuesConsumed < Generator
290
+ def initialize: (untyped pool, Hash[Integer, untyped] values) -> void
291
+ def do_draw: (untyped tc) -> untyped
292
+ end
293
+ end
294
+ end
295
+
296
+ module Syntax
297
+ module Methods
298
+ def booleans: (?p: Float) -> Generators::BooleanGenerator
299
+ def integers: (?min_value: Integer?, ?max_value: Integer?) -> Generators::IntegerGenerator
300
+ def floats: (?min_value: Float?, ?max_value: Float?, ?allow_nan: bool, ?allow_infinity: bool, ?exclude_min: bool, ?exclude_max: bool) -> Generators::FloatGenerator
301
+ def text: (?min_size: Integer, ?max_size: Integer?, ?codec: String?, ?min_codepoint: Integer?, ?max_codepoint: Integer?) -> Generators::TextGenerator
302
+ def arrays: (Generator elements, ?min_size: Integer, ?max_size: Integer?) -> Generators::ArrayGenerator
303
+ def just: (untyped value) -> Generators::JustGenerator
304
+ def sampled_from: (untyped collection) -> Generators::SampledFromGenerator
305
+ def one_of: (*Generator generators) -> Generators::OneOfGenerator
306
+ def optional: (Generator generator) -> Generators::OptionalGenerator
307
+ def tuples: (*Generator generators) -> Generators::TupleGenerator
308
+ def sets: (Generator elements, ?min_size: Integer, ?max_size: Integer?) -> Generators::SetGenerator
309
+ def hashes: (Generator keys, Generator values, ?min_size: Integer, ?max_size: Integer?) -> Generators::HashGenerator
310
+ def characters: (?codec: String?, ?min_codepoint: Integer?, ?max_codepoint: Integer?) -> Generators::CharactersGenerator
311
+ def binary: (?min_size: Integer, ?max_size: Integer?) -> Generators::BinaryGenerator
312
+ def from_regex: (untyped pattern, ?fullmatch: bool) -> Generators::FromRegexGenerator
313
+ def emails: () -> Generators::EmailsGenerator
314
+ def urls: () -> Generators::UrlsGenerator
315
+ def domains: (?max_length: Integer) -> Generators::DomainsGenerator
316
+ def ip_addresses: (?v4: bool, ?v6: bool) -> Generators::IpAddressesGenerator
317
+ def uuids: (?version: Integer?) -> Generators::UuidsGenerator
318
+ def dates: (?min_value: untyped, ?max_value: untyped) -> Generators::DatesGenerator
319
+ def times: (?min_value: String?, ?max_value: String?) -> Generators::TimesGenerator
320
+ def datetimes: (?min_value: Time?, ?max_value: Time?) -> Generators::DatetimesGenerator
321
+ def composite: () { (Generators::CompositeGenerator::BlockTestCase) -> untyped } -> Generators::CompositeGenerator
322
+ def deferred: () -> Generators::DeferredGenerator
323
+ end
324
+ end
325
+
326
+ module DrawName
327
+ ASSIGNMENT_NODE_TYPES: Array[untyped]
328
+
329
+ def self.for: (String path, Integer lineno) -> String?
330
+ def self.reset_cache: () -> void
331
+ def self.parse: (String path) -> untyped
332
+ def self.cache: () -> Hash[String, untyped]
333
+ def self.read_and_parse: (String path) -> untyped
334
+ def self.assignment_nodes: (untyped node, ?Array[untyped] matches) -> Array[untyped]
335
+ def self.covers?: (untyped location, Integer lineno) -> bool
336
+ end
337
+
338
+ module Report
339
+ class Failure
340
+ def initialize: (test_cases: Integer, discarded: Integer, entries: Array[[:draw, String, untyped] | [:note, untyped]], blob: String) -> void
341
+ def test_cases: () -> Integer
342
+ def discarded: () -> Integer
343
+ def entries: () -> Array[[:draw, String, untyped] | [:note, untyped]]
344
+ def blob: () -> String
345
+ end
346
+
347
+ def self.assign_names: (Array[[:draw, String, untyped] | [:note, untyped]] entries) -> Array[[:draw, String, untyped] | [:note, untyped]]
348
+ def self.render_failure: (Failure failure) -> String
349
+ def self.render: (Array[Failure] failures) -> String
350
+ end
351
+
352
+ module Settings
353
+ VERBOSITY_CODES: Hash[Symbol, Integer]
354
+ PHASE_CODES: Hash[Symbol, Integer]
355
+ HEALTH_CHECK_CODES: Hash[Symbol, Integer]
356
+
357
+ def self.apply: (untyped impl, untyped ctx, untyped settings, test_cases: Integer?, seed: Integer?, derandomize: bool?, verbosity: Symbol?, database: String?, database_key: String?, phases: Array[Symbol]?, suppress_health_check: Array[Symbol]?, report_multiple_failures: bool, stateful_step_count: Integer?) -> void
358
+ def self.apply_verbosity: (untyped impl, untyped ctx, untyped settings, Symbol verbosity) -> void
359
+ def self.apply_database: (untyped impl, untyped ctx, untyped settings, database: String?, database_key: String?) -> void
360
+ def self.apply_phases: (untyped impl, untyped ctx, untyped settings, Array[Symbol] phases) -> void
361
+ def self.apply_suppress_health_check: (untyped impl, untyped ctx, untyped settings, Array[Symbol] checks) -> void
362
+ def self.mask_for: (Array[Symbol] values, Hash[Symbol, Integer] codes) -> Integer
363
+ end
364
+
365
+ module Runner
366
+
367
+ UNKNOWN_ORIGIN: String
368
+ UNKNOWN_RUN_ERROR_MESSAGE: String
369
+ LIBRARY_DIR: String
370
+ INSTALLED_GEM_DIRS: Array[String]
371
+ STDLIB_DIR: String
372
+
373
+ class GenerationStats
374
+ def initialize: () -> void
375
+ def record: (Integer status, String? origin) -> void
376
+ def for: (String? origin) -> [Integer, Integer]
377
+ end
378
+
379
+ def self.run: (impl: untyped, ?test_cases: Integer?, ?seed: Integer?, ?derandomize: bool?, ?verbosity: Symbol?, ?database: String?, ?database_key: String?, ?phases: Array[Symbol]?, ?suppress_health_check: Array[Symbol]?, ?report_multiple_failures: bool, ?stateful_step_count: Integer?, ?output: untyped, ?reproduce_failure: String?) { (TestCase) -> void } -> nil
380
+ def self.run_and_finish: (untyped impl, untyped ctx, untyped settings, quiet: bool, output: untyped) { (TestCase) -> void } -> nil
381
+ def self.drive: (untyped impl, untyped ctx, untyped run, GenerationStats stats) { (TestCase) -> void } -> void
382
+ def self.with_test_case: [R] (untyped impl, untyped ctx, untyped tc, ?record: bool) { (TestCase) -> R } -> R
383
+ def self.run_case: (untyped impl, untyped ctx, untyped tc, GenerationStats stats) { (TestCase) -> void } -> void
384
+ def self.finish: (untyped impl, untyped ctx, untyped settings, untyped result, GenerationStats stats, quiet: bool, output: untyped) { (TestCase) -> void } -> nil
385
+ def self.replay: (untyped impl, untyped ctx, untyped settings, untyped result, GenerationStats stats, quiet: bool, output: untyped) { (TestCase) -> void } -> bot
386
+ def self.replay_failure: (untyped impl, untyped ctx, untyped settings, untyped failure, Integer index, GenerationStats stats) { (TestCase) -> void } -> [Exception, Report::Failure]
387
+ def self.reproduce: (untyped impl, untyped ctx, untyped settings, String blob, quiet: bool, output: untyped) { (TestCase) -> void } -> bot
388
+ def self.classify: (TestCase test_case) { (TestCase) -> void } -> [Integer, String?, Exception?, Array[[:draw, String, untyped] | [:note, untyped]]?]
389
+ def self.origin_for: (Exception exception) -> String
390
+ def self.infrastructure?: (String path) -> bool
391
+ def self.flaky_message: () -> String
392
+ def self.multiple_failures_message: (Integer count) -> String
393
+ end
394
+
395
+ module Locate
396
+ LIBRARY_PATH_ENV: String
397
+ ASSET_NAMES: Hash[String, String]
398
+ GEM_LIBHEGEL_DIR: String
399
+ DEFAULT_FINDER: ^(String, String) -> String?
400
+
401
+ def self.resolve: (?env: untyped, ?host_cpu: String, ?host_os: String, ?gem_dir: String, ?finder: ^(String, String) -> String?) -> String
402
+ def self.asset_name: (host_cpu: String, host_os: String) -> String
403
+ def self.from_env: (untyped, String, String) -> String?
404
+ def self.find_override: (String, String, String) -> String
405
+ def self.override_candidate_names: (String, String) -> Array[String]
406
+ def self.release_asset_name: (String, String) -> String?
407
+ def self.host_id: (String, String) -> String
408
+ def self.normalize_os: (String) -> String
409
+ def self.normalize_cpu: (String, String) -> String
410
+ def self.ext_of_os: (String) -> String
411
+ def self.unsupported_host_message: (String, String) -> String
412
+ def self.missing_bundle_message: (String, String) -> String
413
+ def self.missing_override_message: (String, Array[String]) -> String
414
+ end
415
+
416
+ module LibHegel
417
+ HEGEL_OK: Integer
418
+ HEGEL_E_STOP_TEST: Integer
419
+ HEGEL_E_ASSUME: Integer
420
+ HEGEL_E_BACKEND: Integer
421
+ HEGEL_E_INVALID_HANDLE: Integer
422
+ HEGEL_E_INVALID_ARG: Integer
423
+ HEGEL_E_ALREADY_COMPLETE: Integer
424
+ HEGEL_E_NOT_COMPLETE: Integer
425
+ HEGEL_E_INTERNAL: Integer
426
+ HEGEL_E_CONCURRENT_USE: Integer
427
+ CODE_NAMES: Hash[Integer, String]
428
+ HEGEL_STATUS_VALID: Integer
429
+ HEGEL_STATUS_INVALID: Integer
430
+ HEGEL_STATUS_OVERRUN: Integer
431
+ HEGEL_STATUS_INTERESTING: Integer
432
+ HEGEL_RUN_STATUS_PASSED: Integer
433
+ HEGEL_RUN_STATUS_FAILED: Integer
434
+ HEGEL_RUN_STATUS_ERROR: Integer
435
+ HEGEL_VERBOSITY_QUIET: Integer
436
+ HEGEL_VERBOSITY_NORMAL: Integer
437
+ HEGEL_VERBOSITY_VERBOSE: Integer
438
+ HEGEL_VERBOSITY_DEBUG: Integer
439
+ HEGEL_PHASE_EXPLICIT: Integer
440
+ HEGEL_PHASE_REUSE: Integer
441
+ HEGEL_PHASE_GENERATE: Integer
442
+ HEGEL_PHASE_TARGET: Integer
443
+ HEGEL_PHASE_SHRINK: Integer
444
+ HEGEL_PHASE_ALL: Integer
445
+ HEGEL_HC_FILTER_TOO_MUCH: Integer
446
+ HEGEL_HC_TOO_SLOW: Integer
447
+ HEGEL_HC_TEST_CASES_TOO_LARGE: Integer
448
+ HEGEL_HC_LARGE_INITIAL_TEST_CASE: Integer
449
+ HEGEL_LABEL_LIST: Integer
450
+ HEGEL_LABEL_LIST_ELEMENT: Integer
451
+ HEGEL_LABEL_SET: Integer
452
+ HEGEL_LABEL_SET_ELEMENT: Integer
453
+ HEGEL_LABEL_MAP: Integer
454
+ HEGEL_LABEL_MAP_ENTRY: Integer
455
+ HEGEL_LABEL_TUPLE: Integer
456
+ HEGEL_LABEL_ONE_OF: Integer
457
+ HEGEL_LABEL_OPTIONAL: Integer
458
+ HEGEL_LABEL_FIXED_DICT: Integer
459
+ HEGEL_LABEL_FLAT_MAP: Integer
460
+ HEGEL_LABEL_FILTER: Integer
461
+ HEGEL_LABEL_MAPPED: Integer
462
+ HEGEL_LABEL_SAMPLED_FROM: Integer
463
+ HEGEL_LABEL_ENUM_VARIANT: Integer
464
+ HEGEL_LABEL_FEATURE_FLAG: Integer
465
+ HEGEL_LABEL_REGEX: Integer
466
+ HEGEL_LABEL_EMAIL: Integer
467
+ HEGEL_LABEL_URL: Integer
468
+ HEGEL_LABEL_DOMAIN: Integer
469
+ HEGEL_LABEL_DATE: Integer
470
+ HEGEL_LABEL_TIME: Integer
471
+ HEGEL_LABEL_DATETIME: Integer
472
+ HEGEL_LABEL_UUID: Integer
473
+ HEGEL_LABEL_IP_ADDRESS: Integer
474
+ HEGEL_LABEL_INTEGER: Integer
475
+ HEGEL_LABEL_FLOAT: Integer
476
+ HEGEL_LABEL_BOOLEAN: Integer
477
+ HEGEL_LABEL_BYTES: Integer
478
+ HEGEL_LABEL_STRING: Integer
479
+ HEGEL_LABEL_STATEFUL_RULE: Integer
480
+ HEGEL_LABEL_FRESH_ID: Integer
481
+ HEGEL_LABEL_SET_CHOICE: Integer
482
+ HEGEL_STATE_MACHINE_DONE: Integer
483
+ HEGEL_COLLECTION_MAX_SIZE_UNBOUNDED: Integer
484
+ HEGEL_FLOAT64_SMALLEST_NONZERO_MAGNITUDE_UNRESTRICTED: Float
485
+ METHODS: Array[Symbol]
486
+
487
+ def self.with_context: [R] (untyped impl) { (untyped) -> R } -> R
488
+ def self.check!: (untyped impl, untyped ctx, Integer code) -> void
489
+ def self.warn_on_version_mismatch: (untyped impl, untyped ctx, ?io: untyped) -> void
490
+ def self.encode_integer_le: (Integer n) -> String
491
+ def self.decode_integer_le: (String bytes) -> Integer
492
+
493
+ class Real
494
+ def initialize: (?String path, ?io: untyped) -> void
495
+ def context_new: () -> untyped
496
+ def context_free: (untyped ctx) -> nil
497
+ def context_last_error: (untyped ctx) -> String
498
+ def version: (untyped ctx) -> String
499
+ def settings_new: (untyped ctx) -> untyped
500
+ def settings_free: (untyped ctx, untyped s) -> nil
501
+ def settings_set_test_cases: (untyped ctx, untyped s, Integer n) -> nil
502
+ def settings_set_verbosity: (untyped ctx, untyped s, Integer v) -> nil
503
+ def settings_set_seed: (untyped ctx, untyped s, Integer seed, bool has_seed) -> nil
504
+ def settings_set_derandomize: (untyped ctx, untyped s, bool derandomize) -> nil
505
+ def settings_set_database: (untyped ctx, untyped s, String? database) -> nil
506
+ def settings_set_stateful_step_count: (untyped ctx, untyped s, Integer n) -> nil
507
+ def settings_set_report_multiple_failures: (untyped ctx, untyped s, bool yes) -> nil
508
+ def settings_set_database_key: (untyped ctx, untyped s, String? key) -> nil
509
+ def settings_set_phases: (untyped ctx, untyped s, Integer phases) -> nil
510
+ def settings_set_suppress_health_check: (untyped ctx, untyped s, Integer checks) -> nil
511
+ def run_start: (untyped ctx, untyped settings) -> untyped
512
+ def next_test_case: (untyped ctx, untyped run) -> untyped?
513
+ def run_free: (untyped ctx, untyped run) -> nil
514
+ def test_case_free: (untyped ctx, untyped tc) -> nil
515
+ def mark_complete: (untyped ctx, untyped tc, Integer status, String? origin) -> nil
516
+ def target: (untyped ctx, untyped tc, Float value, String label) -> nil
517
+ def generate_boolean: (untyped ctx, untyped tc, Float p, bool forced, bool has_forced) -> bool
518
+ def generate_integer: (untyped ctx, untyped tc, Integer min_value, Integer max_value) -> Integer
519
+ def generate_integer_big: (untyped ctx, untyped tc, Integer min_value, Integer max_value) -> Integer
520
+ def run_result: (untyped ctx, untyped run) -> untyped
521
+ def run_result_free: (untyped ctx, untyped r) -> nil
522
+ def run_result_status: (untyped ctx, untyped r) -> Integer
523
+ def run_result_error: (untyped ctx, untyped r) -> String?
524
+ def run_result_failure_count: (untyped ctx, untyped r) -> Integer
525
+ def run_result_failure: (untyped ctx, untyped r, Integer index) -> untyped
526
+ def failure_free: (untyped ctx, untyped f) -> nil
527
+ def failure_origin: (untyped ctx, untyped f) -> String
528
+ def failure_reproduction_blob: (untyped ctx, untyped f) -> String?
529
+ def test_case_from_blob: (untyped ctx, untyped settings, String blob) -> untyped
530
+ def start_span: (untyped ctx, untyped tc, Integer label) -> nil
531
+ def stop_span: (untyped ctx, untyped tc, bool discard) -> nil
532
+ def new_collection: (untyped ctx, untyped tc, Integer min_size, Integer max_size) -> untyped
533
+ def collection_more: (untyped ctx, untyped tc, untyped collection) -> bool
534
+ def collection_reject: (untyped ctx, untyped tc, untyped collection, ?String? why) -> nil
535
+ def collection_free: (untyped ctx, untyped collection) -> nil
536
+ def new_pool: (untyped ctx, untyped tc) -> untyped
537
+ def pool_add: (untyped ctx, untyped tc, untyped pool) -> Integer
538
+ def pool_generate: (untyped ctx, untyped tc, untyped pool, bool consume) -> Integer
539
+ def pool_free: (untyped ctx, untyped pool) -> nil
540
+ def new_state_machine: (untyped ctx, untyped tc, Array[String] rule_names, Array[String] invariant_names) -> untyped
541
+ def state_machine_next_rule: (untyped ctx, untyped tc, untyped state_machine) -> Integer
542
+ def state_machine_rule_rejected: (untyped ctx, untyped tc, untyped state_machine) -> nil
543
+ def state_machine_free: (untyped ctx, untyped state_machine) -> nil
544
+ def generate_float: (untyped ctx, untyped tc, Integer width, Float min_value, Float max_value, bool allow_nan, bool allow_infinity, bool exclude_min, bool exclude_max, Float smallest_nonzero_magnitude) -> Float
545
+ def string_generator_text: (untyped ctx, min_size: Integer, max_size: Integer, ?codec: String?, ?min_codepoint: Integer, ?max_codepoint: Integer) -> untyped
546
+ def string_generator_free: (untyped ctx, untyped generator) -> nil
547
+ def generate_string: (untyped ctx, untyped tc, untyped generator) -> String
548
+ def generate_string_result_free: (untyped ctx, untyped result) -> nil
549
+ def generate_bytes: (untyped ctx, untyped tc, Integer min_size, Integer max_size) -> String
550
+ def generate_bytes_result_free: (untyped ctx, untyped result) -> nil
551
+ def string_generator_regex: (untyped ctx, String pattern, bool fullmatch, ?untyped alphabet) -> untyped
552
+ def string_generator_email: (untyped ctx) -> untyped
553
+ def string_generator_url: (untyped ctx) -> untyped
554
+ def string_generator_domain: (untyped ctx, Integer max_length) -> untyped
555
+ def generate_ipv4: (untyped ctx, untyped tc) -> String
556
+ def generate_ipv6: (untyped ctx, untyped tc) -> String
557
+ def generate_uuid: (untyped ctx, untyped tc, Integer version, bool has_version) -> String
558
+ def generate_date: (untyped ctx, untyped tc, Array[Integer] min_value, Array[Integer] max_value) -> Array[Integer]
559
+ def generate_time: (untyped ctx, untyped tc, Array[Integer] min_value, Array[Integer] max_value) -> Array[Integer]
560
+ def generate_datetime: (untyped ctx, untyped tc, Array[Integer] min_date, Array[Integer] min_time, Array[Integer] max_date, Array[Integer] max_time) -> [Array[Integer], Array[Integer]]
561
+ end
562
+ end
563
+ end
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: hegel-ruby
3
+ description: >
4
+ Write property-based tests using Hegel in Ruby projects, with RSpec,
5
+ Minitest, or any other test runner. Use this skill whenever the user asks
6
+ to write tests, add test coverage, or improve testing for Ruby functions,
7
+ modules, or classes. It applies especially when the code has properties
8
+ like round-trips, invariants, or contracts that hold across many inputs.
9
+ Also triggers on: "property-based tests", "PBT", "hegel", "generative tests",
10
+ "randomized testing", "test with random inputs", "shrinking", or when
11
+ existing tests use rantly, prop_check, rubycheck, pbt, or propr.
12
+ ---
13
+
14
+ # Hegel: Property-Based Testing for Ruby
15
+
16
+ This skill covers the Ruby API of Hegel, driven by the `hegeltest` gem (an
17
+ unofficial, third-party binding to `libhegel`, unaffiliated with the
18
+ `hegeldev` organization). It does not carry Hegel's methodology: how to find
19
+ a property worth testing, how to keep a generator from being
20
+ over-constrained, and the catalogue of property patterns. That methodology
21
+ lives in the upstream
22
+ [hegel-skill](https://github.com/hegeldev/hegel-skill), and applies to Ruby
23
+ the same way it applies to Rust, Go, C++, TypeScript, Java, and OCaml. Load
24
+ that skill first for the workflow and the property catalogue, then come back
25
+ here for the Ruby-specific API.
26
+
27
+ Before writing a Ruby property-based test, load
28
+ `references/ruby/reference.md` for the exact API: `Hegel.test`, settings,
29
+ `TestCase` methods, every generator and its options, stateful testing with
30
+ `Hegel::StateMachine`, the combinator methods, and Ruby-specific gotchas.