prop_check 0.9.0 → 0.10.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 +4 -4
- data/.gitignore +2 -1
- data/Gemfile +6 -3
- data/README.md +16 -1
- data/lib/prop_check/hooks.rb +122 -0
- data/lib/prop_check/property.rb +28 -8
- data/lib/prop_check/version.rb +1 -1
- data/prop_check.gemspec +1 -3
- metadata +7 -35
- data/Gemfile.lock +0 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aaa23e40547bec5672e806d62c567947cd596e1c11ed2bc80fbe6912cd75a777
|
4
|
+
data.tar.gz: a0263cddf3efb8ee55f29395004915c38b6e78c7fc7bbf2c1b456f344013b8da
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7976fb344d4c7135cd858ca86606beb97cd9e9f57c40e2bb322a3c76287de46491d4feb1dd3a65158ffef8ea98894490edc7eb9bc506d80ddb110b2f026de8d
|
7
|
+
data.tar.gz: f80bffd3b08d1a1cf120a70645b548c2f70b540ae5be317b7635f7416cc4e7be94d3a1bfca10acd117166e08fd0a0c6cd8ab08bf68097182fc064519600cfb55
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
@@ -3,6 +3,9 @@ source "https://rubygems.org"
|
|
3
3
|
# Specify your gem's dependencies in prop_check.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
gem
|
7
|
-
gem
|
8
|
-
gem
|
6
|
+
gem "bundler", "~> 2.0"
|
7
|
+
gem "rake", "~> 12.3", require: false, group: :test
|
8
|
+
gem "rspec", "~> 3.0", require: false, group: :test
|
9
|
+
gem "doctest-rspec", require: false, group: :test
|
10
|
+
gem "simplecov", require: false, group: :test
|
11
|
+
|
data/README.md
CHANGED
@@ -27,6 +27,7 @@ Before releasing this gem on Rubygems, the following things need to be finished:
|
|
27
27
|
- [x] Look into customization of settings from e.g. command line arguments.
|
28
28
|
- [x] Good, unicode-compliant, string generators.
|
29
29
|
- [x] Filtering generator outputs.
|
30
|
+
- [x] Before/after/around hooks to add setup/teardown logic to be called before/after/around each time a check is run with new data.
|
30
31
|
|
31
32
|
# Nice-to-haves
|
32
33
|
|
@@ -87,7 +88,8 @@ Here we check if `naive_average` indeed always returns an integer for all arrays
|
|
87
88
|
def naive_average(array)
|
88
89
|
array.sum / array.length
|
89
90
|
end
|
90
|
-
|
91
|
+
```
|
92
|
+
```ruby
|
91
93
|
# And then in a test case:
|
92
94
|
include PropCheck::Generators
|
93
95
|
PropCheck.forall(numbers: array(integer)) do |numbers:|
|
@@ -96,6 +98,19 @@ PropCheck.forall(numbers: array(integer)) do |numbers:|
|
|
96
98
|
raise "Expected the average to be an integer!"
|
97
99
|
end
|
98
100
|
end
|
101
|
+
|
102
|
+
# Or if you e.g. are using RSpec:
|
103
|
+
describe "#naive_average" do
|
104
|
+
include PropCheck
|
105
|
+
include PropCheck::Generators
|
106
|
+
|
107
|
+
it "returns an integer for any input" do
|
108
|
+
forall(numbers: array(integer)) do |numbers:|
|
109
|
+
result = naive_average(numbers)
|
110
|
+
expect(result).to be_a(Integer)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
99
114
|
```
|
100
115
|
|
101
116
|
When running this particular example PropCheck very quickly finds out that we have made a programming mistake:
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# Contains the logic to combine potentially many before/after/around hooks
|
5
|
+
# into a single pair of procedures called `before` and `after`.
|
6
|
+
#
|
7
|
+
# _Note: This module is an implementation detail of PropCheck._
|
8
|
+
#
|
9
|
+
# These can be invoked by manually calling `#before` and `#after`.
|
10
|
+
# Important:
|
11
|
+
# - Always call first `#before` and then `#after`.
|
12
|
+
# This is required to make sure that `around` callbacks will work properly.
|
13
|
+
# - Make sure that if you call `#before`, to also call `#after`.
|
14
|
+
# It is thus highly recommended to call `#after` inside an `ensure`.
|
15
|
+
# This is to make sure that `around` callbacks indeed perform their proper cleanup.
|
16
|
+
#
|
17
|
+
# Alternatively, check out `PropCheck::Hooks::Enumerable` which allows
|
18
|
+
# wrapping the elements of an enumerable with hooks.
|
19
|
+
class PropCheck::Hooks
|
20
|
+
# attr_reader :before, :after, :around
|
21
|
+
def initialize()
|
22
|
+
@before = proc {}
|
23
|
+
@after = proc {}
|
24
|
+
@around = proc { |*args, &block| block.call(*args) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def wrap_enum(enumerable)
|
28
|
+
PropCheck::Hooks::Enumerable.new(enumerable, self)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
##
|
33
|
+
# Wraps a block with all hooks that were configured this far.
|
34
|
+
#
|
35
|
+
# This means that whenever the block is called,
|
36
|
+
# the before/around/after hooks are called before/around/after it.
|
37
|
+
def wrap_block(&block)
|
38
|
+
proc { |*args| call(*args, &block) }
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Wraps a block with all hooks that were configured this far,
|
43
|
+
# and immediately calls it using the given `*args`.
|
44
|
+
#
|
45
|
+
# See also #wrap_block
|
46
|
+
def call(*args, &block)
|
47
|
+
begin
|
48
|
+
@before.call()
|
49
|
+
@around.call do
|
50
|
+
block.call(*args)
|
51
|
+
end
|
52
|
+
ensure
|
53
|
+
@after.call()
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Adds `hook` to the `before` proc.
|
59
|
+
# It is called after earlier-added `before` procs.
|
60
|
+
def add_before(&hook)
|
61
|
+
old_before = @before
|
62
|
+
@before = proc {
|
63
|
+
old_before.call
|
64
|
+
hook.call
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# Adds `hook` to the `after` proc.
|
70
|
+
# It is called before earlier-added `after` procs.
|
71
|
+
def add_after(&hook)
|
72
|
+
old_after = @after
|
73
|
+
@after = proc {
|
74
|
+
hook.call
|
75
|
+
old_after.call
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# Adds `hook` to the `around` proc.
|
81
|
+
# It is called _inside_ earlier-added `around` procs.
|
82
|
+
def add_around(&hook)
|
83
|
+
old_around = @around
|
84
|
+
@around = proc do |&block|
|
85
|
+
old_around.call do |*args|
|
86
|
+
hook.call(*args, &block)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
# Wraps enumerable `inner` with a `PropCheck::Hooks` object
|
93
|
+
# such that the before/after/around hooks are called
|
94
|
+
# before/after/around each element that is fetched from `inner`.
|
95
|
+
#
|
96
|
+
# This is very helpful if you need to perform cleanup logic
|
97
|
+
# before/after/around e.g. data is generated or fetched.
|
98
|
+
#
|
99
|
+
# Note that whatever is after a `yield` in an `around` hook
|
100
|
+
# is not guaranteed to be called (for instance when a StopIteration is raised).
|
101
|
+
# Thus: make sure you use `ensure` to clean up resources.
|
102
|
+
class Enumerable
|
103
|
+
include ::Enumerable
|
104
|
+
|
105
|
+
def initialize(inner, hooks)
|
106
|
+
@inner = inner
|
107
|
+
@hooks = hooks
|
108
|
+
end
|
109
|
+
|
110
|
+
def each(&task)
|
111
|
+
return to_enum(:each) unless block_given?
|
112
|
+
|
113
|
+
enum = @inner.to_enum
|
114
|
+
|
115
|
+
wrapped_yielder = @hooks.wrap_block do
|
116
|
+
yield enum.next(&task)
|
117
|
+
end
|
118
|
+
|
119
|
+
loop(&wrapped_yielder)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/lib/prop_check/property.rb
CHANGED
@@ -2,6 +2,7 @@ require 'stringio'
|
|
2
2
|
require "awesome_print"
|
3
3
|
|
4
4
|
require 'prop_check/property/configuration'
|
5
|
+
require 'prop_check/hooks'
|
5
6
|
module PropCheck
|
6
7
|
##
|
7
8
|
# Run properties
|
@@ -67,6 +68,7 @@ module PropCheck
|
|
67
68
|
@kwbindings = kwbindings
|
68
69
|
@condition = proc { true }
|
69
70
|
@config = self.class.configuration
|
71
|
+
@hooks = PropCheck::Hooks.new
|
70
72
|
end
|
71
73
|
|
72
74
|
##
|
@@ -103,13 +105,28 @@ module PropCheck
|
|
103
105
|
# Only filter if you have few inputs to reject. Otherwise, improve your generators.
|
104
106
|
def where(&condition)
|
105
107
|
original_condition = @condition.dup
|
106
|
-
@condition = proc do
|
107
|
-
original_condition.call(
|
108
|
+
@condition = proc do |*args|
|
109
|
+
original_condition.call(*args) && condition.call(*args)
|
108
110
|
end
|
109
111
|
|
110
112
|
self
|
111
113
|
end
|
112
114
|
|
115
|
+
def before(&hook)
|
116
|
+
@hooks.add_before(&hook)
|
117
|
+
self
|
118
|
+
end
|
119
|
+
|
120
|
+
def after(&hook)
|
121
|
+
@hooks.add_after(&hook)
|
122
|
+
self
|
123
|
+
end
|
124
|
+
|
125
|
+
def around(&hook)
|
126
|
+
@hooks.add_around(&hook)
|
127
|
+
self
|
128
|
+
end
|
129
|
+
|
113
130
|
##
|
114
131
|
# Checks the property (after settings have been altered using the other instance methods in this class.)
|
115
132
|
def check(&block)
|
@@ -127,7 +144,7 @@ module PropCheck
|
|
127
144
|
n_successful = 0
|
128
145
|
|
129
146
|
# Loop stops at first exception
|
130
|
-
|
147
|
+
attempts_enum(binding_generator).each do |generator_result|
|
131
148
|
n_runs += 1
|
132
149
|
check_attempt(generator_result, n_successful, &block)
|
133
150
|
n_successful += 1
|
@@ -182,10 +199,15 @@ module PropCheck
|
|
182
199
|
raise e, output_string, e.backtrace
|
183
200
|
end
|
184
201
|
|
185
|
-
private def
|
202
|
+
private def attempts_enum(binding_generator)
|
203
|
+
@hooks
|
204
|
+
.wrap_enum(raw_attempts_enum(binding_generator))
|
205
|
+
.lazy
|
206
|
+
.take(@config.n_runs)
|
207
|
+
end
|
186
208
|
|
209
|
+
private def raw_attempts_enum(binding_generator)
|
187
210
|
rng = Random::DEFAULT
|
188
|
-
n_runs = 0
|
189
211
|
size = 1
|
190
212
|
(0...@config.max_generate_attempts)
|
191
213
|
.lazy
|
@@ -193,12 +215,10 @@ module PropCheck
|
|
193
215
|
.reject { |val| val.root.any? { |elem| elem == :"_PropCheck.filter_me" }}
|
194
216
|
.select { |val| @condition.call(*val.root) }
|
195
217
|
.map do |result|
|
196
|
-
n_runs += 1
|
197
218
|
size += 1
|
198
219
|
|
199
220
|
result
|
200
221
|
end
|
201
|
-
.take_while { n_runs <= @config.n_runs }
|
202
222
|
end
|
203
223
|
|
204
224
|
private def show_problem_output(problem, generator_results, n_successful, &block)
|
@@ -253,7 +273,7 @@ module PropCheck
|
|
253
273
|
parent_siblings = nil
|
254
274
|
problem_exception = nil
|
255
275
|
shrink_steps = 0
|
256
|
-
(0..@config.max_shrink_steps).each do
|
276
|
+
@hooks.wrap_enum(0..@config.max_shrink_steps).lazy.each do
|
257
277
|
begin
|
258
278
|
sibling = siblings.next
|
259
279
|
rescue StopIteration
|
data/lib/prop_check/version.rb
CHANGED
data/prop_check.gemspec
CHANGED
@@ -36,7 +36,5 @@ Gem::Specification.new do |spec|
|
|
36
36
|
|
37
37
|
spec.required_ruby_version = '>= 2.5.1'
|
38
38
|
|
39
|
-
spec.
|
40
|
-
spec.add_development_dependency "rake", "~> 12.3"
|
41
|
-
spec.add_development_dependency "rspec", "~> 3.0"
|
39
|
+
spec.add_dependency 'awesome_print', '~> 1.8'
|
42
40
|
end
|
metadata
CHANGED
@@ -1,57 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prop_check
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Qqwy/Wiebe-Marten Wijnja
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-07-
|
11
|
+
date: 2020-07-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: awesome_print
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
20
|
-
type: :
|
19
|
+
version: '1.8'
|
20
|
+
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '12.3'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '12.3'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rspec
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '3.0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '3.0'
|
26
|
+
version: '1.8'
|
55
27
|
description: PropCheck allows you to do property-based testing, including shrinking.
|
56
28
|
(akin to Haskell's QuickCheck, Erlang's PropEr, Elixir's StreamData). This means
|
57
29
|
that your test are run many times with different, autogenerated inputs, and as soon
|
@@ -71,7 +43,6 @@ files:
|
|
71
43
|
- CHANGELOG.md
|
72
44
|
- CODE_OF_CONDUCT.md
|
73
45
|
- Gemfile
|
74
|
-
- Gemfile.lock
|
75
46
|
- LICENSE.txt
|
76
47
|
- README.md
|
77
48
|
- Rakefile
|
@@ -82,6 +53,7 @@ files:
|
|
82
53
|
- lib/prop_check/generators.rb
|
83
54
|
- lib/prop_check/helper.rb
|
84
55
|
- lib/prop_check/helper/lazy_append.rb
|
56
|
+
- lib/prop_check/hooks.rb
|
85
57
|
- lib/prop_check/lazy_tree.rb
|
86
58
|
- lib/prop_check/property.rb
|
87
59
|
- lib/prop_check/property/configuration.rb
|
data/Gemfile.lock
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
prop_check (0.9.0)
|
5
|
-
|
6
|
-
GEM
|
7
|
-
remote: https://rubygems.org/
|
8
|
-
specs:
|
9
|
-
awesome_print (1.8.0)
|
10
|
-
diff-lcs (1.3)
|
11
|
-
docile (1.3.2)
|
12
|
-
doctest-core (0.0.2)
|
13
|
-
doctest-rspec (0.0.3)
|
14
|
-
doctest-core (~> 0.0.2)
|
15
|
-
rspec
|
16
|
-
json (2.2.0)
|
17
|
-
rake (12.3.3)
|
18
|
-
rspec (3.8.0)
|
19
|
-
rspec-core (~> 3.8.0)
|
20
|
-
rspec-expectations (~> 3.8.0)
|
21
|
-
rspec-mocks (~> 3.8.0)
|
22
|
-
rspec-core (3.8.1)
|
23
|
-
rspec-support (~> 3.8.0)
|
24
|
-
rspec-expectations (3.8.4)
|
25
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
26
|
-
rspec-support (~> 3.8.0)
|
27
|
-
rspec-mocks (3.8.1)
|
28
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
29
|
-
rspec-support (~> 3.8.0)
|
30
|
-
rspec-support (3.8.2)
|
31
|
-
simplecov (0.16.1)
|
32
|
-
docile (~> 1.1)
|
33
|
-
json (>= 1.8, < 3)
|
34
|
-
simplecov-html (~> 0.10.0)
|
35
|
-
simplecov-html (0.10.2)
|
36
|
-
|
37
|
-
PLATFORMS
|
38
|
-
ruby
|
39
|
-
|
40
|
-
DEPENDENCIES
|
41
|
-
awesome_print
|
42
|
-
bundler (~> 2.0)
|
43
|
-
doctest-rspec
|
44
|
-
prop_check!
|
45
|
-
rake (~> 12.3)
|
46
|
-
rspec (~> 3.0)
|
47
|
-
simplecov
|
48
|
-
|
49
|
-
BUNDLED WITH
|
50
|
-
2.1.4
|