zizek 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 +115 -0
- data/GUIDE.md +774 -0
- data/LICENSE +201 -0
- data/README.md +201 -0
- data/docs/assets/zizek-rb-logo-dark.svg +16 -0
- data/docs/assets/zizek-rb-logo.svg +16 -0
- data/examples/combinators.rb +93 -0
- data/examples/coverage_assertions.rb +138 -0
- data/examples/database_workflow.rb +120 -0
- data/examples/die_hard.rb +122 -0
- data/examples/hypothesis_quickstart.rb +44 -0
- data/examples/keyed_draws.rb +123 -0
- data/examples/ledger.rb +114 -0
- data/examples/min_stack.rb +108 -0
- data/examples/minitest_suite.rb +64 -0
- data/examples/multi.rb +46 -0
- data/examples/queue.rb +122 -0
- data/examples/roundtrip.rb +71 -0
- data/examples/stack.rb +68 -0
- data/examples/tour.rb +126 -0
- data/lib/zizek/database.rb +211 -0
- data/lib/zizek/gen.rb +10377 -0
- data/lib/zizek/generators.rb +879 -0
- data/lib/zizek/minitest.rb +94 -0
- data/lib/zizek/rspec.rb +67 -0
- data/lib/zizek/settings.rb +249 -0
- data/lib/zizek/shell.rb +2938 -0
- data/lib/zizek/stateful.rb +349 -0
- data/lib/zizek/version.rb +8 -0
- data/lib/zizek.rb +8 -0
- metadata +109 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "generators"
|
|
4
|
+
require_relative "shell"
|
|
5
|
+
|
|
6
|
+
module Zizek
|
|
7
|
+
module Stateful
|
|
8
|
+
DEFAULT_STEP_COUNT = 50
|
|
9
|
+
|
|
10
|
+
# Chance a free per-step "stop here?" boolean comes up true under the stop
|
|
11
|
+
# protocols (1 and 2), which only a replay of an older blob still records,
|
|
12
|
+
# and which a probe extends from the RNG once that blob runs out. New cases
|
|
13
|
+
# record a "continue?" boolean instead, at
|
|
14
|
+
# Core.stateful_continue_probability.
|
|
15
|
+
STOP_PROBABILITY = 1.0 / 65536.0
|
|
16
|
+
|
|
17
|
+
Rule = Struct.new(:name, :apply)
|
|
18
|
+
|
|
19
|
+
# A state machine written as a class.
|
|
20
|
+
#
|
|
21
|
+
# class QueueMachine < Zizek::Stateful::Machine
|
|
22
|
+
# def initialize
|
|
23
|
+
# @queue = RingQueue.new
|
|
24
|
+
# @model = []
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# rule(:push) do |t|
|
|
28
|
+
# value = t.draw(Zizek::Generators.integers(min: 0, max: 99))
|
|
29
|
+
# @queue.push(value) && @model << value
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# rule(:pop) do |t|
|
|
33
|
+
# t.assume(!@model.empty?)
|
|
34
|
+
# raise "wrong value" unless @queue.pop == @model.shift
|
|
35
|
+
# end
|
|
36
|
+
#
|
|
37
|
+
# invariant(:sizes_agree) { @queue.size == @model.length or raise "drifted" }
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
# Zizek.check(QueueMachine.property, examples: 2_000)
|
|
41
|
+
#
|
|
42
|
+
# Rule bodies run with `self` bound to the machine, so they read and write
|
|
43
|
+
# its state directly. `assume` inside a rule is a precondition: the step is
|
|
44
|
+
# discarded and the run continues, rather than the whole example being
|
|
45
|
+
# rejected.
|
|
46
|
+
#
|
|
47
|
+
# The array-returning `#rules` and `#invariants` protocol this is built on
|
|
48
|
+
# is still supported; a machine that needs to compute its rules at runtime
|
|
49
|
+
# can define those methods directly instead of subclassing.
|
|
50
|
+
class Machine
|
|
51
|
+
class << self
|
|
52
|
+
def rule(name, &body)
|
|
53
|
+
raise ArgumentError, "rule #{name.inspect} needs a block" if body.nil?
|
|
54
|
+
|
|
55
|
+
declared_rules << Rule.new(name.to_s, ->(machine, test_case) { machine.instance_exec(test_case, &body) })
|
|
56
|
+
name
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def invariant(name, &body)
|
|
60
|
+
raise ArgumentError, "invariant #{name.inspect} needs a block" if body.nil?
|
|
61
|
+
|
|
62
|
+
declared_invariants << Rule.new(
|
|
63
|
+
name.to_s, ->(machine, test_case) { machine.instance_exec(test_case, &body) }
|
|
64
|
+
)
|
|
65
|
+
name
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def declared_rules
|
|
69
|
+
@declared_rules ||= []
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def declared_invariants
|
|
73
|
+
@declared_invariants ||= []
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# A property that runs one fresh machine per example.
|
|
77
|
+
#
|
|
78
|
+
# A machine holds mutable state, so the instance cannot be shared: each
|
|
79
|
+
# example — and each shrink candidate — gets its own.
|
|
80
|
+
def property(step_count: DEFAULT_STEP_COUNT, **new_args)
|
|
81
|
+
machine_class = self
|
|
82
|
+
lambda do |test_case|
|
|
83
|
+
machine = new_args.empty? ? machine_class.new : machine_class.new(**new_args)
|
|
84
|
+
Stateful.run(machine, test_case, step_count: step_count)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Subclasses start from their parent's rules rather than an empty list,
|
|
89
|
+
# so a shared base machine can define the rules every variant has.
|
|
90
|
+
def inherited(subclass)
|
|
91
|
+
super
|
|
92
|
+
subclass.instance_variable_set(:@declared_rules, declared_rules.dup)
|
|
93
|
+
subclass.instance_variable_set(:@declared_invariants, declared_invariants.dup)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private_class_method :declared_rules, :declared_invariants, :inherited
|
|
98
|
+
|
|
99
|
+
def rules
|
|
100
|
+
self.class.__send__(:declared_rules)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def invariants
|
|
104
|
+
self.class.__send__(:declared_invariants)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
class FeatureFlags
|
|
109
|
+
def initialize(test_case, rule_count)
|
|
110
|
+
raw = test_case.__send__(:stateful_feature_probability_raw,
|
|
111
|
+
Core.stateful_feature_generation_raw
|
|
112
|
+
)
|
|
113
|
+
@probability = Core.stateful_feature_probability(raw)
|
|
114
|
+
@disabled = Array.new(rule_count)
|
|
115
|
+
@candidates = (0...rule_count).to_a
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def enabled?(test_case, index)
|
|
119
|
+
test_case.start_span("stateful.feature_flag")
|
|
120
|
+
decided = @disabled[index]
|
|
121
|
+
forced = Core.stateful_feature_forced(
|
|
122
|
+
@candidates.length,
|
|
123
|
+
@candidates.include?(index),
|
|
124
|
+
!decided.nil?,
|
|
125
|
+
decided || false
|
|
126
|
+
)
|
|
127
|
+
begin
|
|
128
|
+
disabled = forced.nil? ? test_case.weighted(@probability) : test_case.__send__(:forced_boolean, forced)
|
|
129
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
|
130
|
+
test_case.stop_span
|
|
131
|
+
raise
|
|
132
|
+
end
|
|
133
|
+
@disabled[index] = disabled
|
|
134
|
+
@candidates.clear unless disabled
|
|
135
|
+
@candidates.delete(index)
|
|
136
|
+
test_case.stop_span
|
|
137
|
+
!disabled
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Execution-local pool of arbitrary host-language values.
|
|
142
|
+
#
|
|
143
|
+
# A variable's identity is a *recorded* choice rather than a host-side
|
|
144
|
+
# counter, and a draw from the pool records the identifier it selected
|
|
145
|
+
# rather than a position in the live list. That is what lets the shrinker
|
|
146
|
+
# delete an earlier addition without changing what every later draw refers
|
|
147
|
+
# to — see ir/stateful.zir's stable-identifier section. Before that,
|
|
148
|
+
# deleting one addition renumbered every reference after it, so most
|
|
149
|
+
# deletions produced a different rule sequence that no longer failed and
|
|
150
|
+
# shrinking stalled far from minimal.
|
|
151
|
+
class Pool
|
|
152
|
+
def initialize
|
|
153
|
+
@active = []
|
|
154
|
+
@values = {}
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def length
|
|
158
|
+
@active.length
|
|
159
|
+
end
|
|
160
|
+
alias size length
|
|
161
|
+
|
|
162
|
+
def empty?
|
|
163
|
+
@active.empty?
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Add a value under a freshly drawn identifier.
|
|
167
|
+
#
|
|
168
|
+
# Takes the case because adding is now a *draw*: the identifier is
|
|
169
|
+
# recorded in the choice stream rather than handed out by a host-side
|
|
170
|
+
# counter, which is what lets a later reference to this variable keep
|
|
171
|
+
# meaning this variable when an earlier addition is shrunk away.
|
|
172
|
+
def add(test_case, value)
|
|
173
|
+
variable_id = test_case.__send__(:fresh_id)
|
|
174
|
+
@active << variable_id
|
|
175
|
+
@values[variable_id] = value
|
|
176
|
+
nil
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def values_reusable
|
|
180
|
+
ValuesReusable.new(self)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def values_consumed
|
|
184
|
+
ValuesConsumed.new(self)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
protected
|
|
188
|
+
|
|
189
|
+
attr_reader :active, :values
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
class ValuesReusable
|
|
193
|
+
def initialize(pool)
|
|
194
|
+
@pool = pool
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def do_draw(test_case)
|
|
198
|
+
test_case.assume(!@pool.empty?)
|
|
199
|
+
variable_id = test_case.__send__(:from_set, @pool.send(:active))
|
|
200
|
+
@pool.send(:values).fetch(variable_id)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
class ValuesConsumed
|
|
205
|
+
def initialize(pool)
|
|
206
|
+
@pool = pool
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def do_draw(test_case)
|
|
210
|
+
test_case.assume(!@pool.empty?)
|
|
211
|
+
variable_id = test_case.__send__(:from_set, @pool.send(:active))
|
|
212
|
+
@pool.send(:active).delete(variable_id)
|
|
213
|
+
@pool.send(:values).delete(variable_id)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
module_function
|
|
218
|
+
|
|
219
|
+
def run(machine, test_case, step_count: DEFAULT_STEP_COUNT)
|
|
220
|
+
rules = machine.rules.to_a
|
|
221
|
+
test_case.assume(!rules.empty?)
|
|
222
|
+
invariants = machine.invariants.to_a
|
|
223
|
+
test_case.note("initial invariant check")
|
|
224
|
+
check_invariants(machine, invariants, test_case)
|
|
225
|
+
|
|
226
|
+
steps_drawn = 0
|
|
227
|
+
steps_rejected = 0
|
|
228
|
+
protocol = test_case.__send__(:stateful_protocol)
|
|
229
|
+
|
|
230
|
+
# The first decision, which is forced, and the feature probability sit
|
|
231
|
+
# before any step's span; each step's span then holds its rule probes,
|
|
232
|
+
# the rule's own draws, and the decision that follows it. That makes
|
|
233
|
+
# every step, the first included, one span the shrinker can delete
|
|
234
|
+
# without misaligning the steps after it. Upstream's span starts at the
|
|
235
|
+
# decision instead, which leaves the first step undeletable here:
|
|
236
|
+
# deleting it takes the forced decision and the probability with it, and
|
|
237
|
+
# a misread decision stops the run. The span tree is not part of the blob.
|
|
238
|
+
return nil if stop_decision(test_case, protocol, steps_drawn, steps_rejected, step_count)
|
|
239
|
+
|
|
240
|
+
feature_flags = protocol == 1 ? nil : FeatureFlags.new(test_case, rules.length)
|
|
241
|
+
|
|
242
|
+
loop do
|
|
243
|
+
test_case.start_span("stateful.rule")
|
|
244
|
+
rejected = false
|
|
245
|
+
begin
|
|
246
|
+
if feature_flags.nil?
|
|
247
|
+
index = draw_rule_index(test_case, rules.length)
|
|
248
|
+
else
|
|
249
|
+
index, feature_flags = select_rule(test_case, rules.length, feature_flags)
|
|
250
|
+
end
|
|
251
|
+
rule = rules[index]
|
|
252
|
+
steps_drawn += 1
|
|
253
|
+
test_case.note("step #{steps_drawn}: #{rule.name}")
|
|
254
|
+
begin
|
|
255
|
+
rule.apply.call(machine, test_case)
|
|
256
|
+
rescue AssumptionRejected
|
|
257
|
+
steps_rejected += 1
|
|
258
|
+
rejected = true
|
|
259
|
+
test_case.note("rule rejected by assumption")
|
|
260
|
+
end
|
|
261
|
+
check_invariants(machine, invariants, test_case) unless rejected
|
|
262
|
+
stop = stop_decision(test_case, protocol, steps_drawn, steps_rejected, step_count)
|
|
263
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
|
264
|
+
test_case.stop_span
|
|
265
|
+
raise
|
|
266
|
+
end
|
|
267
|
+
test_case.stop_span(discarded: rejected)
|
|
268
|
+
break if stop
|
|
269
|
+
end
|
|
270
|
+
nil
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Draw one round's decision and report whether the case stops. Protocol 3
|
|
274
|
+
# records "continue?"; protocols 1 and 2 recorded "stop here?", and a
|
|
275
|
+
# replay of one of their blobs keeps drawing that.
|
|
276
|
+
def stop_decision(test_case, protocol, steps_drawn, steps_rejected, step_count)
|
|
277
|
+
if protocol == 3
|
|
278
|
+
forced = Core.stateful_continue_forced(steps_drawn, steps_rejected, step_count)
|
|
279
|
+
proceed = if forced.nil?
|
|
280
|
+
test_case.weighted(Core.stateful_continue_probability)
|
|
281
|
+
else
|
|
282
|
+
test_case.__send__(:forced_boolean, forced)
|
|
283
|
+
end
|
|
284
|
+
return !proceed
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
forced = Core.stateful_stop_forced(steps_drawn, steps_rejected, step_count)
|
|
288
|
+
forced.nil? ? test_case.weighted(STOP_PROBABILITY) : test_case.__send__(:forced_boolean, forced)
|
|
289
|
+
end
|
|
290
|
+
private_class_method :stop_decision
|
|
291
|
+
|
|
292
|
+
def draw_rule_index(test_case, rule_count)
|
|
293
|
+
test_case.integer(0, rule_count - 1)
|
|
294
|
+
end
|
|
295
|
+
private_class_method :draw_rule_index
|
|
296
|
+
|
|
297
|
+
def select_rule(test_case, rule_count, feature_flags)
|
|
298
|
+
feature_flags ||= FeatureFlags.new(test_case, rule_count)
|
|
299
|
+
known_bad = []
|
|
300
|
+
Core.stateful_rule_probe_count.times do
|
|
301
|
+
# A probe — the rule index and the flag it consults — is one span, so
|
|
302
|
+
# the shrinker can delete a probe that landed on a disabled rule
|
|
303
|
+
# without misaligning the draws after it. A misaligned continue
|
|
304
|
+
# decision reads as a stop, so that deletion is the one that reaches
|
|
305
|
+
# the minimal sequence; the span tree is not part of the blob.
|
|
306
|
+
test_case.start_span("stateful.rule_probe")
|
|
307
|
+
begin
|
|
308
|
+
index = draw_rule_index(test_case, rule_count)
|
|
309
|
+
enabled = !known_bad.include?(index) && feature_flags.enabled?(test_case, index)
|
|
310
|
+
ensure
|
|
311
|
+
test_case.stop_span
|
|
312
|
+
end
|
|
313
|
+
return [index, feature_flags] if enabled
|
|
314
|
+
|
|
315
|
+
known_bad << index unless known_bad.include?(index)
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
max_good = Core.stateful_fallback_max_good(rule_count, known_bad.length)
|
|
319
|
+
speculative = test_case.integer(0, max_good - 1)
|
|
320
|
+
allowed = []
|
|
321
|
+
rule_count.times do |index|
|
|
322
|
+
next if known_bad.include?(index)
|
|
323
|
+
next unless feature_flags.enabled?(test_case, index)
|
|
324
|
+
|
|
325
|
+
allowed << index
|
|
326
|
+
if Core.stateful_fallback_accept(allowed.length, speculative)
|
|
327
|
+
return [test_case.__send__(:forced_integer, index), feature_flags]
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
offset = test_case.integer(0, allowed.length - 1)
|
|
331
|
+
[test_case.__send__(:forced_integer, allowed[offset]), feature_flags]
|
|
332
|
+
end
|
|
333
|
+
private_class_method :select_rule
|
|
334
|
+
|
|
335
|
+
def check_invariants(machine, invariants, test_case)
|
|
336
|
+
invariants.each do |invariant|
|
|
337
|
+
test_case.note("invariant: #{invariant.name}")
|
|
338
|
+
invariant.apply.call(machine, test_case)
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
private_class_method :check_invariants
|
|
342
|
+
|
|
343
|
+
def pool(_test_case)
|
|
344
|
+
Pool.new
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
private_constant :FeatureFlags, :ValuesReusable, :ValuesConsumed, :STOP_PROBABILITY
|
|
348
|
+
end
|
|
349
|
+
end
|
data/lib/zizek.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: zizek
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- waldemar.quevedo@gmail.com
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: minitest
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '5'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '5'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rspec
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '3'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '3'
|
|
40
|
+
description: |
|
|
41
|
+
Zizek is a native property-based testing for Ruby, modeled on Hegel and Hypothesis.
|
|
42
|
+
It explores generated examples, records every choice, shrinks the first failure to a
|
|
43
|
+
minimal counterexample, and reports a reproduce blob that replays that exact example.
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files:
|
|
47
|
+
- CHANGELOG.md
|
|
48
|
+
- GUIDE.md
|
|
49
|
+
- LICENSE
|
|
50
|
+
- README.md
|
|
51
|
+
files:
|
|
52
|
+
- CHANGELOG.md
|
|
53
|
+
- GUIDE.md
|
|
54
|
+
- LICENSE
|
|
55
|
+
- README.md
|
|
56
|
+
- docs/assets/zizek-rb-logo-dark.svg
|
|
57
|
+
- docs/assets/zizek-rb-logo.svg
|
|
58
|
+
- examples/combinators.rb
|
|
59
|
+
- examples/coverage_assertions.rb
|
|
60
|
+
- examples/database_workflow.rb
|
|
61
|
+
- examples/die_hard.rb
|
|
62
|
+
- examples/hypothesis_quickstart.rb
|
|
63
|
+
- examples/keyed_draws.rb
|
|
64
|
+
- examples/ledger.rb
|
|
65
|
+
- examples/min_stack.rb
|
|
66
|
+
- examples/minitest_suite.rb
|
|
67
|
+
- examples/multi.rb
|
|
68
|
+
- examples/queue.rb
|
|
69
|
+
- examples/roundtrip.rb
|
|
70
|
+
- examples/stack.rb
|
|
71
|
+
- examples/tour.rb
|
|
72
|
+
- lib/zizek.rb
|
|
73
|
+
- lib/zizek/database.rb
|
|
74
|
+
- lib/zizek/gen.rb
|
|
75
|
+
- lib/zizek/generators.rb
|
|
76
|
+
- lib/zizek/minitest.rb
|
|
77
|
+
- lib/zizek/rspec.rb
|
|
78
|
+
- lib/zizek/settings.rb
|
|
79
|
+
- lib/zizek/shell.rb
|
|
80
|
+
- lib/zizek/stateful.rb
|
|
81
|
+
- lib/zizek/version.rb
|
|
82
|
+
homepage: https://github.com/wallyqs/zizek.rb
|
|
83
|
+
licenses:
|
|
84
|
+
- Apache-2.0
|
|
85
|
+
metadata:
|
|
86
|
+
homepage_uri: https://github.com/wallyqs/zizek.rb
|
|
87
|
+
source_code_uri: https://github.com/wallyqs/zizek.rb
|
|
88
|
+
changelog_uri: https://github.com/wallyqs/zizek.rb/blob/main/CHANGELOG.md
|
|
89
|
+
documentation_uri: https://github.com/wallyqs/zizek.rb/blob/main/GUIDE.md
|
|
90
|
+
bug_tracker_uri: https://github.com/wallyqs/zizek.rb/issues
|
|
91
|
+
rubygems_mfa_required: 'true'
|
|
92
|
+
rdoc_options: []
|
|
93
|
+
require_paths:
|
|
94
|
+
- lib
|
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
96
|
+
requirements:
|
|
97
|
+
- - ">="
|
|
98
|
+
- !ruby/object:Gem::Version
|
|
99
|
+
version: '2.6'
|
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
101
|
+
requirements:
|
|
102
|
+
- - ">="
|
|
103
|
+
- !ruby/object:Gem::Version
|
|
104
|
+
version: '3.0'
|
|
105
|
+
requirements: []
|
|
106
|
+
rubygems_version: 4.0.23
|
|
107
|
+
specification_version: 4
|
|
108
|
+
summary: Property-based testing with portable, cross-language reproduce blobs
|
|
109
|
+
test_files: []
|