speculation 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +13 -0
- data/.travis.yml +2 -4
- data/README.md +22 -12
- data/Rakefile +5 -9
- data/bin/console +54 -7
- data/examples/codebreaker.rb +246 -0
- data/examples/spec_guide.rb +1288 -0
- data/lib/speculation.rb +145 -146
- data/lib/speculation/error.rb +4 -3
- data/lib/speculation/gen.rb +51 -47
- data/lib/speculation/identifier.rb +7 -7
- data/lib/speculation/namespaced_symbols.rb +26 -19
- data/lib/speculation/pmap.rb +9 -10
- data/lib/speculation/spec_impl/and_spec.rb +3 -4
- data/lib/speculation/spec_impl/every_spec.rb +24 -24
- data/lib/speculation/spec_impl/f_spec.rb +32 -35
- data/lib/speculation/spec_impl/hash_spec.rb +33 -41
- data/lib/speculation/spec_impl/merge_spec.rb +2 -3
- data/lib/speculation/spec_impl/nilable_spec.rb +8 -9
- data/lib/speculation/spec_impl/or_spec.rb +5 -7
- data/lib/speculation/spec_impl/regex_spec.rb +2 -3
- data/lib/speculation/spec_impl/spec.rb +3 -5
- data/lib/speculation/spec_impl/tuple_spec.rb +8 -10
- data/lib/speculation/test.rb +126 -101
- data/lib/speculation/utils.rb +31 -5
- data/lib/speculation/version.rb +1 -1
- data/speculation.gemspec +0 -1
- metadata +30 -44
- data/lib/speculation/conj.rb +0 -32
- data/lib/speculation/utils_specs.rb +0 -57
data/lib/speculation/error.rb
CHANGED
@@ -3,15 +3,16 @@ require "pp"
|
|
3
3
|
|
4
4
|
module Speculation
|
5
5
|
class Error < StandardError
|
6
|
-
attr_reader :data
|
6
|
+
attr_reader :data, :message
|
7
7
|
|
8
8
|
def initialize(message, data)
|
9
9
|
super(message)
|
10
|
-
@data = data
|
10
|
+
@data = data
|
11
|
+
@message = message
|
11
12
|
end
|
12
13
|
|
13
14
|
def to_s
|
14
|
-
PP.pp(@data, String.new)
|
15
|
+
"#{@message} #{PP.pp(@data, String.new)}"
|
15
16
|
end
|
16
17
|
end
|
17
18
|
end
|
data/lib/speculation/gen.rb
CHANGED
@@ -5,54 +5,13 @@ require "rantly/property"
|
|
5
5
|
require "rantly/shrinks"
|
6
6
|
require "concurrent/delay"
|
7
7
|
require "date"
|
8
|
+
require "securerandom"
|
9
|
+
require "uri"
|
10
|
+
require "date"
|
11
|
+
require "time"
|
8
12
|
|
9
13
|
module Speculation
|
10
|
-
using NamespacedSymbols.refine(self)
|
11
|
-
|
12
14
|
module Gen
|
13
|
-
# @private
|
14
|
-
GEN_BUILTINS = {
|
15
|
-
Integer => ->(r) { r.integer },
|
16
|
-
String => ->(r) { r.sized(r.range(0, 100)) { string(:alpha) } },
|
17
|
-
Float => ->(_r) { rand(Float::MIN..Float::MAX) },
|
18
|
-
Numeric => ->(r) { r.branch(Gen.gen_for_pred(Integer), Gen.gen_for_pred(Float)) },
|
19
|
-
Symbol => ->(r) { r.sized(r.range(0, 100)) { string(:alpha).to_sym } },
|
20
|
-
TrueClass => ->(_r) { true },
|
21
|
-
FalseClass => ->(_r) { false },
|
22
|
-
Date => ->(r) { Gen.gen_for_pred(Time).call(r).to_date },
|
23
|
-
Time => ->(r) { Time.at(r.range(-569001744000, 569001744000)) }, # 20k BC => 20k AD
|
24
|
-
Array => ->(r) do
|
25
|
-
size = r.range(0, 20)
|
26
|
-
|
27
|
-
r.array(size) do
|
28
|
-
gen = Gen.gen_for_pred(r.choose(Integer, String, Float, Symbol, Date, Time, Set[true, false]))
|
29
|
-
gen.call(r)
|
30
|
-
end
|
31
|
-
end,
|
32
|
-
Set => ->(r) do
|
33
|
-
gen = Gen.gen_for_pred(Array)
|
34
|
-
Set.new(gen.call(r))
|
35
|
-
end,
|
36
|
-
Hash => ->(r) do
|
37
|
-
kgen = Gen.gen_for_pred(r.choose(Integer, String, Float, Symbol, Date, Time))
|
38
|
-
vgen = Gen.gen_for_pred(r.choose(Integer, String, Float, Symbol, Date, Time, Set[true, false]))
|
39
|
-
size = r.range(0, 20)
|
40
|
-
|
41
|
-
h = {}
|
42
|
-
r.each(size) do
|
43
|
-
k = kgen.call(r)
|
44
|
-
r.guard(!h.key?(k))
|
45
|
-
h[k] = vgen.call(r)
|
46
|
-
end
|
47
|
-
h
|
48
|
-
end,
|
49
|
-
Enumerable => ->(r) do
|
50
|
-
klass = r.choose(Array, Hash, Set)
|
51
|
-
gen = Gen.gen_for_pred(klass)
|
52
|
-
gen.call(r)
|
53
|
-
end
|
54
|
-
}.freeze
|
55
|
-
|
56
15
|
# Adds `pred` as a Rantly `guard` to generator `gen`.
|
57
16
|
# @param pred
|
58
17
|
# @param gen [Proc]
|
@@ -77,7 +36,7 @@ module Speculation
|
|
77
36
|
# @param limit [Integer] specifies how many times `gen` can fail to produce a valid value.
|
78
37
|
# @return [Array] array of generated values using gne
|
79
38
|
# @see https://github.com/abargnesi/rantly Rantly
|
80
|
-
def self.sample(gen, n, limit = 100)
|
39
|
+
def self.sample(gen, n = 10, limit = 100)
|
81
40
|
Rantly.map(n, limit, &gen)
|
82
41
|
end
|
83
42
|
|
@@ -99,8 +58,53 @@ module Speculation
|
|
99
58
|
delayed = Concurrent::Delay.new(&block)
|
100
59
|
|
101
60
|
->(rantly) do
|
102
|
-
delayed.value
|
61
|
+
delayed.value!.call(rantly)
|
103
62
|
end
|
104
63
|
end
|
64
|
+
|
65
|
+
# @private
|
66
|
+
GEN_BUILTINS = {
|
67
|
+
Integer => ->(r) { r.integer },
|
68
|
+
String => ->(r) { r.sized(r.range(0, 20)) { string(:alpha) } },
|
69
|
+
Float => ->(_r) { rand(Float::MIN..Float::MAX) },
|
70
|
+
Numeric => ->(r) { r.branch(Gen.gen_for_pred(Integer), Gen.gen_for_pred(Float)) },
|
71
|
+
Symbol => ->(r) { r.sized(r.range(0, 20)) { string(:alpha).to_sym } },
|
72
|
+
TrueClass => ->(_r) { true },
|
73
|
+
FalseClass => ->(_r) { false },
|
74
|
+
NilClass => ->(_r) { nil },
|
75
|
+
Date => Speculation.gen(Speculation.date_in(Date.new(1970, 1, 1)..Date.new(3000, 1, 1))),
|
76
|
+
Time => Speculation.gen(Speculation.time_in(Time.new(1970, 1, 1)..Time.new(3000, 1, 1))),
|
77
|
+
URI => ->(_r) { URI("http://#{SecureRandom.uuid}.com") },
|
78
|
+
Array => ->(r) do
|
79
|
+
size = r.range(0, 20)
|
80
|
+
|
81
|
+
r.array(size) do
|
82
|
+
gen = Gen.gen_for_pred(r.choose(Integer, String, Float, Symbol, Date, Time, Set[true, false]))
|
83
|
+
gen.call(r)
|
84
|
+
end
|
85
|
+
end,
|
86
|
+
Set => ->(r) do
|
87
|
+
gen = Gen.gen_for_pred(Array)
|
88
|
+
Set.new(gen.call(r))
|
89
|
+
end,
|
90
|
+
Hash => ->(r) do
|
91
|
+
kgen = Gen.gen_for_pred(r.choose(Integer, String, Float, Symbol, Date, Time))
|
92
|
+
vgen = Gen.gen_for_pred(r.choose(Integer, String, Float, Symbol, Date, Time, Set[true, false]))
|
93
|
+
size = r.range(0, 20)
|
94
|
+
|
95
|
+
h = {}
|
96
|
+
r.each(size) do
|
97
|
+
k = kgen.call(r)
|
98
|
+
r.guard(!h.key?(k))
|
99
|
+
h[k] = vgen.call(r)
|
100
|
+
end
|
101
|
+
h
|
102
|
+
end,
|
103
|
+
Enumerable => ->(r) do
|
104
|
+
klass = r.choose(Array, Hash, Set)
|
105
|
+
gen = Gen.gen_for_pred(klass)
|
106
|
+
gen.call(r)
|
107
|
+
end
|
108
|
+
}.freeze
|
105
109
|
end
|
106
110
|
end
|
@@ -5,22 +5,22 @@ module Speculation
|
|
5
5
|
class Identifier
|
6
6
|
attr_reader :namespace, :name
|
7
7
|
|
8
|
-
def initialize(namespace, name,
|
8
|
+
def initialize(namespace, name, is_instance_method)
|
9
9
|
@namespace = namespace
|
10
10
|
@name = name
|
11
|
-
@
|
11
|
+
@is_instance_method = is_instance_method
|
12
12
|
end
|
13
13
|
|
14
14
|
def instance_method?
|
15
|
-
@
|
15
|
+
@is_instance_method
|
16
16
|
end
|
17
17
|
|
18
18
|
def get_method
|
19
|
-
@
|
19
|
+
@is_instance_method ? @namespace.instance_method(@name) : @namespace.method(@name)
|
20
20
|
end
|
21
21
|
|
22
22
|
def redefine_method!(new_method)
|
23
|
-
if @
|
23
|
+
if @is_instance_method
|
24
24
|
name = @name
|
25
25
|
@namespace.class_eval { define_method(name, new_method) }
|
26
26
|
else
|
@@ -29,7 +29,7 @@ module Speculation
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def hash
|
32
|
-
[@namespace, @name, @
|
32
|
+
[@namespace, @name, @is_instance_method].hash
|
33
33
|
end
|
34
34
|
|
35
35
|
def ==(other)
|
@@ -39,7 +39,7 @@ module Speculation
|
|
39
39
|
alias eql? ==
|
40
40
|
|
41
41
|
def to_s
|
42
|
-
sep = @
|
42
|
+
sep = @is_instance_method ? "#" : "."
|
43
43
|
"#{@namespace}#{sep}#{@name}"
|
44
44
|
end
|
45
45
|
alias inspect to_s
|
@@ -2,27 +2,34 @@
|
|
2
2
|
|
3
3
|
module Speculation
|
4
4
|
module NamespacedSymbols
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
5
|
+
# @param [#to_s] namespace
|
6
|
+
# @param [#to_s] name
|
7
|
+
# @return [Symbol] concatenation of `namespace` and `name`
|
8
|
+
# @example
|
9
|
+
# ns(Foo::Bar, :foo)
|
10
|
+
# # => :"Foo::Bar/baz"
|
11
|
+
def ns(name_or_namespace, name = nil)
|
12
|
+
if name
|
13
|
+
namespace = name_or_namespace
|
14
|
+
else
|
15
|
+
name = name_or_namespace
|
16
|
+
namespace = is_a?(Module) ? self.name : self.class.name
|
17
|
+
end
|
15
18
|
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
+
NamespacedSymbols.symbol(namespace, name)
|
20
|
+
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
def self.symbol(ns, name)
|
23
|
+
:"#{ns}/#{name}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.name(sym)
|
27
|
+
sym.to_s.split("/").last
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.namespace(sym)
|
31
|
+
parts = sym.to_s.split("/")
|
32
|
+
parts.first if parts.count == 2
|
26
33
|
end
|
27
34
|
end
|
28
35
|
end
|
data/lib/speculation/pmap.rb
CHANGED
@@ -1,26 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
2
|
require "concurrent"
|
4
3
|
|
5
4
|
module Speculation
|
6
5
|
# @private
|
7
6
|
module Pmap
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
7
|
+
if RUBY_PLATFORM == "java"
|
8
|
+
def pmap(coll, &block)
|
9
|
+
Pmap.pmap_jruby(coll, &block)
|
10
|
+
end
|
11
|
+
else
|
12
|
+
def pmap(coll, &block)
|
13
|
+
coll.map(&block)
|
15
14
|
end
|
16
15
|
end
|
17
16
|
|
18
|
-
def self.pmap_jruby(
|
17
|
+
def self.pmap_jruby(coll, &block)
|
19
18
|
thread_count = [1, Concurrent.processor_count - 1].max
|
20
19
|
pool = Concurrent::FixedThreadPool.new(thread_count, :auto_terminate => true,
|
21
20
|
:fallback_policy => :abort)
|
22
21
|
|
23
|
-
|
22
|
+
coll.
|
24
23
|
map { |x| Concurrent::Future.execute(:executor => pool) { block.call(x) } }.
|
25
24
|
map { |f| f.value || f.reason }
|
26
25
|
ensure
|
@@ -1,10 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Speculation
|
4
|
-
using NamespacedSymbols.refine(self)
|
5
|
-
|
6
4
|
# @private
|
7
5
|
class AndSpec < SpecImpl
|
6
|
+
include NamespacedSymbols
|
8
7
|
S = Speculation
|
9
8
|
|
10
9
|
def initialize(preds)
|
@@ -15,10 +14,10 @@ module Speculation
|
|
15
14
|
end
|
16
15
|
|
17
16
|
def conform(value)
|
18
|
-
@specs.value
|
17
|
+
@specs.value!.each do |spec|
|
19
18
|
value = spec.conform(value)
|
20
19
|
|
21
|
-
return :invalid
|
20
|
+
return ns(S, :invalid) if S.invalid?(value)
|
22
21
|
end
|
23
22
|
|
24
23
|
value
|
@@ -1,10 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module Speculation
|
3
|
-
using NamespacedSymbols.refine(self)
|
4
|
-
using Conj
|
5
|
-
|
6
3
|
# @private
|
7
4
|
class EverySpec < SpecImpl
|
5
|
+
include NamespacedSymbols
|
8
6
|
S = Speculation
|
9
7
|
|
10
8
|
def initialize(predicate, options)
|
@@ -24,22 +22,22 @@ module Speculation
|
|
24
22
|
end)
|
25
23
|
end
|
26
24
|
|
27
|
-
@collection_predicate = ->(coll) { collection_predicates.all? { |f| f === coll } }
|
25
|
+
@collection_predicate = ->(coll) { collection_predicates.all? { |f| f.respond_to?(:call) ? f.call(coll) : f === coll } }
|
28
26
|
@delayed_spec = Concurrent::Delay.new { S.send(:specize, predicate) }
|
29
|
-
@kfn = options.fetch(:kfn
|
27
|
+
@kfn = options.fetch(ns(S, :kfn), ->(i, _v) { i })
|
30
28
|
@conform_keys, @conform_all, @kind, @gen_into, @gen_max, @distinct, @count, @min_count, @max_count =
|
31
|
-
options.values_at(:conform_keys, :conform_all
|
29
|
+
options.values_at(:conform_keys, ns(S, :conform_all), :kind, :into, :gen_max, :distinct, :count, :min_count, :max_count)
|
32
30
|
@gen_max ||= 20
|
33
31
|
@conform_into = @gen_into
|
34
32
|
|
35
33
|
# returns a tuple of [init add complete] fns
|
36
34
|
@cfns = ->(x) do
|
37
35
|
if Utils.array?(x) && (!@conform_into || Utils.array?(@conform_into))
|
38
|
-
[:itself
|
36
|
+
[Utils.method(:itself),
|
39
37
|
->(ret, i, v, cv) { v.equal?(cv) ? ret : ret.tap { |r| r[i] = cv } },
|
40
|
-
:itself
|
38
|
+
Utils.method(:itself)]
|
41
39
|
elsif Utils.hash?(x) && ((@kind && !@conform_into) || Utils.hash?(@conform_into))
|
42
|
-
[@conform_keys ? Utils.method(:empty) : :itself
|
40
|
+
[@conform_keys ? Utils.method(:empty) : Utils.method(:itself),
|
43
41
|
->(ret, _i, v, cv) {
|
44
42
|
if v.equal?(cv) && !@conform_keys
|
45
43
|
ret
|
@@ -47,19 +45,19 @@ module Speculation
|
|
47
45
|
ret.merge((@conform_keys ? cv : v).first => cv.last)
|
48
46
|
end
|
49
47
|
},
|
50
|
-
:itself
|
48
|
+
Utils.method(:itself)]
|
51
49
|
else
|
52
50
|
[->(init) { Utils.empty(@conform_into || init) },
|
53
|
-
->(ret, _i, _v, cv) {
|
54
|
-
:itself
|
51
|
+
->(ret, _i, _v, cv) { Utils.conj(ret, cv) },
|
52
|
+
Utils.method(:itself)]
|
55
53
|
end
|
56
54
|
end
|
57
55
|
end
|
58
56
|
|
59
57
|
def conform(value)
|
60
|
-
return :invalid
|
58
|
+
return ns(S, :invalid) unless @collection_predicate.call(value)
|
61
59
|
|
62
|
-
spec = @delayed_spec.value
|
60
|
+
spec = @delayed_spec.value!
|
63
61
|
|
64
62
|
if @conform_all
|
65
63
|
init, add, complete = @cfns.call(value)
|
@@ -70,7 +68,7 @@ module Speculation
|
|
70
68
|
conformed_value = spec.conform(val)
|
71
69
|
|
72
70
|
if S.invalid?(conformed_value)
|
73
|
-
return :invalid
|
71
|
+
return ns(S, :invalid)
|
74
72
|
else
|
75
73
|
return_value = add.call(return_value, index, val, conformed_value)
|
76
74
|
end
|
@@ -83,7 +81,7 @@ module Speculation
|
|
83
81
|
|
84
82
|
value.each_with_index do |item, index|
|
85
83
|
return value if index == limit
|
86
|
-
return :invalid
|
84
|
+
return ns(S, :invalid) unless S.valid?(spec, item)
|
87
85
|
end
|
88
86
|
|
89
87
|
value
|
@@ -94,13 +92,13 @@ module Speculation
|
|
94
92
|
probs = collection_problems(value, @kind, @distinct, @count, @min_count, @max_count, path, via, inn)
|
95
93
|
return probs if probs
|
96
94
|
|
97
|
-
spec = @delayed_spec.value
|
95
|
+
spec = @delayed_spec.value!
|
98
96
|
|
99
97
|
probs = value.lazy.each_with_index.flat_map { |v, i|
|
100
98
|
k = @kfn.call(i, v)
|
101
99
|
|
102
100
|
unless S.valid?(spec, v)
|
103
|
-
S.explain1(@predicate, path, via,
|
101
|
+
S.explain1(@predicate, path, via, Utils.conj(inn, k), v)
|
104
102
|
end
|
105
103
|
}
|
106
104
|
|
@@ -158,18 +156,20 @@ module Speculation
|
|
158
156
|
return S.explain1(pred, path, via, inn, x)
|
159
157
|
end
|
160
158
|
|
161
|
-
if count &&
|
162
|
-
return [{ :path => path, :pred =>
|
159
|
+
if count && !Utils.count_eq?(x, count)
|
160
|
+
return [{ :path => path, :pred => [Utils.method(:count_eq?), [x, count]], :val => x, :via => via, :in => inn }]
|
163
161
|
end
|
164
162
|
|
165
163
|
if min_count || max_count
|
166
|
-
|
167
|
-
|
164
|
+
min_count ||= 0
|
165
|
+
max_count ||= Float::INFINITY
|
166
|
+
unless Utils.count_between?(x, min_count, max_count)
|
167
|
+
return [{ :path => path, :pred => [Utils.method(:count_between?), [x, min_count, max_count]], :val => x, :via => via, :in => inn }]
|
168
168
|
end
|
169
169
|
end
|
170
170
|
|
171
|
-
if distinct && !x.empty? && Utils.distinct?(x)
|
172
|
-
[{ :path => path, :pred =>
|
171
|
+
if distinct && !x.empty? && !Utils.distinct?(x)
|
172
|
+
[{ :path => path, :pred => [Utils.method(:distinct?), [x]], :val => x, :via => via, :in => inn }]
|
173
173
|
end
|
174
174
|
end
|
175
175
|
end
|
@@ -1,62 +1,58 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Speculation
|
4
|
-
using NamespacedSymbols.refine(self)
|
5
|
-
using Conj
|
6
|
-
|
7
4
|
# @private
|
8
5
|
class FSpec < SpecImpl
|
6
|
+
include NamespacedSymbols
|
9
7
|
S = Speculation
|
10
8
|
|
11
|
-
attr_reader :
|
9
|
+
attr_reader :args, :ret, :fn, :block
|
12
10
|
|
13
|
-
def initialize(
|
14
|
-
@
|
15
|
-
@
|
16
|
-
@
|
17
|
-
@
|
11
|
+
def initialize(args: nil, ret: nil, fn: nil, block: nil)
|
12
|
+
@args = args
|
13
|
+
@ret = ret
|
14
|
+
@fn = fn
|
15
|
+
@block = block
|
18
16
|
end
|
19
17
|
|
20
18
|
def conform(f)
|
21
|
-
raise "Can't conform fspec without args spec: #{inspect}" unless @
|
19
|
+
raise "Can't conform fspec without args spec: #{inspect}" unless @args
|
22
20
|
|
23
|
-
return :invalid
|
21
|
+
return ns(S, :invalid) unless f.is_a?(Proc) || f.is_a?(Method)
|
24
22
|
|
25
|
-
specs = { :args => @
|
23
|
+
specs = { :args => @args, :ret => @ret, :fn => @fn, :block => @block }
|
26
24
|
|
27
25
|
if f.equal?(FSpec.validate_fn(f, specs, S.fspec_iterations))
|
28
26
|
f
|
29
27
|
else
|
30
|
-
:invalid
|
28
|
+
ns(S, :invalid)
|
31
29
|
end
|
32
30
|
end
|
33
31
|
|
34
32
|
def explain(path, via, inn, f)
|
35
33
|
unless f.respond_to?(:call)
|
36
|
-
return [{ :path => path, :pred =>
|
34
|
+
return [{ :path => path, :pred => [f.method(:respond_to?), [:call]], :val => f, :via => via, :in => inn }]
|
37
35
|
end
|
38
36
|
|
39
|
-
specs = { :args => @
|
40
|
-
|
41
|
-
return if f.equal?(
|
37
|
+
specs = { :args => @args, :ret => @ret, :fn => @fn, :block => @block }
|
38
|
+
validate_fn_result = FSpec.validate_fn(f, specs, 100)
|
39
|
+
return if f.equal?(validate_fn_result)
|
42
40
|
|
43
|
-
ret =
|
44
|
-
f.call(*args, &block)
|
45
|
-
rescue => e
|
46
|
-
e
|
47
|
-
end
|
41
|
+
ret = f.call(*validate_fn_result[:args], &validate_fn_result[:block]) rescue $!
|
48
42
|
|
49
43
|
if ret.is_a?(Exception)
|
50
|
-
|
51
|
-
|
44
|
+
# no args available for pred
|
45
|
+
pred = [f, validate_fn_result[:args]]
|
46
|
+
pred << validate_fn_result[:block] if validate_fn_result[:block]
|
47
|
+
return [{ :path => path, :pred => pred, :val => validate_fn_result, :reason => ret.message.chomp, :via => via, :in => inn }]
|
52
48
|
end
|
53
49
|
|
54
|
-
cret = S.dt(@
|
55
|
-
return S.explain1(@
|
50
|
+
cret = S.dt(@ret, ret)
|
51
|
+
return S.explain1(@ret, Utils.conj(path, :ret), via, inn, ret) if S.invalid?(cret)
|
56
52
|
|
57
|
-
if @
|
58
|
-
cargs = S.conform(@
|
59
|
-
S.explain1(@
|
53
|
+
if @fn
|
54
|
+
cargs = S.conform(@args, args)
|
55
|
+
S.explain1(@fn, Utils.conj(path, :fn), via, inn, :args => cargs, :ret => cret)
|
60
56
|
end
|
61
57
|
end
|
62
58
|
|
@@ -65,15 +61,15 @@ module Speculation
|
|
65
61
|
|
66
62
|
->(_rantly) do
|
67
63
|
->(*args, &block) do
|
68
|
-
unless S.pvalid?(@
|
69
|
-
raise S.explain_str(@
|
64
|
+
unless S.pvalid?(@args, args)
|
65
|
+
raise S.explain_str(@args, args)
|
70
66
|
end
|
71
67
|
|
72
|
-
if @
|
73
|
-
raise S.explain_str(@
|
68
|
+
if @block && !S.pvalid?(@block, block)
|
69
|
+
raise S.explain_str(@block, block)
|
74
70
|
end
|
75
71
|
|
76
|
-
S::Gen.generate(S.gen(@
|
72
|
+
S::Gen.generate(S.gen(@ret, overrides))
|
77
73
|
end
|
78
74
|
end
|
79
75
|
end
|
@@ -91,7 +87,8 @@ module Speculation
|
|
91
87
|
|
92
88
|
combined = ->(r) { [args_gen.call(r), block_gen.call(r)] }
|
93
89
|
|
94
|
-
|
90
|
+
generator_guard = ->(genned_val) { S.valid?(specs[:args], genned_val) }
|
91
|
+
ret = S::Test.send(:rantly_quick_check, combined, iterations, generator_guard) { |(args, block)|
|
95
92
|
call_valid?(f, specs, args, block)
|
96
93
|
}
|
97
94
|
|