blood_contracts 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a3374d5e7af34959e56cb4f31d22cd807fe84f3ee1f74e9446646b7d4b49f27
4
- data.tar.gz: 48907e94554abde9079f25e8a657fc24dbda9fac02468f239c547031eff0df0e
3
+ metadata.gz: 28ac2afbaaff105357ceefb2e96e4812f5689ceca47fecc4eeffd5c51c45d3a9
4
+ data.tar.gz: 3d14390aa3ea943b3ad53495b3855c673d2c87da925b3230071d22c555de506c
5
5
  SHA512:
6
- metadata.gz: 9fc96fb5b769b22fb747573dd4ae5bf8da1cf2a40e429ddde58f82d78263aba3e9957d31183c0c5b0af3206abe0172e511d10e312d9624c0533cef030c502d84
7
- data.tar.gz: c22e684f33f0d4f4ab37a5e08174c4dcefae73afa09079250eb1b158c2e03d572be92a40a202a2724f8bf0026e91fd8b89308cb1843de2f8f6ceff0d9fa4ff92
6
+ metadata.gz: 8850f7821e11d0b3ed3acf837569b7ada9b45f0844fb8491a9e212bda1e1599035635f849e28acb0f7966933fe5cb0e4d865266012ce0148a37a2d6afe3679e9
7
+ data.tar.gz: 8e7e070351a3dc36d63bf6f0817176489350c0ea361dc3deb8cc48f5e578909bb2b62379e421d82b3d27267c9892d5965f788648a64f8b0bb86db10421d37ffc
@@ -6,7 +6,6 @@ require "hashie/mash"
6
6
 
7
7
  require_relative "blood_contracts/suite"
8
8
  require_relative "blood_contracts/storage"
9
- require_relative "blood_contracts/statistics"
10
9
  require_relative "blood_contracts/runner"
11
10
  require_relative "blood_contracts/debugger"
12
11
  require_relative "blood_contracts/base_contract"
@@ -0,0 +1,34 @@
1
+ module BloodContracts
2
+ module Contracts
3
+ class Iterator
4
+ extend Dry::Initializer
5
+
6
+ param :iterations, ->(v) do
7
+ v = ENV["iterations"] if ENV["iterations"]
8
+ v.to_i.positive? ? v.to_i : 1
9
+ end
10
+ param :time_to_run, ->(v) do
11
+ v = ENV["duration"] if ENV["duration"]
12
+ v.to_f if v.to_f.positive?
13
+ end, optional: true
14
+
15
+ def next
16
+ return iterations.times { yield } unless time_to_run
17
+
18
+ @iterations = iterations_from_time_to_run { yield }
19
+ [iterations - 1, 0].max.times { yield }
20
+ end
21
+
22
+ def count
23
+ @iterations
24
+ end
25
+
26
+ protected
27
+
28
+ def iterations_from_time_to_run
29
+ time_per_action = Benchmark.measure { yield }
30
+ (time_to_run / time_per_action.real).ceil
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ module BloodContracts
2
+ module Contracts
3
+ class Statistics
4
+ extend Dry::Initializer
5
+ param :iterator
6
+ option :storage, default: -> { Hash.new(0) }
7
+
8
+ def store(rule)
9
+ storage[rule] += 1
10
+ end
11
+
12
+ def to_h
13
+ Hash[storage.map { |rule_name, times| [rule_name, rule_stats(times)] }]
14
+ end
15
+
16
+ def to_s
17
+ to_h.map do |name, occasions|
18
+ " - '#{name}' happened #{occasions.times} time(s) "\
19
+ "(#{(occasions.percent * 100).round(2)}% of the time)"
20
+ end.join("; \n")
21
+ end
22
+
23
+ def found_unexpected_behavior?
24
+ storage.key?(Storage::UNDEFINED_RULE)
25
+ end
26
+
27
+ private
28
+
29
+ def rule_stats(times)
30
+ Hashie::Mash.new(times: times, percent: (times.to_f / iterator.count))
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,6 +1,8 @@
1
1
  require_relative "contracts/validator"
2
2
  require_relative "contracts/matcher"
3
3
  require_relative "contracts/description"
4
+ require_relative "contracts/iterator"
5
+ require_relative "contracts/statistics"
4
6
 
5
7
  module BloodContracts
6
8
  class Runner
@@ -11,19 +13,16 @@ module BloodContracts
11
13
  option :suite
12
14
  option :storage, default: -> { suite.storage }
13
15
 
14
- option :iterations, ->(v) do
15
- v = ENV["iterations"] if ENV["iterations"]
16
- v.to_i.positive? ? v.to_i : 1
17
- end, default: -> { 1 }
18
- option :time_to_run, ->(v) do
19
- v = ENV["duration"] if ENV["duration"]
20
- v.to_f if v.to_f.positive?
21
- end, optional: true
16
+ option :iterations, default: -> { 1 }
17
+ option :time_to_run, optional: true
18
+ option :stop_on_unexpected, default: -> { false }
19
+ option :iterator, default: -> do
20
+ Contracts::Iterator.new(iterations, time_to_run)
21
+ end
22
22
 
23
23
  option :context, optional: true
24
- option :stop_on_unexpected, default: -> { false }
25
24
 
26
- option :statistics, default: -> { Statistics.new(iterations) }
25
+ option :statistics, default: -> { Contracts::Statistics.new(iterator) }
27
26
  option :matcher, default: -> { Contracts::Matcher.new(suite.contract) }
28
27
  option :validator, default: -> { Contracts::Validator.new(suite.contract) }
29
28
  option :contract_description, default: -> do
@@ -31,15 +30,15 @@ module BloodContracts
31
30
  end
32
31
 
33
32
  def call
34
- iterate do
35
- next if match_rules?(matches_storage: statistics) do
36
- input = suite.data_generator.call
37
- [input, checking_proc.call(input)]
33
+ return false if :halt == catch(:unexpected_behavior) do
34
+ iterator.next do
35
+ next if match_rules?(matches_storage: statistics) do
36
+ input = suite.data_generator.call
37
+ [input, checking_proc.call(input)]
38
+ end
39
+ throw :unexpected_behavior, :halt if stop_on_unexpected
38
40
  end
39
- throw :unexpected_behavior, :stop if stop_on_unexpected
40
41
  end
41
- return if stopped_by_unexpected_behavior?
42
-
43
42
  validator.valid?(statistics)
44
43
  end
45
44
 
@@ -47,21 +46,21 @@ module BloodContracts
47
46
  def failure_message
48
47
  intro = "expected that given Proc would meet the contract:"
49
48
 
50
- if validator.expected_behavior?
49
+ if stats.unexpected_behavior?
51
50
  "#{intro}\n#{contract_description}\n"\
52
- " during #{iterations} run(s) but got:\n#{statistics}\n\n"\
53
- "For further investigations open: #{storage.suggestion}"
51
+ " during #{iterator.count} run(s) but got unexpected behavior.\n\n"\
52
+ "For further investigations open: #{storage.unexpected_suggestion}"
54
53
  else
55
54
  "#{intro}\n#{contract_description}\n"\
56
- " during #{iterations} run(s) but got unexpected behavior.\n\n"\
57
- "For further investigations open: #{storage.unexpected_suggestion}"
55
+ " during #{iterator.count} run(s) but got:\n#{statistics}\n\n"\
56
+ "For further investigations open: #{storage.suggestion}"
58
57
  end
59
58
  end
60
59
 
61
60
  # FIXME: Move to locales
62
61
  def description
63
62
  "meet the contract:\n#{contract_description} \n"\
64
- " during #{iterations} run(s). Stats:\n#{statistics}\n\n"\
63
+ " during #{iterator.count} run(s). Stats:\n#{statistics}\n\n"\
65
64
  "For further investigations open: #{storage.suggestion}\n"
66
65
  end
67
66
 
@@ -78,28 +77,6 @@ module BloodContracts
78
77
  raise
79
78
  end
80
79
 
81
- def stopped_by_unexpected_behavior?
82
- @_stopped_by_unexpected_behavior == :stop
83
- end
84
-
85
- def iterate
86
- run_iterations ||= iterations
87
-
88
- if time_to_run
89
- run_iterations = iterations_count_from_time_to_run { yield }
90
- @iterations = run_iterations + 1
91
- end
92
-
93
- @_stopped_by_unexpected_behavior = catch(:unexpected_behavior) do
94
- run_iterations.times { yield }
95
- end
96
- end
97
-
98
- def iterations_count_from_time_to_run
99
- time_per_action = Benchmark.measure { yield }
100
- (time_to_run / time_per_action.real).ceil
101
- end
102
-
103
80
  def store_exception(error, input, output, context)
104
81
  storage.store(
105
82
  options: {
@@ -67,7 +67,6 @@ module BloodContracts
67
67
  { load: Oj.method(:load), dump: Oj.method(:dump) }
68
68
  end
69
69
 
70
- # Quick open: `vim -O tmp/contract_tests/<tstamp>/<tag>/<tstamp>.*`
71
70
  def store(options:, rules:, context:)
72
71
  options = Hashie::Mash.new(options)
73
72
 
@@ -103,30 +103,6 @@ module BloodContracts
103
103
  f << write(dump_proc, context, data)
104
104
  end
105
105
  end
106
- #
107
- # def serialize_input(tag, options, context)
108
- # return unless (dump_proc = input_serializer[:dump])
109
- # name = sample_name(tag)
110
- # File.open("#{name}.input.dump", "w+") do |f|
111
- # f << write(dump_proc, context, options.input)
112
- # end
113
- # end
114
- #
115
- # def serialize_output(tag, options, context)
116
- # return unless (dump_proc = output_serializer[:dump])
117
- # name = sample_name(tag)
118
- # File.open("#{name}.output.dump", "w+") do |f|
119
- # f << write(dump_proc, context, options.output)
120
- # end
121
- # end
122
- #
123
- # def serialize_meta(tag, options, context)
124
- # return unless (dump_proc = meta_serializer[:dump])
125
- # name = sample_name(tag)
126
- # File.open("#{name}.meta.dump", "w+") do |f|
127
- # f << write(dump_proc, context, options.meta)
128
- # end
129
- # end
130
106
  end
131
107
  end
132
108
  end
@@ -2,14 +2,15 @@ module BloodContracts
2
2
  class Suite
3
3
  extend Dry::Initializer
4
4
 
5
+ option :contract, ->(v) { Hashie::Mash.new(v) }
5
6
  option :data_generator, optional: true
6
- option :contract, ->(v) { Hashie::Mash.new(v) }, default: -> { Hash.new }
7
7
 
8
8
  option :input_writer, optional: true
9
9
  option :output_writer, optional: true
10
10
 
11
- option :input_serializer, ->(v) { parse_serializer(v) }, optional: true
12
- option :output_serializer, ->(v) { parse_serializer(v) }, optional: true
11
+ option :input_serializer, optional: true
12
+ option :output_serializer, optional: true
13
+ option :meta_serializer, optional: true
13
14
 
14
15
  option :storage_backend, optional: true
15
16
  option :storage, default: -> { default_storage }
@@ -19,11 +20,6 @@ module BloodContracts
19
20
  @data_generator = generator
20
21
  end
21
22
 
22
- def contract=(contract)
23
- raise ArgumentError unless contract.respond_to?(:to_h)
24
- @contract = Hashie::Mash.new(contract.to_h)
25
- end
26
-
27
23
  def input_writer=(writer)
28
24
  storage.input_writer = writer
29
25
  end
@@ -34,10 +30,11 @@ module BloodContracts
34
30
 
35
31
  def default_storage
36
32
  Storage.new(
37
- input_writer: input_writer,
33
+ input_writer: input_writer,
38
34
  output_writer: output_writer,
39
- input_serializer: input_serializer,
35
+ input_serializer: input_serializer,
40
36
  output_serializer: output_serializer,
37
+ meta_serializer: meta_serializer,
41
38
  )
42
39
  end
43
40
  end
@@ -1,3 +1,3 @@
1
1
  module BloodContracts
2
- VERSION = "0.2.0".freeze
2
+ VERSION = "0.2.1".freeze
3
3
  end
@@ -23,9 +23,11 @@ module RSpec
23
23
  suite = nil
24
24
  if args.respond_to?(:to_contract_suite)
25
25
  suite = args.to_contract_suite(name: _example_name_to_path)
26
- elsif args.respond_to?(:to_hash) && args.fetch(:contract) { false }
27
- suite = ::BloodContracts::Suite.new(storage: new_storage)
28
- suite.contract = args[:contract]
26
+ elsif args.respond_to?(:to_h) && args.to_h.fetch(:contract) { false }
27
+ ::BloodContracts::Suite.new(
28
+ storage: new_storage,
29
+ contract: args[:contract]
30
+ )
29
31
  else
30
32
  raise "Matcher arguments is not a Blood Contract"
31
33
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blood_contracts
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergey Dolganov
@@ -128,11 +128,12 @@ files:
128
128
  - lib/blood_contracts.rb
129
129
  - lib/blood_contracts/base_contract.rb
130
130
  - lib/blood_contracts/contracts/description.rb
131
+ - lib/blood_contracts/contracts/iterator.rb
131
132
  - lib/blood_contracts/contracts/matcher.rb
133
+ - lib/blood_contracts/contracts/statistics.rb
132
134
  - lib/blood_contracts/contracts/validator.rb
133
135
  - lib/blood_contracts/debugger.rb
134
136
  - lib/blood_contracts/runner.rb
135
- - lib/blood_contracts/statistics.rb
136
137
  - lib/blood_contracts/storage.rb
137
138
  - lib/blood_contracts/storages/base_backend.rb
138
139
  - lib/blood_contracts/storages/file_backend.rb
@@ -1,32 +0,0 @@
1
- module BloodContracts
2
- class Statistics
3
- extend Dry::Initializer
4
- param :iterations
5
- option :storage, default: -> { Hash.new(0) }
6
-
7
- def store(rule)
8
- storage[rule] += 1
9
- end
10
-
11
- def to_h
12
- Hash[storage.map { |rule_name, times| [rule_name, rule_stats(times)] }]
13
- end
14
-
15
- def to_s
16
- to_h.map do |name, occasions|
17
- " - '#{name}' happened #{occasions.times} time(s) "\
18
- "(#{(occasions.percent * 100).round(2)}% of the time)"
19
- end.join("; \n")
20
- end
21
-
22
- def found_unexpected_behavior?
23
- storage.key?(Storage::UNDEFINED_RULE)
24
- end
25
-
26
- private
27
-
28
- def rule_stats(times)
29
- Hashie::Mash.new(times: times, percent: (times.to_f / iterations))
30
- end
31
- end
32
- end