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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +22 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +264 -0
- data/Rakefile +19 -0
- data/docs/README.md +25 -0
- data/docs/adr/0001-bind-libhegel-through-fiddle.md +54 -0
- data/docs/adr/0002-ship-one-prebuilt-engine-per-platform-specific-gem.md +48 -0
- data/docs/adr/0003-publish-as-hegeltest-require-as-hegel.md +39 -0
- data/docs/adr/0004-expose-generators-through-a-mixin-with-keyword-options.md +42 -0
- data/docs/adr/0005-name-drawn-values-from-the-callers-source-with-prism.md +40 -0
- data/docs/adr/0006-verify-the-binding-in-seven-layers-with-full-coverage.md +51 -0
- data/docs/adr/0007-ship-a-thin-ruby-skill-shaped-for-donation.md +56 -0
- data/docs/adr/0008-revisit-the-binding-after-milestone-c-on-measurement.md +81 -0
- data/docs/adr/0009-turn-the-example-database-on-with-a-key.md +89 -0
- data/docs/adr/0010-declare-stateful-rules-with-a-class-macro.md +113 -0
- data/docs/adr/0011-let-the-test-case-own-every-pool-drawn-from-it.md +83 -0
- data/docs/adr/0012-build-a-failure-origin-from-the-callers-own-frame.md +72 -0
- data/docs/adr/0013-bind-libhegel-through-the-ffi-gem.md +102 -0
- data/docs/architecture.md +182 -0
- data/lib/hegel/draw_name.rb +109 -0
- data/lib/hegel/errors.rb +47 -0
- data/lib/hegel/generator.rb +98 -0
- data/lib/hegel/generators.rb +865 -0
- data/lib/hegel/lib_hegel/real.rb +1149 -0
- data/lib/hegel/lib_hegel.rb +269 -0
- data/lib/hegel/libhegel_version.rb +9 -0
- data/lib/hegel/locate.rb +188 -0
- data/lib/hegel/report.rb +87 -0
- data/lib/hegel/runner.rb +464 -0
- data/lib/hegel/settings.rb +164 -0
- data/lib/hegel/state_machine.rb +89 -0
- data/lib/hegel/stateful/pool.rb +111 -0
- data/lib/hegel/stateful.rb +120 -0
- data/lib/hegel/syntax/methods.rb +173 -0
- data/lib/hegel/test_case.rb +523 -0
- data/lib/hegel/version.rb +5 -0
- data/lib/hegel.rb +92 -0
- data/lib/hegeltest.rb +7 -0
- data/lib/tasks/libhegel.rake +112 -0
- data/lib/tasks/platform_gems.rake +111 -0
- data/sig/hegel.rbs +563 -0
- data/skills/hegel-ruby/SKILL.md +30 -0
- data/skills/hegel-ruby/references/ruby/reference.md +1210 -0
- 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.
|