blood_contracts 0.2.0 → 0.2.1

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 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