mutant 0.8.10 → 0.8.11

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.
Files changed (247) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -4
  3. data/Changelog.md +8 -0
  4. data/README.md +112 -43
  5. data/Rakefile +2 -16
  6. data/circle.yml +1 -1
  7. data/config/flay.yml +2 -2
  8. data/config/flog.yml +1 -1
  9. data/config/reek.yml +3 -4
  10. data/config/rubocop.yml +53 -16
  11. data/lib/mutant.rb +27 -6
  12. data/lib/mutant/ast/meta/const.rb +2 -0
  13. data/lib/mutant/ast/meta/optarg.rb +2 -0
  14. data/lib/mutant/ast/meta/resbody.rb +2 -0
  15. data/lib/mutant/ast/meta/restarg.rb +2 -0
  16. data/lib/mutant/ast/meta/send.rb +4 -0
  17. data/lib/mutant/ast/meta/symbol.rb +2 -0
  18. data/lib/mutant/ast/named_children.rb +14 -4
  19. data/lib/mutant/ast/nodes.rb +3 -1
  20. data/lib/mutant/ast/regexp.rb +53 -0
  21. data/lib/mutant/ast/regexp/transformer.rb +185 -0
  22. data/lib/mutant/ast/regexp/transformer/alternative.rb +39 -0
  23. data/lib/mutant/ast/regexp/transformer/character_set.rb +46 -0
  24. data/lib/mutant/ast/regexp/transformer/direct.rb +99 -0
  25. data/lib/mutant/ast/regexp/transformer/options_group.rb +66 -0
  26. data/lib/mutant/ast/regexp/transformer/quantifier.rb +112 -0
  27. data/lib/mutant/ast/regexp/transformer/recursive.rb +50 -0
  28. data/lib/mutant/ast/regexp/transformer/root.rb +29 -0
  29. data/lib/mutant/ast/regexp/transformer/text.rb +55 -0
  30. data/lib/mutant/ast/types.rb +92 -5
  31. data/lib/mutant/cli.rb +2 -14
  32. data/lib/mutant/color.rb +1 -1
  33. data/lib/mutant/config.rb +1 -3
  34. data/lib/mutant/expression/methods.rb +1 -1
  35. data/lib/mutant/expression/namespace.rb +2 -2
  36. data/lib/mutant/expression/parser.rb +1 -1
  37. data/lib/mutant/integration.rb +10 -28
  38. data/lib/mutant/isolation.rb +9 -60
  39. data/lib/mutant/isolation/fork.rb +72 -0
  40. data/lib/mutant/isolation/none.rb +25 -0
  41. data/lib/mutant/matcher/config.rb +2 -0
  42. data/lib/mutant/matcher/method/singleton.rb +5 -4
  43. data/lib/mutant/meta.rb +11 -4
  44. data/lib/mutant/meta/example.rb +2 -116
  45. data/lib/mutant/meta/example/dsl.rb +22 -19
  46. data/lib/mutant/meta/example/verification.rb +86 -0
  47. data/lib/mutant/mutator.rb +22 -49
  48. data/lib/mutant/mutator/node.rb +15 -19
  49. data/lib/mutant/mutator/node/and_asgn.rb +1 -1
  50. data/lib/mutant/mutator/node/argument.rb +10 -5
  51. data/lib/mutant/mutator/node/arguments.rb +5 -9
  52. data/lib/mutant/mutator/node/begin.rb +4 -17
  53. data/lib/mutant/mutator/node/block.rb +1 -1
  54. data/lib/mutant/mutator/node/break.rb +1 -1
  55. data/lib/mutant/mutator/node/class.rb +21 -0
  56. data/lib/mutant/mutator/node/conditional_loop.rb +1 -1
  57. data/lib/mutant/mutator/node/define.rb +1 -1
  58. data/lib/mutant/mutator/node/defined.rb +1 -1
  59. data/lib/mutant/mutator/node/dstr.rb +1 -1
  60. data/lib/mutant/mutator/node/dsym.rb +1 -1
  61. data/lib/mutant/mutator/node/generic.rb +3 -3
  62. data/lib/mutant/mutator/node/kwbegin.rb +1 -1
  63. data/lib/mutant/mutator/node/literal.rb +9 -0
  64. data/lib/mutant/mutator/node/literal/boolean.rb +1 -1
  65. data/lib/mutant/mutator/node/literal/fixnum.rb +2 -2
  66. data/lib/mutant/mutator/node/literal/float.rb +4 -6
  67. data/lib/mutant/mutator/node/literal/hash.rb +1 -1
  68. data/lib/mutant/mutator/node/literal/nil.rb +1 -1
  69. data/lib/mutant/mutator/node/literal/range.rb +2 -19
  70. data/lib/mutant/mutator/node/literal/regex.rb +43 -3
  71. data/lib/mutant/mutator/node/literal/string.rb +1 -1
  72. data/lib/mutant/mutator/node/literal/symbol.rb +2 -4
  73. data/lib/mutant/mutator/node/masgn.rb +1 -1
  74. data/lib/mutant/mutator/node/match_current_line.rb +1 -1
  75. data/lib/mutant/mutator/node/mlhs.rb +2 -3
  76. data/lib/mutant/mutator/node/named_value/access.rb +2 -2
  77. data/lib/mutant/mutator/node/named_value/constant_assignment.rb +4 -3
  78. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +4 -4
  79. data/lib/mutant/mutator/node/next.rb +1 -1
  80. data/lib/mutant/mutator/node/nthref.rb +1 -1
  81. data/lib/mutant/mutator/node/or_asgn.rb +1 -1
  82. data/lib/mutant/mutator/node/regexp.rb +44 -0
  83. data/lib/mutant/mutator/node/regopt.rb +31 -0
  84. data/lib/mutant/mutator/node/resbody.rb +1 -1
  85. data/lib/mutant/mutator/node/rescue.rb +1 -3
  86. data/lib/mutant/mutator/node/return.rb +1 -1
  87. data/lib/mutant/mutator/node/send.rb +43 -3
  88. data/lib/mutant/mutator/node/send/attribute_assignment.rb +4 -1
  89. data/lib/mutant/mutator/node/send/conditional.rb +23 -0
  90. data/lib/mutant/mutator/node/send/index.rb +1 -1
  91. data/lib/mutant/mutator/node/splat.rb +1 -1
  92. data/lib/mutant/mutator/node/when.rb +1 -1
  93. data/lib/mutant/mutator/node/yield.rb +1 -1
  94. data/lib/mutant/mutator/util.rb +0 -30
  95. data/lib/mutant/mutator/util/array.rb +4 -16
  96. data/lib/mutant/parallel.rb +1 -1
  97. data/lib/mutant/parallel/worker.rb +1 -1
  98. data/lib/mutant/registry.rb +44 -0
  99. data/lib/mutant/reporter/cli/format.rb +2 -0
  100. data/lib/mutant/reporter/cli/printer.rb +2 -2
  101. data/lib/mutant/reporter/cli/printer/config.rb +0 -1
  102. data/lib/mutant/reporter/cli/printer/env_progress.rb +1 -11
  103. data/lib/mutant/reporter/cli/printer/mutation_progress_result.rb +1 -1
  104. data/lib/mutant/reporter/cli/printer/mutation_result.rb +2 -0
  105. data/lib/mutant/reporter/cli/tput.rb +1 -1
  106. data/lib/mutant/reporter/sequence.rb +3 -0
  107. data/lib/mutant/require_highjack.rb +6 -2
  108. data/lib/mutant/result.rb +1 -1
  109. data/lib/mutant/subject.rb +5 -5
  110. data/lib/mutant/subject/method/instance.rb +1 -2
  111. data/lib/mutant/util.rb +18 -0
  112. data/lib/mutant/version.rb +1 -1
  113. data/lib/mutant/zombifier.rb +5 -3
  114. data/meta/and.rb +1 -1
  115. data/meta/and_asgn.rb +1 -1
  116. data/meta/array.rb +2 -2
  117. data/meta/begin.rb +2 -2
  118. data/meta/block.rb +7 -7
  119. data/meta/block_pass.rb +1 -1
  120. data/meta/blockarg.rb +1 -1
  121. data/meta/break.rb +1 -1
  122. data/meta/case.rb +2 -2
  123. data/meta/casgn.rb +11 -2
  124. data/meta/cbase.rb +1 -1
  125. data/meta/class.rb +10 -0
  126. data/meta/const.rb +9 -1
  127. data/meta/csend.rb +8 -0
  128. data/meta/cvar.rb +1 -1
  129. data/meta/cvasgn.rb +1 -1
  130. data/meta/date.rb +4 -4
  131. data/meta/def.rb +14 -14
  132. data/meta/defined.rb +1 -1
  133. data/meta/dstr.rb +1 -1
  134. data/meta/dsym.rb +1 -1
  135. data/meta/ensure.rb +1 -1
  136. data/meta/false.rb +1 -1
  137. data/meta/float.rb +3 -3
  138. data/meta/gvar.rb +1 -1
  139. data/meta/gvasgn.rb +1 -1
  140. data/meta/hash.rb +1 -1
  141. data/meta/if.rb +17 -3
  142. data/meta/int.rb +1 -1
  143. data/meta/ivar.rb +1 -1
  144. data/meta/ivasgn.rb +14 -1
  145. data/meta/kwarg.rb +8 -0
  146. data/meta/kwbegin.rb +1 -1
  147. data/meta/kwoptarg.rb +11 -0
  148. data/meta/lvar.rb +1 -1
  149. data/meta/lvasgn.rb +1 -1
  150. data/meta/masgn.rb +1 -1
  151. data/meta/match_current_line.rb +2 -2
  152. data/meta/next.rb +1 -1
  153. data/meta/nil.rb +1 -1
  154. data/meta/nthref.rb +5 -5
  155. data/meta/op_assgn.rb +1 -1
  156. data/meta/or.rb +1 -1
  157. data/meta/or_asgn.rb +5 -5
  158. data/meta/range.rb +2 -8
  159. data/meta/redo.rb +1 -1
  160. data/meta/regexp.rb +106 -0
  161. data/meta/regexp/regexp_bol_anchor.rb +13 -0
  162. data/meta/regexp/regexp_bos_anchor.rb +26 -0
  163. data/meta/regexp/regexp_root_expression.rb +13 -0
  164. data/meta/regopt.rb +8 -0
  165. data/meta/rescue.rb +49 -4
  166. data/meta/restarg.rb +6 -9
  167. data/meta/return.rb +2 -2
  168. data/meta/self.rb +1 -1
  169. data/meta/send.rb +228 -55
  170. data/meta/str.rb +1 -1
  171. data/meta/super.rb +3 -3
  172. data/meta/{symbol.rb → sym.rb} +1 -1
  173. data/meta/true.rb +1 -1
  174. data/meta/until.rb +1 -1
  175. data/meta/while.rb +2 -2
  176. data/meta/yield.rb +1 -1
  177. data/mutant-rspec.gemspec +2 -2
  178. data/mutant.gemspec +6 -5
  179. data/spec/integration/mutant/isolation/fork_spec.rb +8 -0
  180. data/spec/integration/mutant/rspec_spec.rb +1 -1
  181. data/spec/integration/mutant/test_mutator_handles_types_spec.rb +1 -2
  182. data/spec/integrations.yml +93 -24
  183. data/spec/spec_helper.rb +12 -7
  184. data/spec/support/compress_helper.rb +1 -1
  185. data/spec/support/corpus.rb +115 -50
  186. data/spec/support/fake_actor.rb +5 -5
  187. data/spec/support/file_system.rb +1 -1
  188. data/spec/support/ice_nine_config.rb +3 -3
  189. data/spec/support/ruby_vm.rb +11 -12
  190. data/spec/support/shared_context.rb +22 -13
  191. data/spec/support/test_app.rb +1 -1
  192. data/spec/support/warning.rb +64 -0
  193. data/spec/support/warnings.yml +4 -0
  194. data/spec/support/xspec.rb +177 -0
  195. data/spec/unit/mutant/actor/env_spec.rb +2 -2
  196. data/spec/unit/mutant/actor/sender_spec.rb +1 -1
  197. data/spec/unit/mutant/ast/meta/send_spec.rb +1 -1
  198. data/spec/unit/mutant/ast/named_children_spec.rb +26 -0
  199. data/spec/unit/mutant/ast/regexp/parse_spec.rb +7 -0
  200. data/spec/unit/mutant/ast/regexp/supported_predicate_spec.rb +14 -0
  201. data/spec/unit/mutant/ast/regexp/transformer/lookup_table/table_spec.rb +19 -0
  202. data/spec/unit/mutant/ast/regexp/transformer/lookup_table_spec.rb +33 -0
  203. data/spec/unit/mutant/ast/regexp/transformer_spec.rb +19 -0
  204. data/spec/unit/mutant/ast/regexp_spec.rb +617 -0
  205. data/spec/unit/mutant/cli_spec.rb +7 -45
  206. data/spec/unit/mutant/context_spec.rb +4 -7
  207. data/spec/unit/mutant/env/{boostrap_spec.rb → bootstrap_spec.rb} +2 -2
  208. data/spec/unit/mutant/env_spec.rb +13 -16
  209. data/spec/unit/mutant/expression/namespace/{flat_spec.rb → exact_spec.rb} +0 -0
  210. data/spec/unit/mutant/integration/rspec_spec.rb +2 -2
  211. data/spec/unit/mutant/integration_spec.rb +14 -0
  212. data/spec/unit/mutant/isolation/fork_spec.rb +155 -0
  213. data/spec/unit/mutant/isolation/none_spec.rb +16 -0
  214. data/spec/unit/mutant/loader_spec.rb +1 -1
  215. data/spec/unit/mutant/matcher/methods/instance_spec.rb +2 -4
  216. data/spec/unit/mutant/meta/example/dsl_spec.rb +106 -0
  217. data/spec/unit/mutant/meta/example/verification_spec.rb +120 -0
  218. data/spec/unit/mutant/meta/example_spec.rb +32 -0
  219. data/spec/unit/mutant/mutator/node_spec.rb +37 -4
  220. data/spec/unit/mutant/mutator_spec.rb +21 -0
  221. data/spec/unit/mutant/{runner → parallel}/driver_spec.rb +0 -0
  222. data/spec/unit/mutant/parallel/master_spec.rb +13 -13
  223. data/spec/unit/mutant/registry_spec.rb +47 -0
  224. data/spec/unit/mutant/reporter/cli/printer/config_spec.rb +0 -4
  225. data/spec/unit/mutant/reporter/cli/printer/env_progress_spec.rb +0 -8
  226. data/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb +0 -2
  227. data/spec/unit/mutant/reporter/cli/printer/status_progressive_spec.rb +0 -8
  228. data/spec/unit/mutant/reporter/cli/printer/status_spec.rb +0 -34
  229. data/spec/unit/mutant/reporter/cli_spec.rb +0 -22
  230. data/spec/unit/mutant/repository/diff_spec.rb +6 -6
  231. data/spec/unit/mutant/require_highjack_spec.rb +38 -14
  232. data/spec/unit/mutant/result/env_spec.rb +1 -4
  233. data/spec/unit/mutant/runner_spec.rb +1 -1
  234. data/spec/unit/mutant/subject/method/instance_spec.rb +1 -1
  235. data/spec/unit/mutant/subject_spec.rb +3 -3
  236. data/spec/unit/mutant/util/one_spec.rb +20 -0
  237. data/spec/unit/mutant/zombifier_spec.rb +18 -18
  238. data/test_app/{Gemfile.rspec3.3 → Gemfile.rspec3.5} +2 -2
  239. metadata +94 -24
  240. data/TODO +0 -21
  241. data/lib/mutant/mutator/node/blockarg.rb +0 -13
  242. data/lib/mutant/mutator/node/restarg.rb +0 -13
  243. data/lib/mutant/mutator/registry.rb +0 -49
  244. data/meta/boolean.rb +0 -13
  245. data/meta/regex.rb +0 -19
  246. data/spec/unit/mutant/isolation_spec.rb +0 -104
  247. data/spec/unit/mutant/mutator/registry_spec.rb +0 -57
@@ -16,7 +16,7 @@ module FakeActor
16
16
  end
17
17
  block.call(other.message) if block
18
18
  end
19
- end
19
+ end # Expectation
20
20
 
21
21
  class MessageSequence
22
22
  include Adamantium::Flat, Concord::Public.new(:messages)
@@ -47,7 +47,7 @@ module FakeActor
47
47
  def consumed?
48
48
  messages.empty?
49
49
  end
50
- end
50
+ end # MessageSequence
51
51
 
52
52
  class Env
53
53
  include Concord.new(:messages, :mailbox_names)
@@ -89,7 +89,7 @@ module FakeActor
89
89
  def bind(sender)
90
90
  Mutant::Actor::Binding.new(self, sender)
91
91
  end
92
- end
92
+ end # Mailbox
93
93
 
94
94
  class Sender
95
95
  include Concord.new(:name, :messages)
@@ -105,5 +105,5 @@ module FakeActor
105
105
  def call
106
106
  messages.receiving(name)
107
107
  end
108
- end
109
- end
108
+ end # Receiver
109
+ end # FakeActor
@@ -44,7 +44,7 @@ module MutantSpec
44
44
  def state
45
45
  file_system.state(pathname.to_s)
46
46
  end
47
- end # Pathname
47
+ end # FakePathname
48
48
 
49
49
  class FileSystem
50
50
  include Adamantium, Concord.new(:file_states)
@@ -3,6 +3,6 @@ require 'ice_nine'
3
3
  module IceNine
4
4
  class Freezer
5
5
  class RSpec < NoFreeze
6
- end
7
- end
8
- end
6
+ end # RSpec
7
+ end # Freezer
8
+ end # IceNine
@@ -3,18 +3,19 @@ module MutantSpec
3
3
  # require semantics Zombifier relies on in a way we can avoid having to
4
4
  # mock around everywhere to test every detail.
5
5
  class RubyVM
6
- include Concord.new(:expected_events)
6
+ include Concord::Public.new(:expected_events)
7
7
 
8
8
  # An event being observed by the VM handlers
9
9
  class EventObservation
10
10
  include Concord::Public.new(:type, :payload)
11
- end
11
+ end # EventObservation
12
12
 
13
13
  # An event being expected, can advance the VM
14
14
  class EventExpectation
15
15
  include AbstractType, Anima.new(
16
16
  :expected_payload,
17
- :trigger_requires
17
+ :trigger_requires,
18
+ :return_value
18
19
  )
19
20
 
20
21
  DEFAULTS = IceNine.deep_freeze(trigger_requires: [])
@@ -29,29 +30,28 @@ module MutantSpec
29
30
  end
30
31
 
31
32
  trigger_requires.each(&vm.method(:require))
33
+
34
+ self
32
35
  end
33
36
 
34
37
  private
35
38
 
36
- abstract_method :advance_vm
37
-
38
39
  def match?(observation)
39
40
  observation.type.eql?(self.class) && observation.payload.eql?(expected_payload)
40
41
  end
41
42
 
42
43
  # Expectation and advance on require calls
43
44
  class Require < self
44
- end
45
+ end # Require
45
46
 
46
47
  # Expectation and advance on eval calls
47
48
  class Eval < self
48
- end
49
- end
49
+ end # Eval
50
+ end # EventExpectation
50
51
 
51
52
  # A fake implementation of Kernel#require
52
53
  def require(logical_name)
53
54
  handle_event(EventObservation.new(EventExpectation::Require, logical_name: logical_name))
54
- self
55
55
  end
56
56
 
57
57
  # A fake implementation of Kernel#eval
@@ -64,7 +64,6 @@ module MutantSpec
64
64
  source_location: location
65
65
  )
66
66
  )
67
- self
68
67
  end
69
68
 
70
69
  # Test if VM events where fully processed
@@ -77,7 +76,7 @@ module MutantSpec
77
76
  def handle_event(observation)
78
77
  fail "Unexpected event: #{observation.type} / #{observation.payload}" if expected_events.empty?
79
78
 
80
- expected_events.slice!(0).handle(self, observation)
79
+ expected_events.slice!(0).handle(self, observation).return_value
81
80
  end
82
- end
81
+ end # RubyVM
83
82
  end # MutantSpec
@@ -1,9 +1,18 @@
1
1
  # rubocop:disable ModuleLength
2
2
  module SharedContext
3
+ # Prepend an anonymous module with the new `with` method
4
+ #
5
+ # Using an anonymous module eliminates warnings where `setup_shared_context`
6
+ # is used and one of the shared methods is immediately redefined.
3
7
  def with(name, &block)
4
- define_method(name) do
5
- super().with(instance_eval(&block))
6
- end
8
+ new_definition =
9
+ Module.new do
10
+ define_method(name) do
11
+ super().with(instance_eval(&block))
12
+ end
13
+ end
14
+
15
+ prepend(new_definition)
7
16
  end
8
17
 
9
18
  def messages(&block)
@@ -52,8 +61,8 @@ module SharedContext
52
61
 
53
62
  let(:config) do
54
63
  Mutant::Config::DEFAULT.with(
55
- jobs: 1,
56
- reporter: Mutant::Reporter::Null.new
64
+ jobs: 1,
65
+ reporter: Mutant::Reporter::Null.new
57
66
  )
58
67
  end
59
68
 
@@ -94,19 +103,19 @@ module SharedContext
94
103
 
95
104
  let(:mutation_a_test_result) do
96
105
  Mutant::Result::Test.new(
97
- tests: [test_a],
98
- passed: false,
99
- runtime: 1.0,
106
+ tests: [test_a],
107
+ passed: false,
108
+ runtime: 1.0,
100
109
  output: 'mutation a test result output'
101
110
  )
102
111
  end
103
112
 
104
113
  let(:mutation_b_test_result) do
105
114
  Mutant::Result::Test.new(
106
- tests: [test_a],
107
- passed: false,
108
- runtime: 1.0,
109
- output: 'mutation b test result output'
115
+ tests: [test_a],
116
+ passed: false,
117
+ runtime: 1.0,
118
+ output: 'mutation b test result output'
110
119
  )
111
120
  end
112
121
 
@@ -126,4 +135,4 @@ module SharedContext
126
135
  subject_a_result.with(mutation_results: [mutation_a_result])
127
136
  end
128
137
  end
129
- end
138
+ end # SharedContext
@@ -2,4 +2,4 @@ module TestApp
2
2
  def self.root
3
3
  File.expand_path('../../../test_app', __FILE__)
4
4
  end
5
- end
5
+ end # TestApp
@@ -0,0 +1,64 @@
1
+ require 'yaml'
2
+ require 'equalizer'
3
+ require 'memoizable'
4
+ require 'ice_nine'
5
+
6
+ module MutantSpec
7
+ class Warning
8
+ def self.assert_no_warnings
9
+ return if EXTRACTOR.warnings.empty?
10
+
11
+ fail UnexpectedWarnings, EXTRACTOR.warnings.to_a
12
+ end
13
+
14
+ class UnexpectedWarnings < StandardError
15
+ MSG = 'Unexpected warnings: %s'.freeze
16
+
17
+ def initialize(warnings)
18
+ super(MSG % warnings.join("\n"))
19
+ end
20
+ end
21
+
22
+ class Extractor < DelegateClass(IO)
23
+ PATTERN = /\A(?:.+):(?:\d+): warning: (?:.+)\n\z/.freeze
24
+
25
+ include Equalizer.new(:whitelist, :seen, :io), Memoizable
26
+
27
+ def initialize(io, whitelist)
28
+ @whitelist = whitelist
29
+ @seen = Set.new
30
+ @io = io
31
+
32
+ super(io)
33
+ end
34
+
35
+ def write(message)
36
+ return super if PATTERN !~ message
37
+
38
+ add(message.chomp)
39
+
40
+ self
41
+ end
42
+
43
+ def warnings
44
+ seen.dup
45
+ end
46
+ memoize :warnings
47
+
48
+ private
49
+
50
+ def add(warning)
51
+ return if whitelist.any?(&warning.public_method(:end_with?))
52
+
53
+ seen << warning
54
+ end
55
+
56
+ attr_reader :whitelist, :seen, :io
57
+ end
58
+
59
+ warnings = Pathname.new(__dir__).join('warnings.yml').freeze
60
+ whitelist = IceNine.deep_freeze(YAML.load(warnings.read))
61
+
62
+ EXTRACTOR = Extractor.new(STDERR, whitelist)
63
+ end
64
+ end
@@ -0,0 +1,4 @@
1
+ ---
2
+ - 'lib/parser/lexer.rb:10791: warning: assigned but unused variable - testEof'
3
+ - 'lib/parser/source/rewriter.rb:392: warning: assigned but unused variable - begin_pos'
4
+ - 'lib/regexp_parser/scanner.rb:1646: warning: assigned but unused variable - testEof'
@@ -0,0 +1,177 @@
1
+ module XSpec
2
+ class MessageReaction
3
+ include Concord.new(:event_list)
4
+
5
+ TERMINATE_EVENTS = IceNine.deep_freeze(%i[return exception].to_set)
6
+ VALID_EVENTS = IceNine.deep_freeze(%i[return exception yields].to_set)
7
+
8
+ private_constant(*constants(false))
9
+
10
+ def call(observation)
11
+ event_list.map do |event, object|
12
+ __send__(event, observation, object)
13
+ end.last
14
+ end
15
+
16
+ # Parse events into reaction
17
+ #
18
+ # @param [Array{Symbol,Object}, Hash{Symbol,Object}]
19
+ #
20
+ # @return [MessageReaction]
21
+ def self.parse(events)
22
+ event_list = events.to_a
23
+ assert_valid(event_list)
24
+ new(event_list)
25
+ end
26
+
27
+ private
28
+
29
+ def return(_, value)
30
+ value
31
+ end
32
+
33
+ def exception(_, exception)
34
+ fail exception
35
+ end
36
+
37
+ def yields(observation, yields)
38
+ block = observation.block or fail 'No block passed where expected'
39
+
40
+ validate_block_arity(observation, yields)
41
+
42
+ block.call(*yields)
43
+ end
44
+
45
+ def validate_block_arity(observation, yields)
46
+ expected, observed = yields.length, observation.block.arity
47
+
48
+ # block allows anything we can skip the check
49
+ return if observed.equal?(-1)
50
+ fail 'Optargs currently not supported' if observed < -1
51
+ block_arity_mismatch(observation, expected, observed) unless expected.equal?(observed)
52
+ end
53
+
54
+ def block_arity_mismatch(observation, expected, observed)
55
+ fail "block arity mismatch, expected #{expected} observed #{observed}\nobservation:\n#{observation.inspect}"
56
+ end
57
+
58
+ alias_method :yields_return, :yields
59
+
60
+ def self.assert_valid(event_list)
61
+ assert_not_empty(event_list)
62
+ assert_valid_events(event_list)
63
+ assert_total(event_list)
64
+ end
65
+ private_class_method :assert_valid
66
+
67
+ def self.assert_valid_events(event_list)
68
+ event_list.map(&:first).each do |event|
69
+ fail "Invalid event: #{event}" unless VALID_EVENTS.include?(event)
70
+ end
71
+ end
72
+ private_class_method :assert_valid_events
73
+
74
+ def self.assert_not_empty(event_list)
75
+ fail 'no events' if event_list.empty?
76
+ end
77
+ private_class_method :assert_not_empty
78
+
79
+ def self.assert_total(event_list)
80
+ if event_list[0..-2].map(&:first).any?(&TERMINATE_EVENTS.method(:include?))
81
+ fail "Reaction not total: #{event_list}"
82
+ end
83
+ end
84
+ private_class_method :assert_total
85
+ end # MessageReaction
86
+
87
+ class MessageExpectation
88
+ include Anima.new(:receiver, :selector, :arguments, :reaction)
89
+
90
+ # rubocop:disable ParameterLists
91
+ def self.parse(receiver:, selector:, arguments: [], reaction: nil)
92
+ new(
93
+ receiver: receiver,
94
+ selector: selector,
95
+ arguments: arguments,
96
+ reaction: MessageReaction.parse(reaction || { return: nil })
97
+ )
98
+ end
99
+
100
+ def call(observation)
101
+ Verifier.new(self, observation).call
102
+ end
103
+
104
+ class Verifier
105
+ include Concord.new(:expectation, :observation)
106
+
107
+ VERIFIED_ATTRIBUTES = IceNine.deep_freeze(%i[receiver selector arguments])
108
+
109
+ def call
110
+ VERIFIED_ATTRIBUTES.each(&method(:assert_expected_attribute))
111
+
112
+ expectation.reaction.call(observation)
113
+ end
114
+
115
+ private
116
+
117
+ def assert_expected_attribute(name)
118
+ error("#{name} mismatch") unless observation.public_send(name).eql?(expectation.public_send(name))
119
+ end
120
+
121
+ def error(message)
122
+ fail "#{message},\n observation:\n #{observation.inspect}\n expectation:\n #{expectation.inspect}"
123
+ end
124
+
125
+ def trigger_exception
126
+ exception = expectation.exception
127
+ fail exception if exception
128
+ end
129
+
130
+ end # Verifier
131
+ end # MessageExpectation
132
+
133
+ class MessageObservation
134
+ include Anima.new(:receiver, :selector, :arguments, :block)
135
+ end # MessageObservation
136
+
137
+ class ExpectationVerifier
138
+ include Concord.new(:expectations)
139
+
140
+ def call(observation)
141
+ expectation = expectations.shift or fail "No expected message but observed #{observation}"
142
+ expectation.call(observation)
143
+ end
144
+
145
+ def assert_done
146
+ expectations.empty? or fail "unconsumed expectations:\n#{expectations.map(&:inspect).join}"
147
+ end
148
+
149
+ # rubocop:disable MethodLength
150
+ def self.verify(rspec_context, expectations)
151
+ verifier = new(expectations)
152
+
153
+ hooks = expectations
154
+ .map { |expectation| [expectation.receiver, expectation.selector] }
155
+ .to_set
156
+
157
+ hooks.each do |receiver, selector|
158
+ rspec_context.instance_eval do
159
+ allow(receiver).to receive(selector) do |*arguments, &block|
160
+ verifier.call(
161
+ MessageObservation.new(
162
+ receiver: receiver,
163
+ selector: selector,
164
+ arguments: arguments,
165
+ block: block
166
+ )
167
+ )
168
+ end
169
+ end
170
+ end
171
+
172
+ yield
173
+
174
+ verifier.assert_done
175
+ end
176
+ end # ExpectationVerifier
177
+ end # XSpec