pbt 0.4.0 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +59 -46
- data/lib/pbt/check/rspec_adapter/predicate_block_inspector.rb +1 -1
- data/lib/pbt/check/rspec_adapter/property_extension.rb +1 -1
- data/lib/pbt/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 129484c73e5be7741c4bbf70c2ab374a0e48f28926c3cd4b6b3ca004c1037b98
|
4
|
+
data.tar.gz: 83ec64585e4b33bfad802b262784b0507f341621764099279af3f6faeda273fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c61fa019e0aff56404f4c3346b039615c368e175e98407f118be9ef55060e61f4ebacbd6cec79b5e0834cfec2550fdbdf6952be47eeacb1437455f5fd54dd5b
|
7
|
+
data.tar.gz: 209b104767846e73d8db2b94d0b4c9e2e9528c695e9d4cb766d6845de7e8c1556e523b172461c693c77e6deea3f0a764dd51c8e2a17611a2914760146e399bcc
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.4.2] - 2024-05-23
|
4
|
+
|
5
|
+
- Fix Prism `LoadError` message [#27](https://github.com/ohbarye/pbt/pull/27) by @sambostock
|
6
|
+
|
7
|
+
## [0.4.1] - 2024-05-10
|
8
|
+
|
9
|
+
- Fix a bug for experimental_ractor_rspec_integration mode. When a test file name starts with a number, it can't be a constant name.
|
10
|
+
|
3
11
|
## [0.4.0] - 2024-05-06
|
4
12
|
|
5
13
|
- Allow to use RSpec::Matchers for `worker: :none, :thread, :process` also.
|
data/README.md
CHANGED
@@ -8,6 +8,8 @@ A property-based testing tool for Ruby with experimental features that allow you
|
|
8
8
|
|
9
9
|
PBT stands for Property-Based Testing.
|
10
10
|
|
11
|
+
As for the results of the parallelization experiment, please refer the talk at RubyKaigi 2024: [Unlocking Potential of Property Based Testing with Ractor](https://rubykaigi.org/2024/presentations/ohbarye.html).
|
12
|
+
|
11
13
|
## What's Property-Based Testing?
|
12
14
|
|
13
15
|
Property-Based Testing is a testing methodology that focuses on the properties a system should always satisfy, rather than checking individual examples. Instead of writing tests for predefined inputs and outputs, PBT allows you to specify the general characteristics that your code should adhere to and then automatically generates a wide range of inputs to verify these properties.
|
@@ -39,29 +41,34 @@ Off course you can install with `gem intstall pbt`.
|
|
39
41
|
### Simple property
|
40
42
|
|
41
43
|
```ruby
|
42
|
-
# Let's say you have
|
43
|
-
def
|
44
|
-
|
44
|
+
# Let's say you have your own sort method.
|
45
|
+
def sort(array)
|
46
|
+
return array if array.size <= 2 # Here's a bug! It should be 1.
|
47
|
+
pivot, *rest = array
|
48
|
+
left, right = rest.partition { |n| n <= pivot }
|
49
|
+
sort(left) + [pivot] + sort(right)
|
45
50
|
end
|
46
51
|
|
47
52
|
Pbt.assert do
|
48
|
-
# The given block is executed 100 times with different random numbers.
|
53
|
+
# The given block is executed 100 times with different arrays with random numbers.
|
49
54
|
# Besides, the block runs in parallel by Ractor.
|
50
|
-
Pbt.property(Pbt.integer) do |
|
51
|
-
result =
|
52
|
-
|
55
|
+
Pbt.property(Pbt.array(Pbt.integer)) do |numbers|
|
56
|
+
result = sort(numbers)
|
57
|
+
result.each_cons(2) do |x, y|
|
58
|
+
raise "Sort algorithm is wrong." unless x <= y
|
59
|
+
end
|
53
60
|
end
|
54
61
|
end
|
55
62
|
|
56
|
-
# If the
|
57
|
-
# For example, the
|
63
|
+
# If the method has a bug, the test fails and it reports a minimum counterexample.
|
64
|
+
# For example, the sort method doesn't work for [0, -1].
|
58
65
|
#
|
59
66
|
# Pbt::PropertyFailure:
|
60
67
|
# Property failed after 23 test(s)
|
61
|
-
#
|
62
|
-
#
|
63
|
-
# Shrunk
|
64
|
-
# Got
|
68
|
+
# seed: 43738985293126714007411539287084402325
|
69
|
+
# counterexample: [0, -1]
|
70
|
+
# Shrunk 40 time(s)
|
71
|
+
# Got RuntimeError: Sort algorithm is wrong.
|
65
72
|
```
|
66
73
|
|
67
74
|
### Explain The Snippet
|
@@ -92,7 +99,7 @@ Here, we used only one type of arbitrary, `Pbt.integer`. There are many other bu
|
|
92
99
|
In PBT, If a test fails, it attempts to shrink the case that caused the failure into a form that is easier for humans to understand.
|
93
100
|
In other words, instead of stopping the test itself the first time it fails and reporting the failed value, it tries to find the minimal value that causes the error.
|
94
101
|
|
95
|
-
When there is a test that fails when given an even number, a counterexample of `
|
102
|
+
When there is a test that fails when given an even number, a counterexample of `[0, -1]` is simpler and easier to understand than any complex example like `[-897860, -930517, 577817, -16302, 310864, 856411, -304517, 86613, -78231]`.
|
96
103
|
|
97
104
|
### Arbitrary
|
98
105
|
|
@@ -103,16 +110,16 @@ There are many built-in arbitraries in `Pbt`. You can use them to generate rando
|
|
103
110
|
```ruby
|
104
111
|
rng = Random.new
|
105
112
|
|
106
|
-
Pbt.integer.generate(rng)
|
113
|
+
Pbt.integer.generate(rng) # => 42
|
107
114
|
Pbt.integer(min: -1, max: 8).generate(rng) # => Integer between -1 and 8
|
108
115
|
|
109
|
-
Pbt.symbol.generate(rng)
|
116
|
+
Pbt.symbol.generate(rng) # => :atq
|
110
117
|
|
111
|
-
Pbt.ascii_char.generate(rng)
|
112
|
-
Pbt.ascii_string.generate(rng)
|
118
|
+
Pbt.ascii_char.generate(rng) # => "a"
|
119
|
+
Pbt.ascii_string.generate(rng) # => "aagjZfao"
|
113
120
|
|
114
|
-
Pbt.boolean.generate(rng)
|
115
|
-
Pbt.constant(42).generate(rng)
|
121
|
+
Pbt.boolean.generate(rng) # => true or false
|
122
|
+
Pbt.constant(42).generate(rng) # => 42 always
|
116
123
|
```
|
117
124
|
|
118
125
|
#### Composites
|
@@ -120,15 +127,15 @@ Pbt.constant(42).generate(rng) # => 42 always
|
|
120
127
|
```ruby
|
121
128
|
rng = Random.new
|
122
129
|
|
123
|
-
Pbt.array(Pbt.integer).generate(rng)
|
124
|
-
Pbt.array(Pbt.integer, max: 1, empty: true).generate(rng)
|
130
|
+
Pbt.array(Pbt.integer).generate(rng) # => [121, -13141, 9825]
|
131
|
+
Pbt.array(Pbt.integer, max: 1, empty: true).generate(rng) # => [] or [42] etc.
|
125
132
|
|
126
|
-
Pbt.tuple(Pbt.symbol, Pbt.integer).generate(rng)
|
133
|
+
Pbt.tuple(Pbt.symbol, Pbt.integer).generate(rng) # => [:atq, 42]
|
127
134
|
|
128
135
|
Pbt.fixed_hash(x: Pbt.symbol, y: Pbt.integer).generate(rng) # => {x: :atq, y: 42}
|
129
|
-
Pbt.hash(Pbt.symbol, Pbt.integer).generate(rng)
|
136
|
+
Pbt.hash(Pbt.symbol, Pbt.integer).generate(rng) # => {atq: 121, ygab: -1142}
|
130
137
|
|
131
|
-
Pbt.one_of(:a, 1, 0.1).generate(rng)
|
138
|
+
Pbt.one_of(:a, 1, 0.1).generate(rng) # => :a or 1 or 0.1
|
132
139
|
````
|
133
140
|
|
134
141
|
See [ArbitraryMethods](https://github.com/ohbarye/pbt/blob/main/lib/pbt/arbitrary/arbitrary_methods.rb) module for more details.
|
@@ -144,18 +151,18 @@ When a test fails, you'll see a message like below.
|
|
144
151
|
```text
|
145
152
|
Pbt::PropertyFailure:
|
146
153
|
Property failed after 23 test(s)
|
147
|
-
|
148
|
-
|
149
|
-
Shrunk
|
150
|
-
Got
|
154
|
+
seed: 43738985293126714007411539287084402325
|
155
|
+
counterexample: [0, -1]
|
156
|
+
Shrunk 40 time(s)
|
157
|
+
Got RuntimeError: Sort algorithm is wrong.
|
151
158
|
# and backtraces
|
152
159
|
```
|
153
160
|
|
154
161
|
You can reproduce the failure by passing the seed to `Pbt.assert`.
|
155
162
|
|
156
163
|
```ruby
|
157
|
-
Pbt.assert(seed:
|
158
|
-
Pbt.property(Pbt.integer) do |number|
|
164
|
+
Pbt.assert(seed: 43738985293126714007411539287084402325) do
|
165
|
+
Pbt.property(Pbt.array(Pbt.integer)) do |number|
|
159
166
|
# your test
|
160
167
|
end
|
161
168
|
end
|
@@ -177,25 +184,29 @@ The verbose mode prints the results of each tested values.
|
|
177
184
|
|
178
185
|
```text
|
179
186
|
Encountered failures were:
|
180
|
-
- [
|
181
|
-
- [
|
182
|
-
- [
|
187
|
+
- [-897860, -930517, 577817, -16302, 310864, 856411, -304517, 86613, -78231]
|
188
|
+
- [310864, 856411, -304517, 86613, -78231]
|
189
|
+
- [-304517, 86613, -78231]
|
183
190
|
(snipped for README)
|
184
|
-
- [
|
185
|
-
- [
|
191
|
+
- [0, -3]
|
192
|
+
- [0, -2]
|
193
|
+
- [0, -1]
|
186
194
|
|
187
195
|
Execution summary:
|
188
|
-
. × [
|
189
|
-
. . √ [
|
190
|
-
. . √ [-
|
191
|
-
. .
|
192
|
-
. .
|
196
|
+
. × [-897860, -930517, 577817, -16302, 310864, 856411, -304517, 86613, -78231]
|
197
|
+
. . √ [-897860, -930517, 577817, -16302, 310864]
|
198
|
+
. . √ [-930517, 577817, -16302, 310864, 856411]
|
199
|
+
. . √ [577817, -16302, 310864, 856411, -304517]
|
200
|
+
. . √ [-16302, 310864, 856411, -304517, 86613]
|
201
|
+
. . × [310864, 856411, -304517, 86613, -78231]
|
193
202
|
(snipped for README)
|
194
|
-
. . . . . . . . . . . . . . . . . √ [2
|
195
|
-
. . . . . . . . . . . . . . . . .
|
196
|
-
. . . . . . . . . . . . . . . . . .
|
197
|
-
. . . . . . . . . . . . . . . . . . √ [
|
198
|
-
. . . . . . . . . . . . . . . . . . √ [
|
203
|
+
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . √ [-2]
|
204
|
+
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . √ []
|
205
|
+
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . × [0, -1]
|
206
|
+
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . √ [0]
|
207
|
+
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . √ [-1]
|
208
|
+
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . √ []
|
209
|
+
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . √ [0, 0]
|
199
210
|
```
|
200
211
|
|
201
212
|
## Configuration
|
@@ -365,6 +376,8 @@ Once this project finishes the following, we will release v1.0.0.
|
|
365
376
|
- [ ] Implement frequency arbitrary
|
366
377
|
- [ ] Statistics feature to aggregate generated values
|
367
378
|
- [ ] Decide DSL
|
379
|
+
- [ ] Try Fiber
|
380
|
+
- [ ] Stateful property-based testing
|
368
381
|
|
369
382
|
## Development
|
370
383
|
|
@@ -5,7 +5,7 @@ begin
|
|
5
5
|
require "prism"
|
6
6
|
rescue LoadError
|
7
7
|
raise InvalidConfiguration,
|
8
|
-
"Prism gem (https://github.com/
|
8
|
+
"Prism gem (https://github.com/ruby/prism) is required to use worker `:ractor` and `:experimental_ractor_rspec_integration`. Please add `gem 'prism'` to your Gemfile."
|
9
9
|
end
|
10
10
|
|
11
11
|
module Pbt
|
@@ -11,7 +11,7 @@ module Pbt
|
|
11
11
|
def setup_rspec_integration
|
12
12
|
filepath, line = @predicate.source_location
|
13
13
|
basename = File.basename(filepath, ".rb")
|
14
|
-
@class_name = basename.split("_").map(&:capitalize).join + line.to_s
|
14
|
+
@class_name = "Test" + basename.split("_").map(&:capitalize).join + line.to_s
|
15
15
|
@method_name = "predicate_#{basename}_#{line}"
|
16
16
|
define_ractor_callable_class
|
17
17
|
end
|
data/lib/pbt/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pbt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ohbarye
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-05-
|
11
|
+
date: 2024-05-23 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|