speculation 0.1.0 → 0.2.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/.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
|
|