speculation 0.4.0 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -19,12 +19,16 @@ module Speculation
19
19
  ms = @preds.map { |pred| S.dt(pred, x) }
20
20
 
21
21
  if ms.any?(&S.method(:invalid?))
22
- S::INVALID
22
+ :"Speculation/invalid"
23
23
  else
24
24
  ms.reduce(&:merge)
25
25
  end
26
26
  end
27
27
 
28
+ def unform(x)
29
+ @preds.reverse.map { |pred| S.unform(pred, x) }.reduce(&:merge)
30
+ end
31
+
28
32
  def explain(path, via, inn, x)
29
33
  @preds.
30
34
  flat_map { |pred| S.explain1(pred, path, via, inn, x) }.
@@ -36,7 +40,7 @@ module Speculation
36
40
  end
37
41
 
38
42
  def gen(overrides, path, rmap)
39
- return @gen if @gen
43
+ return @gen.call if @gen
40
44
 
41
45
  gens = @preds.
42
46
  map { |pred| S.gensub(pred, overrides, path, rmap) }
@@ -20,12 +20,16 @@ module Speculation
20
20
  value.nil? ? value : @delayed_spec.value!.conform(value)
21
21
  end
22
22
 
23
+ def unform(value)
24
+ value.nil? ? nil : @delayed_spec.value!.unform(value)
25
+ end
26
+
23
27
  def explain(path, via, inn, value)
24
28
  return if S.pvalid?(@delayed_spec.value!, value) || value.nil?
25
29
 
26
30
  Utils.conj(
27
- S.explain1(@pred, Utils.conj(path, ns(S, :pred)), via, inn, value),
28
- :path => Utils.conj(path, ns(S, :nil)), :pred => [NilClass, [value]], :val => value, :via => via, :in => inn
31
+ S.explain1(@pred, Utils.conj(path, :pred), via, inn, value),
32
+ :path => Utils.conj(path, :nil), :pred => [NilClass, [value]], :val => value, :via => via, :in => inn
29
33
  )
30
34
  end
31
35
 
@@ -34,11 +38,11 @@ module Speculation
34
38
  end
35
39
 
36
40
  def gen(overrides, path, rmap)
37
- return @gen if @gen
41
+ return @gen.call if @gen
38
42
 
39
43
  ->(rantly) do
40
44
  rantly.freq([1, Gen.delay { Utils.constantly(nil) }],
41
- [9, Gen.delay { S.gensub(@pred, overrides, Utils.conj(path, ns(S, :pred)), rmap) }])
45
+ [9, Gen.delay { S.gensub(@pred, overrides, Utils.conj(path, :pred), rmap) }])
42
46
  end
43
47
  end
44
48
  end
@@ -19,7 +19,11 @@ module Speculation
19
19
  def conform(value)
20
20
  ret = @delayed_spec.value!.conform(value)
21
21
 
22
- S.invalid?(ret) ? S::INVALID : value
22
+ S.invalid?(ret) ? :"Speculation/invalid" : value
23
+ end
24
+
25
+ def unform(value)
26
+ value
23
27
  end
24
28
 
25
29
  def explain(path, via, inn, value)
@@ -33,13 +33,20 @@ module Speculation
33
33
  end
34
34
  end
35
35
 
36
- S::INVALID
36
+ :"Speculation/invalid"
37
+ end
38
+
39
+ def unform(value)
40
+ spec_name, conformed_val = value
41
+ spec = @named_specs.fetch(spec_name)
42
+
43
+ S.unform(spec, conformed_val)
37
44
  end
38
45
 
39
46
  def explain(path, via, inn, value)
40
47
  return if S.pvalid?(self, value)
41
48
 
42
- @keys.zip(@preds).flat_map do |(key, pred)|
49
+ @named_specs.flat_map do |(key, pred)|
43
50
  next if S.pvalid?(pred, value)
44
51
  S.explain1(pred, Utils.conj(path, key), via, inn, value)
45
52
  end
@@ -50,7 +57,7 @@ module Speculation
50
57
  end
51
58
 
52
59
  def gen(overrides, path, rmap)
53
- return @gen if @gen
60
+ return @gen.call if @gen
54
61
 
55
62
  gs = @keys.zip(@preds).
56
63
  map { |(k, p)|
@@ -10,10 +10,11 @@ module Speculation
10
10
  include NamespacedSymbols
11
11
  S = Speculation
12
12
 
13
- def initialize(predicate, should_conform, gen = nil)
13
+ def initialize(predicate, should_conform, gen, unconformer)
14
14
  @predicate = predicate
15
15
  @should_conform = should_conform
16
16
  @gen = gen
17
+ @unconformer = unconformer
17
18
  end
18
19
 
19
20
  def conform(value)
@@ -26,7 +27,17 @@ module Speculation
26
27
  if @should_conform
27
28
  ret
28
29
  else
29
- ret ? value : S::INVALID
30
+ ret ? value : :"Speculation/invalid"
31
+ end
32
+ end
33
+
34
+ def unform(value)
35
+ return value unless @should_conform
36
+
37
+ if @unconformer
38
+ @unconformer.call(value)
39
+ else
40
+ raise "no unformer for conformer"
30
41
  end
31
42
  end
32
43
 
@@ -37,12 +48,12 @@ module Speculation
37
48
  end
38
49
 
39
50
  def with_gen(gen)
40
- self.class.new(@predicate, @should_conform, gen)
51
+ self.class.new(@predicate, @should_conform, gen, @unconformer)
41
52
  end
42
53
 
43
54
  def gen(_, _, _)
44
55
  if @gen
45
- @gen
56
+ @gen.call
46
57
  else
47
58
  Gen.gen_for_pred(@predicate)
48
59
  end
@@ -16,15 +16,19 @@ module Speculation
16
16
  end
17
17
 
18
18
  def conform(value)
19
- if value.nil? || Utils.collection?(value)
19
+ if value.nil? || Predicates.collection?(value)
20
20
  S.re_conform(@regex, value)
21
21
  else
22
- S::INVALID
22
+ :"Speculation/invalid"
23
23
  end
24
24
  end
25
25
 
26
+ def unform(value)
27
+ S.op_unform(@regex, value)
28
+ end
29
+
26
30
  def explain(path, via, inn, value)
27
- if value.nil? || Utils.collection?(value)
31
+ if value.nil? || Predicates.collection?(value)
28
32
  S.re_explain(path, via, inn, @regex, value || [])
29
33
  else
30
34
  [{ :path => path, :val => value, :via => via, :in => inn }]
@@ -36,7 +40,7 @@ module Speculation
36
40
  end
37
41
 
38
42
  def gen(overrides, path, rmap)
39
- return @gen if @gen
43
+ return @gen.call if @gen
40
44
 
41
45
  S.re_gen(@regex, overrides, path, rmap)
42
46
  end
@@ -22,8 +22,8 @@ module Speculation
22
22
  def conform(collection)
23
23
  specs = @delayed_specs.value!
24
24
 
25
- unless Utils.array?(collection) && collection.count == specs.count
26
- return S::INVALID
25
+ unless Predicates.array?(collection) && collection.count == specs.count
26
+ return :"Speculation/invalid"
27
27
  end
28
28
 
29
29
  return_value = collection.class.new
@@ -32,7 +32,7 @@ module Speculation
32
32
  conformed_value = spec.conform(value)
33
33
 
34
34
  if S.invalid?(conformed_value)
35
- return S::INVALID
35
+ return :"Speculation/invalid"
36
36
  else
37
37
  return_value += [conformed_value]
38
38
  end
@@ -41,9 +41,17 @@ module Speculation
41
41
  return_value
42
42
  end
43
43
 
44
+ def unform(value)
45
+ unless Predicates.array?(value) && value.count == @preds.count
46
+ raise ArgumentError, "unform value must be an array of length #{@preds.count}"
47
+ end
48
+
49
+ @preds.zip(value).map { |(pred, val)| S.unform(pred, val) }
50
+ end
51
+
44
52
  def explain(path, via, inn, value)
45
- if !Utils.array?(value)
46
- [{ :path => path, :val => value, :via => via, :in => inn, :pred => [Utils.method(:array?), [value]] }]
53
+ if !Predicates.array?(value)
54
+ [{ :path => path, :val => value, :via => via, :in => inn, :pred => [Predicates.method(:array?), [value]] }]
47
55
  elsif @preds.count != value.count
48
56
  [{ :path => path, :val => value, :via => via, :in => inn, :pred => [Utils.method(:count_eq), [@preds, value.count]] }]
49
57
  else
@@ -62,7 +70,7 @@ module Speculation
62
70
  end
63
71
 
64
72
  def gen(overrides, path, rmap)
65
- return @gen if @gen
73
+ return @gen.call if @gen
66
74
 
67
75
  gens = @preds.each_with_index.
68
76
  map { |p, i| S.gensub(p, overrides, Utils.conj(path, i), rmap) }
@@ -148,7 +148,7 @@ module Speculation
148
148
  # * :result optional boolean as to whether all generative tests passed
149
149
  # * :num_tests optional number of generative tests ran
150
150
  #
151
- # :failure is a hash that will contain a :"Speculation/failure" key with possible values:
151
+ # :failure is a hash that will contain a :failure key with possible values:
152
152
  #
153
153
  # * :check_failed at least one checked return did not conform
154
154
  # * :no_args_spec no :args spec provided
@@ -174,11 +174,11 @@ module Speculation
174
174
  # @return [Hash]
175
175
  def self.abbrev_result(x)
176
176
  if x[:failure]
177
- x.reject { |k, _| k == ns(:ret) }.
177
+ x.reject { |k, _| k == :ret }.
178
178
  merge(:spec => x[:spec].inspect,
179
179
  :failure => unwrap_failure(x[:failure]))
180
180
  else
181
- x.reject { |k, _| [:spec, ns(:ret)].include?(k) }
181
+ x.reject { |k, _| [:spec, :ret].include?(k) }
182
182
  end
183
183
  end
184
184
 
@@ -223,30 +223,30 @@ module Speculation
223
223
  conformed_args = S.conform(fspec.args, args)
224
224
  conformed_block = S.conform(fspec.block, block) if fspec.block
225
225
 
226
- if conformed_args == S::INVALID
226
+ if conformed_args == :"Speculation/invalid"
227
227
  backtrace = backtrace_relevant_to_instrument(caller)
228
228
 
229
229
  ed = S.
230
230
  _explain_data(fspec.args, [:args], [], [], args).
231
- merge(ns(S, :args) => args, ns(S, :failure) => :instrument, ns(:caller) => backtrace.first)
231
+ merge(:args => args, :failure => :instrument, :caller => backtrace.first)
232
232
 
233
233
  io = StringIO.new
234
234
  S.explain_out(ed, io)
235
235
  msg = io.string
236
236
 
237
- raise Speculation::Error.new("Call to '#{ident}' did not conform to spec:\n #{msg}", ed)
238
- elsif conformed_block == S::INVALID
237
+ raise Speculation::Error.new("Call to '#{ident}' did not conform to spec:\n#{msg}", ed)
238
+ elsif conformed_block == :"Speculation/invalid"
239
239
  backtrace = backtrace_relevant_to_instrument(caller)
240
240
 
241
241
  ed = S.
242
242
  _explain_data(fspec.block, [:block], [], [], block).
243
- merge(ns(S, :block) => block, ns(S, :failure) => :instrument, ns(:caller) => backtrace.first)
243
+ merge(:block => block, :failure => :instrument, :caller => backtrace.first)
244
244
 
245
245
  io = StringIO.new
246
246
  S.explain_out(ed, io)
247
247
  msg = io.string
248
248
 
249
- raise Speculation::Error.new("Call to '#{ident}' did not conform to spec:\n #{msg}", ed)
249
+ raise Speculation::Error.new("Call to '#{ident}' did not conform to spec:\n#{msg}", ed)
250
250
  end
251
251
  end
252
252
 
@@ -271,7 +271,7 @@ module Speculation
271
271
  end
272
272
 
273
273
  def no_fspec(ident, spec)
274
- S::Error.new("#{ident} not spec'ed", :method => ident, :spec => spec, ns(S, :failure) => :no_fspec)
274
+ S::Error.new("#{ident} not spec'ed", :method => ident, :spec => spec, :failure => :no_fspec)
275
275
  end
276
276
 
277
277
  def instrument1(ident, opts)
@@ -343,9 +343,9 @@ module Speculation
343
343
  def explain_check(args, spec, v, role)
344
344
  data = unless S.valid?(spec, v)
345
345
  S._explain_data(spec, [role], [], [], v).
346
- merge(ns(:args) => args,
347
- ns(:val) => v,
348
- ns(S, :failure) => :check_failed)
346
+ merge(:args => args,
347
+ :val => v,
348
+ :failure => :check_failed)
349
349
  end
350
350
 
351
351
  S::Error.new("Specification-based check failed", data).tap do |e|
@@ -355,17 +355,17 @@ module Speculation
355
355
 
356
356
  # Returns true if call passes specs, otherwise returns a hash with
357
357
  # :backtrace, :cause and :data keys. :data will have a
358
- # :"Speculation/failure" key.
358
+ # :failure key.
359
359
  def check_call(method, spec, args, block)
360
360
  conformed_args = S.conform(spec.args, args) if spec.args
361
361
 
362
- if conformed_args == S::INVALID
362
+ if conformed_args == :"Speculation/invalid"
363
363
  return explain_check(args, spec.args, args, :args)
364
364
  end
365
365
 
366
366
  conformed_block = S.conform(spec.block, block) if spec.block
367
367
 
368
- if conformed_block == S::INVALID
368
+ if conformed_block == :"Speculation/invalid"
369
369
  return explain_check(block, spec.block, block, :block)
370
370
  end
371
371
 
@@ -373,7 +373,7 @@ module Speculation
373
373
 
374
374
  conformed_ret = S.conform(spec.ret, ret) if spec.ret
375
375
 
376
- if conformed_ret == S::INVALID
376
+ if conformed_ret == :"Speculation/invalid"
377
377
  return explain_check(args, spec.ret, ret, :ret)
378
378
  end
379
379
 
@@ -415,9 +415,9 @@ module Speculation
415
415
  end
416
416
 
417
417
  def make_check_result(method, spec, check_result)
418
- result = { :spec => spec,
419
- ns(:ret) => check_result,
420
- :method => method }
418
+ result = { :spec => spec,
419
+ :ret => check_result,
420
+ :method => method }
421
421
 
422
422
  if check_result[:result] && check_result[:result] != true
423
423
  result[:failure] = check_result[:result]
@@ -440,8 +440,8 @@ module Speculation
440
440
  check_result = quick_check(method, spec, opts)
441
441
  make_check_result(method, spec, check_result)
442
442
  else
443
- failure = { :info => "No :args spec",
444
- ns(:failure) => :no_args_spec }
443
+ failure = { :info => "No :args spec",
444
+ :failure => :no_args_spec }
445
445
 
446
446
  { :failure => failure,
447
447
  :method => method,
@@ -540,7 +540,7 @@ module Speculation
540
540
  ### check reporting ###
541
541
 
542
542
  def failure_type(x)
543
- x.data[ns(S, :failure)] if x.is_a?(S::Error)
543
+ x.data[:failure] if x.is_a?(S::Error)
544
544
  end
545
545
 
546
546
  def unwrap_failure(x)
@@ -548,7 +548,7 @@ module Speculation
548
548
  end
549
549
 
550
550
  # Returns the type of the check result. This can be any of the
551
- # :"Speculation/failure" symbols documented in 'check', or:
551
+ # :failure symbols documented in 'check', or:
552
552
  #
553
553
  # :check_passed all checked method returns conformed
554
554
  # :check_raised checked fn threw an exception
@@ -5,18 +5,6 @@ require "set"
5
5
  module Speculation
6
6
  # @private
7
7
  module Utils
8
- def self.hash?(x)
9
- x.respond_to?(:store)
10
- end
11
-
12
- def self.array?(x)
13
- x.respond_to?(:at)
14
- end
15
-
16
- def self.collection?(xs)
17
- xs.respond_to?(:each)
18
- end
19
-
20
8
  def self.itself(x)
21
9
  x
22
10
  end
@@ -25,24 +13,6 @@ module Speculation
25
13
  ->(*) { x }
26
14
  end
27
15
 
28
- def self.complement(&f)
29
- ->(*args) { !f.call(*args) }
30
- end
31
-
32
- def self.distinct?(xs)
33
- seen = Set[]
34
-
35
- xs.each do |x|
36
- if seen.include?(x)
37
- return false
38
- else
39
- seen << x
40
- end
41
- end
42
-
43
- true
44
- end
45
-
46
16
  def self.ident?(x)
47
17
  x.is_a?(Symbol) || x.is_a?(MethodIdentifier)
48
18
  end
@@ -59,22 +29,6 @@ module Speculation
59
29
  from.reduce(to) { |memo, obj| conj(memo, obj) }
60
30
  end
61
31
 
62
- def self.count_eq?(coll, count)
63
- coll.count == count
64
- end
65
-
66
- def self.count_between?(coll, min_count, max_count)
67
- coll.count.between?(min_count, max_count)
68
- end
69
-
70
- def self.key?(hash, key)
71
- hash.key?(key)
72
- end
73
-
74
- def self.empty?(coll)
75
- coll.empty?
76
- end
77
-
78
32
  def self.conj(a, b)
79
33
  case a
80
34
  when Array, Set
@@ -87,5 +41,9 @@ module Speculation
87
41
  else raise ArgumentError, "#{a}: must be an Array, Set or Hash"
88
42
  end
89
43
  end
44
+
45
+ def self.sort_descending(coll)
46
+ coll.sort { |a, b| yield(b) <=> yield(a) }
47
+ end
90
48
  end
91
49
  end