mutant 0.9.11 → 0.9.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (228) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mutant.rb +1 -0
  3. data/lib/mutant/isolation.rb +1 -1
  4. data/lib/mutant/isolation/fork.rb +2 -2
  5. data/lib/mutant/isolation/none.rb +1 -1
  6. data/lib/mutant/minitest/coverage.rb +53 -0
  7. data/lib/mutant/mutator/node/block_pass.rb +29 -0
  8. data/lib/mutant/mutator/node/noop.rb +1 -1
  9. data/lib/mutant/mutator/node/send.rb +24 -0
  10. data/lib/mutant/version.rb +1 -1
  11. metadata +5 -326
  12. data/.github/workflows/ci.yml +0 -121
  13. data/.gitignore +0 -38
  14. data/.rspec +0 -5
  15. data/.rubocop.yml +0 -210
  16. data/Changelog.md +0 -91
  17. data/Gemfile +0 -7
  18. data/Gemfile.lock +0 -107
  19. data/Gemfile.shared +0 -10
  20. data/README.md +0 -199
  21. data/Rakefile +0 -5
  22. data/config/devtools.yml +0 -2
  23. data/config/reek.yml +0 -139
  24. data/config/yardstick.yml +0 -2
  25. data/docs/commercial-support.md +0 -14
  26. data/docs/concurrency.md +0 -39
  27. data/docs/incremental.md +0 -76
  28. data/docs/known-problems.md +0 -30
  29. data/docs/limitations.md +0 -50
  30. data/docs/mutant-minitest.md +0 -149
  31. data/docs/mutant-rspec.md +0 -130
  32. data/docs/nomenclature.md +0 -82
  33. data/docs/reading-reports.md +0 -74
  34. data/meta/and.rb +0 -13
  35. data/meta/and_asgn.rb +0 -14
  36. data/meta/array.rb +0 -27
  37. data/meta/begin.rb +0 -20
  38. data/meta/block.rb +0 -199
  39. data/meta/block_pass.rb +0 -8
  40. data/meta/blockarg.rb +0 -10
  41. data/meta/break.rb +0 -9
  42. data/meta/case.rb +0 -217
  43. data/meta/casgn.rb +0 -25
  44. data/meta/cbase.rb +0 -8
  45. data/meta/class.rb +0 -12
  46. data/meta/const.rb +0 -17
  47. data/meta/csend.rb +0 -10
  48. data/meta/cvar.rb +0 -7
  49. data/meta/cvasgn.rb +0 -9
  50. data/meta/date.rb +0 -59
  51. data/meta/def.rb +0 -196
  52. data/meta/defined.rb +0 -9
  53. data/meta/dstr.rb +0 -13
  54. data/meta/dsym.rb +0 -14
  55. data/meta/ensure.rb +0 -8
  56. data/meta/false.rb +0 -7
  57. data/meta/file.rb +0 -5
  58. data/meta/float.rb +0 -37
  59. data/meta/gvar.rb +0 -7
  60. data/meta/gvasgn.rb +0 -9
  61. data/meta/hash.rb +0 -20
  62. data/meta/if.rb +0 -72
  63. data/meta/index.rb +0 -133
  64. data/meta/indexasgn.rb +0 -31
  65. data/meta/int.rb +0 -18
  66. data/meta/ivar.rb +0 -8
  67. data/meta/ivasgn.rb +0 -22
  68. data/meta/kwarg.rb +0 -10
  69. data/meta/kwbegin.rb +0 -8
  70. data/meta/kwoptarg.rb +0 -13
  71. data/meta/lambda.rb +0 -23
  72. data/meta/line.rb +0 -5
  73. data/meta/lvar.rb +0 -16
  74. data/meta/lvasgn.rb +0 -24
  75. data/meta/masgn.rb +0 -7
  76. data/meta/match_current_line.rb +0 -14
  77. data/meta/next.rb +0 -10
  78. data/meta/nil.rb +0 -5
  79. data/meta/nthref.rb +0 -14
  80. data/meta/op_assgn.rb +0 -17
  81. data/meta/or.rb +0 -13
  82. data/meta/or_asgn.rb +0 -50
  83. data/meta/range.rb +0 -63
  84. data/meta/redo.rb +0 -5
  85. data/meta/regexp.rb +0 -80
  86. data/meta/regopt.rb +0 -10
  87. data/meta/rescue.rb +0 -84
  88. data/meta/return.rb +0 -16
  89. data/meta/sclass.rb +0 -12
  90. data/meta/self.rb +0 -7
  91. data/meta/send.rb +0 -600
  92. data/meta/str.rb +0 -7
  93. data/meta/super.rb +0 -27
  94. data/meta/sym.rb +0 -8
  95. data/meta/true.rb +0 -7
  96. data/meta/until.rb +0 -16
  97. data/meta/while.rb +0 -24
  98. data/meta/yield.rb +0 -9
  99. data/mutant-minitest.gemspec +0 -22
  100. data/mutant-rspec.gemspec +0 -22
  101. data/mutant.gemspec +0 -44
  102. data/mutant.sh +0 -12
  103. data/mutant.yml +0 -6
  104. data/spec/integration/mutant/corpus_spec.rb +0 -15
  105. data/spec/integration/mutant/isolation/fork_spec.rb +0 -28
  106. data/spec/integration/mutant/minitest_spec.rb +0 -11
  107. data/spec/integration/mutant/null_spec.rb +0 -16
  108. data/spec/integration/mutant/rspec_spec.rb +0 -15
  109. data/spec/integration/mutant/test_mutator_handles_types_spec.rb +0 -9
  110. data/spec/integrations.yml +0 -63
  111. data/spec/shared/framework_integration_behavior.rb +0 -70
  112. data/spec/shared/method_matcher_behavior.rb +0 -47
  113. data/spec/spec_helper.rb +0 -90
  114. data/spec/support/corpus.rb +0 -318
  115. data/spec/support/file_system.rb +0 -62
  116. data/spec/support/ice_nine_config.rb +0 -10
  117. data/spec/support/ruby_vm.rb +0 -84
  118. data/spec/support/shared_context.rb +0 -169
  119. data/spec/support/xspec.rb +0 -183
  120. data/spec/unit/mutant/ast/find_metaclass_containing_spec.rb +0 -64
  121. data/spec/unit/mutant/ast/meta/optarg_spec.rb +0 -24
  122. data/spec/unit/mutant/ast/meta/send/proc_predicate_spec.rb +0 -30
  123. data/spec/unit/mutant/ast/meta/send/receiver_possible_top_level_const_predicate_spec.rb +0 -39
  124. data/spec/unit/mutant/ast/meta/send_spec.rb +0 -42
  125. data/spec/unit/mutant/ast/named_children_spec.rb +0 -89
  126. data/spec/unit/mutant/ast/sexp_spec.rb +0 -38
  127. data/spec/unit/mutant/ast_spec.rb +0 -57
  128. data/spec/unit/mutant/bootstrap_spec.rb +0 -216
  129. data/spec/unit/mutant/cli_spec.rb +0 -305
  130. data/spec/unit/mutant/clock_monotonic_spec.rb +0 -52
  131. data/spec/unit/mutant/config_spec.rb +0 -126
  132. data/spec/unit/mutant/context_spec.rb +0 -111
  133. data/spec/unit/mutant/env_spec.rb +0 -229
  134. data/spec/unit/mutant/expression/method_spec.rb +0 -62
  135. data/spec/unit/mutant/expression/methods_spec.rb +0 -66
  136. data/spec/unit/mutant/expression/namespace/exact_spec.rb +0 -28
  137. data/spec/unit/mutant/expression/namespace/recursive_spec.rb +0 -66
  138. data/spec/unit/mutant/expression/parser_spec.rb +0 -65
  139. data/spec/unit/mutant/expression_spec.rb +0 -45
  140. data/spec/unit/mutant/integration/rspec_spec.rb +0 -201
  141. data/spec/unit/mutant/integration_spec.rb +0 -150
  142. data/spec/unit/mutant/isolation/fork_spec.rb +0 -309
  143. data/spec/unit/mutant/isolation/none_spec.rb +0 -23
  144. data/spec/unit/mutant/isolation/result_spec.rb +0 -73
  145. data/spec/unit/mutant/license_spec.rb +0 -305
  146. data/spec/unit/mutant/loader_spec.rb +0 -79
  147. data/spec/unit/mutant/matcher/chain_spec.rb +0 -26
  148. data/spec/unit/mutant/matcher/compiler_spec.rb +0 -0
  149. data/spec/unit/mutant/matcher/config_spec.rb +0 -47
  150. data/spec/unit/mutant/matcher/filter_spec.rb +0 -22
  151. data/spec/unit/mutant/matcher/method/instance_spec.rb +0 -164
  152. data/spec/unit/mutant/matcher/method/metaclass_spec.rb +0 -108
  153. data/spec/unit/mutant/matcher/method/singleton_spec.rb +0 -90
  154. data/spec/unit/mutant/matcher/methods/instance_spec.rb +0 -54
  155. data/spec/unit/mutant/matcher/methods/metaclass_spec.rb +0 -62
  156. data/spec/unit/mutant/matcher/methods/singleton_spec.rb +0 -51
  157. data/spec/unit/mutant/matcher/namespace_spec.rb +0 -39
  158. data/spec/unit/mutant/matcher/null_spec.rb +0 -12
  159. data/spec/unit/mutant/matcher/scope_spec.rb +0 -45
  160. data/spec/unit/mutant/matcher/static_spec.rb +0 -13
  161. data/spec/unit/mutant/matcher_spec.rb +0 -102
  162. data/spec/unit/mutant/meta/example/dsl_spec.rb +0 -108
  163. data/spec/unit/mutant/meta/example/verification_spec.rb +0 -154
  164. data/spec/unit/mutant/meta/example_spec.rb +0 -34
  165. data/spec/unit/mutant/mutation_spec.rb +0 -140
  166. data/spec/unit/mutant/mutator/node_spec.rb +0 -47
  167. data/spec/unit/mutant/mutator_spec.rb +0 -21
  168. data/spec/unit/mutant/parallel/driver_spec.rb +0 -126
  169. data/spec/unit/mutant/parallel/source/array_spec.rb +0 -57
  170. data/spec/unit/mutant/parallel/worker_spec.rb +0 -206
  171. data/spec/unit/mutant/parallel_spec.rb +0 -115
  172. data/spec/unit/mutant/parser_spec.rb +0 -26
  173. data/spec/unit/mutant/range_spec.rb +0 -141
  174. data/spec/unit/mutant/registry_spec.rb +0 -74
  175. data/spec/unit/mutant/reporter/cli/printer/config_spec.rb +0 -17
  176. data/spec/unit/mutant/reporter/cli/printer/env_progress_spec.rb +0 -85
  177. data/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb +0 -45
  178. data/spec/unit/mutant/reporter/cli/printer/isolation_result_spec.rb +0 -132
  179. data/spec/unit/mutant/reporter/cli/printer/mutation_progress_result_spec.rb +0 -25
  180. data/spec/unit/mutant/reporter/cli/printer/mutation_result_spec.rb +0 -153
  181. data/spec/unit/mutant/reporter/cli/printer/status_progressive_spec.rb +0 -45
  182. data/spec/unit/mutant/reporter/cli/printer/subject_progress_spec.rb +0 -36
  183. data/spec/unit/mutant/reporter/cli/printer/subject_result_spec.rb +0 -44
  184. data/spec/unit/mutant/reporter/cli/printer/test_result_spec.rb +0 -16
  185. data/spec/unit/mutant/reporter/cli/printer_spec.rb +0 -163
  186. data/spec/unit/mutant/reporter/cli_spec.rb +0 -137
  187. data/spec/unit/mutant/reporter/null_spec.rb +0 -14
  188. data/spec/unit/mutant/reporter/sequence_spec.rb +0 -31
  189. data/spec/unit/mutant/repository/diff/ranges_spec.rb +0 -180
  190. data/spec/unit/mutant/repository/diff_spec.rb +0 -122
  191. data/spec/unit/mutant/repository/subject_filter_spec.rb +0 -30
  192. data/spec/unit/mutant/require_highjack_spec.rb +0 -73
  193. data/spec/unit/mutant/result/class_methods_spec.rb +0 -51
  194. data/spec/unit/mutant/result/env_spec.rb +0 -161
  195. data/spec/unit/mutant/result/mutation_spec.rb +0 -70
  196. data/spec/unit/mutant/result/subject_spec.rb +0 -111
  197. data/spec/unit/mutant/result/test_spec.rb +0 -14
  198. data/spec/unit/mutant/result_spec.rb +0 -33
  199. data/spec/unit/mutant/runner/sink_spec.rb +0 -174
  200. data/spec/unit/mutant/runner_spec.rb +0 -121
  201. data/spec/unit/mutant/selector/expression_spec.rb +0 -62
  202. data/spec/unit/mutant/selector/null_spec.rb +0 -17
  203. data/spec/unit/mutant/subject/method/instance_spec.rb +0 -276
  204. data/spec/unit/mutant/subject/method/metaclass_spec.rb +0 -63
  205. data/spec/unit/mutant/subject/method/singleton_spec.rb +0 -61
  206. data/spec/unit/mutant/subject_spec.rb +0 -93
  207. data/spec/unit/mutant/transform/array_spec.rb +0 -92
  208. data/spec/unit/mutant/transform/bool_spec.rb +0 -63
  209. data/spec/unit/mutant/transform/error_spec.rb +0 -132
  210. data/spec/unit/mutant/transform/exception_spec.rb +0 -44
  211. data/spec/unit/mutant/transform/hash_spec.rb +0 -236
  212. data/spec/unit/mutant/transform/index_spec.rb +0 -92
  213. data/spec/unit/mutant/transform/named_spec.rb +0 -49
  214. data/spec/unit/mutant/transform/primitive_spec.rb +0 -56
  215. data/spec/unit/mutant/transform/sequence_spec.rb +0 -98
  216. data/spec/unit/mutant/util/one_spec.rb +0 -22
  217. data/spec/unit/mutant/warnings_spec.rb +0 -89
  218. data/spec/unit/mutant/world_spec.rb +0 -63
  219. data/spec/unit/mutant/zombifier_spec.rb +0 -122
  220. data/test_app/.rspec +0 -1
  221. data/test_app/Gemfile.minitest +0 -6
  222. data/test_app/Gemfile.rspec3.8 +0 -7
  223. data/test_app/lib/test_app.rb +0 -114
  224. data/test_app/lib/test_app/literal.rb +0 -35
  225. data/test_app/lib/test_app/metaclasses.rb +0 -108
  226. data/test_app/spec/spec_helper.rb +0 -9
  227. data/test_app/spec/unit/test_app/literal_spec.rb +0 -20
  228. data/test_app/test/unit/test_app/literal_test.rb +0 -16
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MutantSpec
4
- class FileState
5
- DEFAULTS = IceNine.deep_freeze(
6
- file: false,
7
- contents: nil,
8
- requires: []
9
- )
10
-
11
- include Adamantium, Anima.new(*DEFAULTS.keys)
12
-
13
- def self.new(attributes = DEFAULTS)
14
- super(DEFAULTS.merge(attributes))
15
- end
16
-
17
- DOES_NOT_EXIST = new
18
-
19
- alias_method :file?, :file
20
- end # FileState
21
-
22
- class FakePathname
23
- include Adamantium, Concord.new(:file_system, :pathname)
24
-
25
- def join(*arguments)
26
- self.class.new(
27
- file_system,
28
- pathname.join(*arguments)
29
- )
30
- end
31
-
32
- def read
33
- state.contents
34
- end
35
-
36
- def to_s
37
- pathname.to_s
38
- end
39
-
40
- def file?
41
- state.file?
42
- end
43
-
44
- private
45
-
46
- def state
47
- file_system.state(pathname.to_s)
48
- end
49
- end # FakePathname
50
-
51
- class FileSystem
52
- include Adamantium, Concord.new(:file_states)
53
-
54
- def state(filename)
55
- file_states.fetch(filename, FileState::DOES_NOT_EXIST)
56
- end
57
-
58
- def path(filename)
59
- FakePathname.new(self, Pathname.new(filename))
60
- end
61
- end # FileSystem
62
- end # MutantSpec
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'ice_nine'
4
-
5
- module IceNine
6
- class Freezer
7
- class RSpec < NoFreeze
8
- end # RSpec
9
- end # Freezer
10
- end # IceNine
@@ -1,84 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MutantSpec
4
- # Not a real VM, just kidding. It connects the require / eval triggers
5
- # require semantics Zombifier relies on in a way we can avoid having to
6
- # mock around everywhere to test every detail.
7
- class RubyVM
8
- include Concord::Public.new(:expected_events)
9
-
10
- # An event being observed by the VM handlers
11
- class EventObservation
12
- include Concord::Public.new(:type, :payload)
13
- end # EventObservation
14
-
15
- # An event being expected, can advance the VM
16
- class EventExpectation
17
- include AbstractType, Anima.new(
18
- :expected_payload,
19
- :trigger_requires,
20
- :return_value
21
- )
22
-
23
- DEFAULTS = IceNine.deep_freeze(trigger_requires: [])
24
-
25
- def initialize(attributes)
26
- super(DEFAULTS.merge(attributes))
27
- end
28
-
29
- def handle(vm, observation) # rubocop:disable Naming/MethodParameterName
30
- unless match?(observation)
31
- fail "Unexpected event observation: #{observation.inspect}, expected #{inspect}"
32
- end
33
-
34
- trigger_requires.each(&vm.method(:require))
35
-
36
- self
37
- end
38
-
39
- private
40
-
41
- def match?(observation)
42
- observation.type.eql?(self.class) && observation.payload.eql?(expected_payload)
43
- end
44
-
45
- # Expectation and advance on require calls
46
- class Require < self
47
- end # Require
48
-
49
- # Expectation and advance on eval calls
50
- class Eval < self
51
- end # Eval
52
- end # EventExpectation
53
-
54
- # A fake implementation of Kernel#require
55
- def require(logical_name)
56
- handle_event(EventObservation.new(EventExpectation::Require, logical_name: logical_name))
57
- end
58
-
59
- # A fake implementation of Kernel#eval
60
- def eval(source, binding, location)
61
- handle_event(
62
- EventObservation.new(
63
- EventExpectation::Eval,
64
- binding: binding,
65
- source: source,
66
- source_location: location
67
- )
68
- )
69
- end
70
-
71
- # Test if VM events where fully processed
72
- def done?
73
- expected_events.empty?
74
- end
75
-
76
- private
77
-
78
- def handle_event(observation)
79
- fail "Unexpected event: #{observation.type} / #{observation.payload}" if expected_events.empty?
80
-
81
- expected_events.slice!(0).handle(self, observation).return_value
82
- end
83
- end # RubyVM
84
- end # MutantSpec
@@ -1,169 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # rubocop:disable Metrics/ModuleLength
4
- module SharedContext
5
- # Prepend an anonymous module with the new `with` method
6
- #
7
- # Using an anonymous module eliminates warnings where `setup_shared_context`
8
- # is used and one of the shared methods is immediately redefined.
9
- def with(name, &block)
10
- new_definition =
11
- Module.new do
12
- define_method(name) do
13
- super().with(instance_eval(&block))
14
- end
15
- end
16
-
17
- prepend(new_definition)
18
- end
19
-
20
- def it_reports(expected_content)
21
- it 'writes expected report to output' do
22
- described_class.call(output, reportable)
23
- output.rewind
24
- expect(output.read).to eql(expected_content)
25
- end
26
- end
27
-
28
- # rubocop:disable Metrics/MethodLength
29
- # rubocop:disable Metrics/AbcSize
30
- def setup_shared_context
31
- let(:mutation_a) { Mutant::Mutation::Evil.new(subject_a, mutation_a_node) }
32
- let(:mutation_a_node) { s(:false) }
33
- let(:mutation_b) { Mutant::Mutation::Evil.new(subject_a, mutation_b_node) }
34
- let(:mutation_b_node) { s(:nil) }
35
- let(:mutations) { [mutation_a, mutation_b] }
36
- let(:output) { StringIO.new }
37
- let(:subject_a_node) { s(:true) }
38
- let(:test_a) { instance_double(Mutant::Test, identification: 'test-a') }
39
- let(:subjects) { [subject_a] }
40
-
41
- let(:job_a) do
42
- Mutant::Parallel::Source::Job.new(
43
- index: 0,
44
- payload: mutation_a
45
- )
46
- end
47
-
48
- let(:job_b) do
49
- Mutant::Parallel::Source::Job.new(
50
- index: 1,
51
- payload: mutation_b
52
- )
53
- end
54
-
55
- let(:env) do
56
- instance_double(
57
- Mutant::Env,
58
- amount_mutations: mutations.length,
59
- amount_selected_tests: selections.values.flatten.to_set.length,
60
- amount_subjects: subjects.length,
61
- amount_total_tests: integration.all_tests.length,
62
- config: config,
63
- integration: integration,
64
- mutations: mutations,
65
- selected_tests: [test_a].to_set,
66
- selections: selections,
67
- subjects: subjects,
68
- test_subject_ratio: Rational(1)
69
- )
70
- end
71
-
72
- let(:selections) do
73
- { subject_a => [test_a] }
74
- end
75
-
76
- let(:integration) do
77
- instance_double(
78
- Mutant::Integration,
79
- all_tests: [test_a]
80
- )
81
- end
82
-
83
- let(:status) do
84
- Mutant::Parallel::Status.new(
85
- active_jobs: [].to_set,
86
- payload: env_result,
87
- done: true
88
- )
89
- end
90
-
91
- let(:config) do
92
- Mutant::Config::DEFAULT.with(
93
- jobs: 1,
94
- reporter: Mutant::Reporter::Null.new
95
- )
96
- end
97
-
98
- let(:subject_a) do
99
- instance_double(
100
- Mutant::Subject,
101
- node: subject_a_node,
102
- source: Unparser.unparse(subject_a_node),
103
- identification: 'subject-a'
104
- )
105
- end
106
-
107
- before do
108
- allow(subject_a).to receive(:mutations).and_return([mutation_a, mutation_b])
109
- end
110
-
111
- let(:env_result) do
112
- Mutant::Result::Env.new(
113
- env: env,
114
- runtime: 4.0,
115
- subject_results: [subject_a_result]
116
- )
117
- end
118
-
119
- let(:mutation_a_result) do
120
- Mutant::Result::Mutation.new(
121
- mutation: mutation_a,
122
- isolation_result: mutation_a_isolation_result,
123
- runtime: 1.0
124
- )
125
- end
126
-
127
- let(:mutation_b_result) do
128
- Mutant::Result::Mutation.new(
129
- isolation_result: mutation_b_isolation_result,
130
- mutation: mutation_b,
131
- runtime: 1.0
132
- )
133
- end
134
-
135
- let(:mutation_a_isolation_result) do
136
- Mutant::Isolation::Result::Success.new(mutation_a_test_result)
137
- end
138
-
139
- let(:mutation_a_test_result) do
140
- Mutant::Result::Test.new(
141
- tests: [test_a],
142
- passed: false,
143
- runtime: 1.0,
144
- output: 'mutation a test result output'
145
- )
146
- end
147
-
148
- let(:mutation_b_test_result) do
149
- Mutant::Result::Test.new(
150
- tests: [test_a],
151
- passed: false,
152
- runtime: 1.0,
153
- output: 'mutation b test result output'
154
- )
155
- end
156
-
157
- let(:mutation_b_isolation_result) do
158
- Mutant::Isolation::Result::Success.new(mutation_b_test_result)
159
- end
160
-
161
- let(:subject_a_result) do
162
- Mutant::Result::Subject.new(
163
- subject: subject_a,
164
- tests: [test_a],
165
- mutation_results: [mutation_a_result, mutation_b_result]
166
- )
167
- end
168
- end
169
- end # SharedContext
@@ -1,183 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module XSpec
4
- class MessageReaction
5
- include Concord.new(:event_list)
6
-
7
- TERMINATE_EVENTS = IceNine.deep_freeze(%i[return exception].to_set)
8
- VALID_EVENTS = IceNine.deep_freeze(%i[return execute exception yields].to_set)
9
-
10
- private_constant(*constants(false))
11
-
12
- def call(observation)
13
- event_list.map do |event, object|
14
- __send__(event, observation, object)
15
- end.last
16
- end
17
-
18
- # Parse events into reaction
19
- #
20
- # @param [Array{Symbol,Object}, Hash{Symbol,Object}]
21
- #
22
- # @return [MessageReaction]
23
- def self.parse(events)
24
- event_list = events.to_a
25
- assert_valid(event_list)
26
- new(event_list)
27
- end
28
-
29
- private
30
-
31
- def return(_event, value)
32
- value
33
- end
34
-
35
- def execute(_event, block)
36
- block.call
37
- end
38
-
39
- def exception(_event, exception)
40
- fail exception
41
- end
42
-
43
- def yields(observation, yields)
44
- block = observation.block or fail 'No block passed where expected'
45
-
46
- validate_block_arity(observation, yields)
47
-
48
- block.call(*yields)
49
- end
50
-
51
- def validate_block_arity(observation, yields)
52
- expected, observed = yields.length, observation.block.arity
53
-
54
- # block allows anything we can skip the check
55
- return if observed.equal?(-1)
56
- fail 'Optargs currently not supported' if observed < -1
57
- block_arity_mismatch(observation, expected, observed) unless expected.equal?(observed)
58
- end
59
-
60
- def block_arity_mismatch(observation, expected, observed)
61
- fail "block arity mismatch, expected #{expected} observed #{observed}\nobservation:\n#{observation.inspect}"
62
- end
63
-
64
- alias_method :yields_return, :yields
65
-
66
- def self.assert_valid(event_list)
67
- assert_not_empty(event_list)
68
- assert_valid_events(event_list)
69
- assert_total(event_list)
70
- end
71
- private_class_method :assert_valid
72
-
73
- def self.assert_valid_events(event_list)
74
- event_list.map(&:first).each do |event|
75
- fail "Invalid event: #{event}" unless VALID_EVENTS.include?(event)
76
- end
77
- end
78
- private_class_method :assert_valid_events
79
-
80
- def self.assert_not_empty(event_list)
81
- fail 'no events' if event_list.empty?
82
- end
83
- private_class_method :assert_not_empty
84
-
85
- def self.assert_total(event_list)
86
- return unless event_list[0..-2].map(&:first).any?(&TERMINATE_EVENTS.method(:include?))
87
-
88
- fail "Reaction not total: #{event_list}"
89
- end
90
- private_class_method :assert_total
91
- end # MessageReaction
92
-
93
- class MessageExpectation
94
- include Anima.new(:receiver, :selector, :arguments, :reaction)
95
-
96
- # rubocop:disable Metrics/ParameterLists
97
- def self.parse(receiver:, selector:, arguments: [], reaction: nil)
98
- new(
99
- receiver: receiver,
100
- selector: selector,
101
- arguments: arguments,
102
- reaction: MessageReaction.parse(reaction || { return: nil })
103
- )
104
- end
105
-
106
- def call(observation)
107
- Verifier.new(self, observation).call
108
- end
109
-
110
- class Verifier
111
- include Concord.new(:expectation, :observation)
112
-
113
- VERIFIED_ATTRIBUTES = IceNine.deep_freeze(%i[receiver selector arguments])
114
-
115
- def call
116
- VERIFIED_ATTRIBUTES.each(&method(:assert_expected_attribute))
117
-
118
- expectation.reaction.call(observation)
119
- end
120
-
121
- private
122
-
123
- def assert_expected_attribute(name)
124
- error("#{name} mismatch") unless observation.public_send(name).eql?(expectation.public_send(name))
125
- end
126
-
127
- def error(message)
128
- fail "#{message},\n observation:\n #{observation.inspect}\n expectation:\n #{expectation.inspect}"
129
- end
130
-
131
- def trigger_exception
132
- exception = expectation.exception
133
- fail exception if exception
134
- end
135
-
136
- end # Verifier
137
- end # MessageExpectation
138
-
139
- class MessageObservation
140
- include Anima.new(:receiver, :selector, :arguments, :block)
141
- end # MessageObservation
142
-
143
- class ExpectationVerifier
144
- include Concord.new(:expectations)
145
-
146
- def call(observation)
147
- expectation = expectations.shift or fail "No expected message but observed #{observation.inspect}"
148
- expectation.call(observation)
149
- end
150
-
151
- def assert_done
152
- expectations.empty? or fail "unconsumed expectations:\n#{expectations.map(&:inspect).join("\n")}"
153
- end
154
-
155
- # rubocop:disable Metrics/MethodLength
156
- def self.verify(rspec_context, expectations)
157
- verifier = new(expectations)
158
-
159
- hooks = expectations
160
- .map { |expectation| [expectation.receiver, expectation.selector] }
161
- .to_set
162
-
163
- hooks.each do |receiver, selector|
164
- rspec_context.instance_eval do
165
- allow(receiver).to receive(selector) do |*arguments, &block|
166
- verifier.call(
167
- MessageObservation.new(
168
- receiver: receiver,
169
- selector: selector,
170
- arguments: arguments,
171
- block: block
172
- )
173
- )
174
- end
175
- end
176
- end
177
-
178
- yield
179
-
180
- verifier.assert_done
181
- end
182
- end # ExpectationVerifier
183
- end # XSpec