mutant 0.5.10 → 0.5.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/Changelog.md +12 -1
  4. data/README.md +3 -0
  5. data/Rakefile +0 -9
  6. data/bin/mutant +1 -1
  7. data/config/flay.yml +1 -1
  8. data/config/mutant.yml +13 -0
  9. data/config/reek.yml +6 -2
  10. data/lib/mutant/constants.rb +0 -13
  11. data/lib/mutant/mutator/node/conditional_loop.rb +2 -1
  12. data/lib/mutant/mutator/node/generic.rb +1 -1
  13. data/lib/mutant/mutator/node/if.rb +1 -1
  14. data/lib/mutant/mutator/node/literal/regex.rb +2 -2
  15. data/lib/mutant/mutator/node/match_current_line.rb +27 -0
  16. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +1 -1
  17. data/lib/mutant/mutator/node/nthref.rb +3 -1
  18. data/lib/mutant/mutator/node/rescue.rb +0 -12
  19. data/lib/mutant/mutator/node/send/attribute_assignment.rb +51 -0
  20. data/lib/mutant/mutator/node/send/index.rb +43 -0
  21. data/lib/mutant/mutator/node/send.rb +7 -37
  22. data/lib/mutant/mutator/node.rb +4 -12
  23. data/lib/mutant/mutator.rb +2 -26
  24. data/lib/mutant/node_helpers.rb +3 -3
  25. data/lib/mutant/require_highjack.rb +64 -0
  26. data/lib/mutant/subject/method/instance.rb +9 -1
  27. data/lib/mutant/version.rb +1 -1
  28. data/lib/mutant/warning_expectation.rb +40 -0
  29. data/lib/mutant/warning_filter.rb +74 -0
  30. data/lib/mutant/zombifier/file.rb +81 -0
  31. data/lib/mutant/zombifier.rb +61 -240
  32. data/lib/mutant.rb +57 -1
  33. data/lib/parser_extensions.rb +25 -0
  34. data/mutant.gemspec +4 -3
  35. data/spec/integration/mutant/corpus_spec.rb +121 -0
  36. data/spec/integration/mutant/rspec_spec.rb +1 -1
  37. data/spec/integration/mutant/test_mutator_handles_types_spec.rb +1 -1
  38. data/spec/integration/mutant/zombie_spec.rb +3 -3
  39. data/spec/integrations.yml +23 -0
  40. data/spec/shared/mutator_behavior.rb +22 -72
  41. data/spec/spec_helper.rb +4 -2
  42. data/spec/support/mutation_verifier.rb +95 -0
  43. data/spec/unit/mutant/mutator/node/conditional_loop_spec.rb +16 -0
  44. data/spec/unit/mutant/mutator/node/match_current_line_spec.rb +4 -4
  45. data/spec/unit/mutant/mutator/node/named_value/access_spec.rb +6 -3
  46. data/spec/unit/mutant/mutator/node/nthref_spec.rb +2 -2
  47. data/spec/unit/mutant/mutator/node/op_assgn_spec.rb +3 -1
  48. data/spec/unit/mutant/mutator/node/send_spec.rb +0 -1
  49. data/spec/unit/mutant/require_highjack_spec.rb +54 -0
  50. data/spec/unit/mutant/subject/method/instance_spec.rb +34 -3
  51. data/spec/unit/mutant/warning_expectation.rb +71 -0
  52. data/spec/unit/mutant/warning_filter_spec.rb +94 -0
  53. metadata +40 -9
  54. data/lib/mutant/singleton_methods.rb +0 -30
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 84a8405d55414b2cd802d72e2a8fab5a3a2e9383
4
- data.tar.gz: e4acca9abe75f892f73c1b830473d17630ec1001
3
+ metadata.gz: bff75074f1898f689c4215c2a19f14d02863ccc4
4
+ data.tar.gz: 576dbee5c9743707641a1f3fac543c0c76d85832
5
5
  SHA512:
6
- metadata.gz: 1ca5f7c4ff72450792d49df2c40d10a45335442d095b54564c8f650bf9218fed4b9821994fa17162d27e4b1e2c1087cfc2f4bf8661e3adf63e73c187e57475d1
7
- data.tar.gz: 8a0154fce8d6a39824108c609c2a76f170f5fb855fa8b5cfb4224b91a5528106b41d83500e5af0b67b2d7775ca4a00b24e5ffbe090369efa94c4e9525e0fccc4
6
+ metadata.gz: 47b0085d022c5e09f1013c824989f2449838bea7b9681b257e9e8ef7641a39fd522dacdf643e20e177422ff8c8a944b9d34a20c5c0f464f68f4458c02b2f1702
7
+ data.tar.gz: 47f0863f96176f3c90200a4a6ed46c86e31a39de77a8d35a66d7728446b38422288d162e5a0f1a4e54bc2a750ee34f869958e7019048652522d5e15c346e6cf7
data/.rubocop.yml CHANGED
@@ -4,4 +4,5 @@ AllCops:
4
4
  Excludes:
5
5
  - 'Gemfile.devtools'
6
6
  - 'vendor/**'
7
+ - 'tmp/**'
7
8
  - 'benchmarks/**'
data/Changelog.md CHANGED
@@ -1,8 +1,19 @@
1
+ # v0.5.11 2014-04-21
2
+
3
+ Changes:
4
+
5
+ * Fix crash on while and until without body
6
+ * Better require highjack based zombifier
7
+ * Do not mutate nthref $1 to gvar $0
8
+ * Use faster duplicate guarding hashing AST::Node intances
9
+ * Fix lots of shadowed invalid ASTs
10
+ * Fix undefine initialize warnings, Closes #175
11
+
1
12
  # v0.5.10 2014-04-06
2
13
 
3
14
  Changes:
4
15
 
5
- * Fix crash without conditionals
16
+ * Fix crash on case without conditional
6
17
  * Remove dependency to descendants tracker
7
18
  * Add mutation #== => #eql?, #equal?
8
19
  * Add mutation #eql? => #equal?
data/README.md CHANGED
@@ -17,6 +17,9 @@ Mutant supports MRI and RBX 1.9 and 2.0, while support for jruby is planned.
17
17
  It should also work under any ruby engine that supports POSIX-fork(2) semantics.
18
18
  Support for MRI 2.1 is unstable, because this MRI release segfaults on basic metaprogramming mutants dependencies do.
19
19
 
20
+ Mutant uses a pure ruby [parser](https://github.com/whitequark/parser) and an [unparser](https://github.com/mbj/unparser)
21
+ to do its magic.
22
+
20
23
  Integrations
21
24
  ------------
22
25
 
data/Rakefile CHANGED
@@ -3,12 +3,3 @@
3
3
  require 'devtools'
4
4
 
5
5
  Devtools.init_rake_tasks
6
-
7
- Rake.application.load_imports
8
- task('metrics:mutant').clear
9
-
10
- namespace :metrics do
11
- task :mutant => :coverage do
12
- $stderr.puts 'Mutant self mutation is disable till mutant is fast enough for travis'
13
- end
14
- end
data/bin/mutant CHANGED
@@ -10,7 +10,7 @@ require 'mutant'
10
10
  namespace =
11
11
  if ARGV.include?('--zombie')
12
12
  $stderr.puts('Running mutant zombified!')
13
- Mutant::Zombifier.zombify
13
+ Mutant.zombify
14
14
  Zombie::Mutant
15
15
  else
16
16
  Mutant
data/config/flay.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 18
3
- total_score: 804
3
+ total_score: 836
data/config/mutant.yml CHANGED
@@ -1,2 +1,15 @@
1
1
  name: mutant
2
2
  namespace: Mutant
3
+ zombify: true
4
+ expect_coverage: 64.99
5
+ ignore_subjects:
6
+ # Mutation causes infinite runtime
7
+ - Mutant::Runner.lookup
8
+ # Suboptimal test selection stragegy (will be fixed soon) causes timeouts on CI
9
+ - Mutant::Zombifier*
10
+ - Mutant::Reporter*
11
+ - Mutant::CLI*
12
+ - Mutant.singleton_subclass_instance
13
+ - Mutant.symbolset
14
+ # Executing this has undefined behavior with the zombifier
15
+ - Mutant.zombify
data/config/reek.yml CHANGED
@@ -52,6 +52,9 @@ NestedIterators:
52
52
  - Mutant::CLI#parse
53
53
  - Mutant::Mutator::Util::Array::Element#dispatch
54
54
  - Mutant::Reporter::CLI::Printer::Config::Runner#generic_stats
55
+ - Mutant::RequireHighjack#infect
56
+ - Mutant::RequireHighjack#desinfect
57
+ - Parser::Lexer#self.new
55
58
  max_allowed_nesting: 1
56
59
  ignore_iterators: []
57
60
  NilCheck:
@@ -85,7 +88,8 @@ TooManyStatements:
85
88
  - Mutant::Reporter::CLI#colorized_diff
86
89
  - Mutant::Reporter::CLI::Printer::Config::Runner#run
87
90
  - Mutant::Runner#dispatch
88
- - Mutant::Zombifier::File#self.find_uncached
91
+ - Mutant::Zombifier::File#self.find
92
+ - Mutant::RequireHighjack#infect
89
93
  # How mutant does CLI parsing is shit
90
94
  - Mutant::CLI#parse
91
95
  - Mutant::CLI#initialize
@@ -123,7 +127,7 @@ UncommunicativeVariableName:
123
127
  - !ruby/regexp /^.$/
124
128
  - !ruby/regexp /[0-9]$/
125
129
  - !ruby/regexp /[A-Z]/
126
- accept: []
130
+ accept: ['force_utf32']
127
131
  UnusedParameters:
128
132
  enabled: true
129
133
  exclude: []
@@ -2,19 +2,6 @@
2
2
 
3
3
  module Mutant
4
4
 
5
- # Return a frozen set of symbols from string enumerable
6
- #
7
- # @param [Enumerable<String>]
8
- #
9
- # @return [Set<Symbol>]
10
- #
11
- # @api private
12
- #
13
- def self.symbolset(strings)
14
- strings.map(&:to_sym).to_set.freeze
15
- end
16
- private_class_method :symbolset
17
-
18
5
  # Set of nodes that cannot be on the LHS of an assignment
19
6
  NOT_ASSIGNABLE = symbolset %w(
20
7
  int float str dstr class module self
@@ -21,8 +21,9 @@ module Mutant
21
21
  #
22
22
  def dispatch
23
23
  emit_condition_mutations
24
- emit_body_mutations
24
+ emit_body_mutations if body
25
25
  emit_body(nil)
26
+ emit_body(RAISE)
26
27
  emit_nil
27
28
  end
28
29
 
@@ -13,7 +13,7 @@ module Mutant
13
13
  :ensure, :redo, :defined?, :regopt, :retry, :arg_expr,
14
14
  :kwrestarg, :kwoptarg, :kwarg, :undef, :module, :empty,
15
15
  :alias, :for, :xstr, :back_ref, :class,
16
- :sclass, :match_with_lvasgn, :match_current_line, :while_post,
16
+ :sclass, :match_with_lvasgn, :while_post,
17
17
  :until_post, :preexe, :postexe, :iflipflop, :eflipflop, :kwsplat,
18
18
  :shadowarg
19
19
  )
@@ -33,7 +33,7 @@ module Mutant
33
33
  #
34
34
  def mutate_condition
35
35
  emit_condition_mutations
36
- emit_self(n_not(condition), if_branch, else_branch)
36
+ emit_self(n_not(condition), if_branch, else_branch) unless condition.type == :match_current_line
37
37
  emit_self(N_TRUE, if_branch, else_branch)
38
38
  emit_self(N_FALSE, if_branch, else_branch)
39
39
  end
@@ -31,11 +31,11 @@ module Mutant
31
31
  # @api private
32
32
  #
33
33
  def dispatch
34
- emit_nil
34
+ emit_nil unless parent_type == :match_current_line
35
35
  children.each_with_index do |child, index|
36
36
  mutate_child(index) unless child.type == :str
37
37
  end
38
- emit_self(s(:str, EMPTY_STRING), options)
38
+ emit_self(options)
39
39
  emit_self(s(:str, NULL_REGEXP_SOURCE), options)
40
40
  end
41
41
 
@@ -0,0 +1,27 @@
1
+ module Mutant
2
+ class Mutator
3
+ class Node
4
+ # Emitter for perl style match current line node
5
+ class MatchCurrentLine < self
6
+
7
+ handle :match_current_line
8
+
9
+ children :regexp
10
+
11
+ private
12
+
13
+ # Emit mutants
14
+ #
15
+ # @return [undefined]
16
+ #
17
+ # @api private
18
+ #
19
+ def dispatch
20
+ emit_nil
21
+ emit_regexp_mutations
22
+ end
23
+
24
+ end # MatchCurrentLine
25
+ end # Node
26
+ end # Mutator
27
+ end # Mutant
@@ -42,7 +42,7 @@ module Mutant
42
42
  def mutate_name
43
43
  prefix = MAP.fetch(node.type)
44
44
  Mutator::Util::Symbol.each(name, self) do |name|
45
- emit_name(prefix + name.to_s)
45
+ emit_name(:"#{prefix}#{name}")
46
46
  end
47
47
  end
48
48
 
@@ -19,7 +19,9 @@ module Mutant
19
19
  # @api private
20
20
  #
21
21
  def dispatch
22
- emit_number(number - 1)
22
+ unless number.equal?(1)
23
+ emit_number(number - 1)
24
+ end
23
25
  emit_number(number + 1)
24
26
  end
25
27
 
@@ -8,18 +8,6 @@ module Mutant
8
8
 
9
9
  handle :rescue
10
10
 
11
- # Return identity
12
- #
13
- # @param [Parser::AST::Node] node
14
- #
15
- # @return [String]
16
- #
17
- # @api private
18
- #
19
- def self.identity(node)
20
- super(NodeHelpers.s(:kwbegin, node))
21
- end
22
-
23
11
  end # Rescue
24
12
  end # Node
25
13
  end # Mutator
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+
3
+ module Mutant
4
+ class Mutator
5
+ class Node
6
+ class Send
7
+
8
+ # Mutator for sends that correspond to an attribute assignment
9
+ class AttributeAssignment < self
10
+
11
+ private
12
+
13
+ # Emit mutations
14
+ #
15
+ # @return [undefined]
16
+ #
17
+ # @api private
18
+ #
19
+ def dispatch
20
+ normal_dispatch
21
+ emit_attribute_read
22
+ end
23
+
24
+ # Mutate arguments
25
+ #
26
+ # @return [undefined]
27
+ #
28
+ # @api private
29
+ #
30
+ def mutate_arguments
31
+ remaining_children_indices.each do |index|
32
+ mutate_child(index)
33
+ end
34
+ end
35
+
36
+ # Emit attribute read
37
+ #
38
+ # @return [undefined]
39
+ #
40
+ # @api private
41
+ #
42
+ def emit_attribute_read
43
+ emit_self(receiver, selector.to_s[0..-2].to_sym)
44
+ end
45
+
46
+ end # AttributeAssignment
47
+
48
+ end # Send
49
+ end # Node
50
+ end # Mutator
51
+ end # Mutant
@@ -0,0 +1,43 @@
1
+ # encoding: UTF-8
2
+
3
+ module Mutant
4
+ class Mutator
5
+ class Node
6
+ class Send
7
+ # Base mutator for index operations
8
+ class Index < self
9
+
10
+ # Mutator for index references
11
+ class Reference < self
12
+
13
+ # Perform dispatch
14
+ #
15
+ # @return [undefined]
16
+ #
17
+ # @api private
18
+ #
19
+ def dispatch
20
+ emit(receiver)
21
+ end
22
+
23
+ end # Reference
24
+
25
+ # Mutator for index assignments
26
+ class Assign < self
27
+
28
+ # Perform dispatch
29
+ #
30
+ # @return [undefined]
31
+ #
32
+ # @api private
33
+ #
34
+ def dispatch
35
+ emit(receiver)
36
+ end
37
+
38
+ end # Assign
39
+ end # Index
40
+ end # Send
41
+ end # Node
42
+ end # Mutator
43
+ end # Mutant
@@ -22,40 +22,6 @@ module Mutant
22
22
  INDEX_ASSIGN = :[]=
23
23
  ASSIGN_SUFFIX = '='.freeze
24
24
 
25
- # Base mutator for index operations
26
- class Index < self
27
-
28
- # Mutator for index references
29
- class Reference < self
30
-
31
- # Perform dispatch
32
- #
33
- # @return [undefined]
34
- #
35
- # @api private
36
- #
37
- def dispatch
38
- emit(receiver)
39
- end
40
-
41
- end # Reference
42
-
43
- # Mutator for index assignments
44
- class Assign < self
45
-
46
- # Perform dispatch
47
- #
48
- # @return [undefined]
49
- #
50
- # @api private
51
- #
52
- def dispatch
53
- emit(receiver)
54
- end
55
-
56
- end # Assign
57
- end # Index
58
-
59
25
  private
60
26
 
61
27
  # Perform dispatch
@@ -65,6 +31,7 @@ module Mutant
65
31
  # @api private
66
32
  #
67
33
  def dispatch
34
+ emit_nil
68
35
  case selector
69
36
  when INDEX_REFERENCE
70
37
  run(Index::Reference)
@@ -73,7 +40,6 @@ module Mutant
73
40
  else
74
41
  non_index_dispatch
75
42
  end
76
- emit_nil
77
43
  end
78
44
 
79
45
  # Perform non index dispatch
@@ -86,6 +52,8 @@ module Mutant
86
52
  case
87
53
  when binary_operator?
88
54
  run(Binary)
55
+ when attribute_assignment?
56
+ run(AttributeAssignment)
89
57
  else
90
58
  normal_dispatch
91
59
  end
@@ -173,7 +141,6 @@ module Mutant
173
141
  # @api private
174
142
  #
175
143
  def mutate_arguments
176
- return if arguments.empty?
177
144
  emit_self(receiver, selector)
178
145
  remaining_children_with_index.each do |node, index|
179
146
  mutate_child(index)
@@ -213,7 +180,10 @@ module Mutant
213
180
  # @api private
214
181
  #
215
182
  def emit_implicit_self
216
- if receiver.type == :self && !KEYWORDS.include?(selector) && !attribute_assignment?
183
+ if receiver.type == :self &&
184
+ !KEYWORDS.include?(selector) &&
185
+ !attribute_assignment? &&
186
+ !OP_ASSIGN.include?(parent_type)
217
187
  emit_receiver(nil)
218
188
  end
219
189
  end
@@ -9,18 +9,6 @@ module Mutant
9
9
  class Node < self
10
10
  include AbstractType, NodeHelpers, Unparser::Constants
11
11
 
12
- # Return identity of node
13
- #
14
- # @param [Parser::AST::Node] node
15
- #
16
- # @return [String]
17
- #
18
- # @api private
19
- #
20
- def self.identity(node)
21
- Unparser.unparse(node)
22
- end
23
-
24
12
  # Define named child
25
13
  #
26
14
  # @param [Symbol] name
@@ -58,6 +46,10 @@ module Mutant
58
46
  children.each_with_index.drop(names.length)
59
47
  end
60
48
 
49
+ define_method(:remaining_children_indices) do
50
+ children.each_index.drop(names.length)
51
+ end
52
+
61
53
  define_method(:remaining_children) do
62
54
  children.drop(names.length)
63
55
  end
@@ -33,18 +33,6 @@ module Mutant
33
33
  end
34
34
  private_class_method :handle
35
35
 
36
- # Return identity of object (for deduplication)
37
- #
38
- # @param [Object] object
39
- #
40
- # @return [Object]
41
- #
42
- # @api private
43
- #
44
- def self.identity(object)
45
- object
46
- end
47
-
48
36
  # Return input
49
37
  #
50
38
  # @return [Object]
@@ -92,7 +80,7 @@ module Mutant
92
80
  # @api private
93
81
  #
94
82
  def new?(object)
95
- !@seen.include?(identity(object))
83
+ !@seen.include?(object)
96
84
  end
97
85
 
98
86
  # Add object to guarded values
@@ -104,19 +92,7 @@ module Mutant
104
92
  # @api private
105
93
  #
106
94
  def guard(object)
107
- @seen << identity(object)
108
- end
109
-
110
- # Return identity for input
111
- #
112
- # @param [Object] input
113
- #
114
- # @return [Object]
115
- #
116
- # @api private
117
- #
118
- def identity(input)
119
- self.class.identity(input)
95
+ @seen << object
120
96
  end
121
97
 
122
98
  # Dispatch node generations
@@ -18,13 +18,13 @@ module Mutant
18
18
  module_function :s
19
19
 
20
20
  NAN =
21
- s(:send, s(:float, 0.0), :/, s(:args, s(:float, 0.0)))
21
+ s(:send, s(:float, 0.0), :/, s(:float, 0.0))
22
22
  INFINITY =
23
- s(:send, s(:float, 1.0), :/, s(:args, s(:float, 0.0)))
23
+ s(:send, s(:float, 1.0), :/, s(:float, 0.0))
24
24
  NEW_OBJECT =
25
25
  s(:send, s(:const, s(:cbase), :Object), :new)
26
26
  NEGATIVE_INFINITY =
27
- s(:send, s(:float, -1.0), :/, s(:args, s(:float, 0.0)))
27
+ s(:send, s(:float, -1.0), :/, s(:float, 0.0))
28
28
 
29
29
  RAISE = s(:send, nil, :raise)
30
30
 
@@ -0,0 +1,64 @@
1
+ module Mutant
2
+ # Require highjack
3
+ class RequireHighjack
4
+ include Concord.new(:target, :callback)
5
+
6
+ # Return original method
7
+ #
8
+ # @return [#call]
9
+ #
10
+ # @api private
11
+ #
12
+ attr_reader :original
13
+
14
+ # Run block with highjacked require
15
+ #
16
+ # @return [self]
17
+ #
18
+ # @api pivate
19
+ #
20
+ def run
21
+ infect
22
+ yield
23
+ self
24
+ ensure
25
+ desinfect
26
+ end
27
+
28
+ # Infect kernel with highjack
29
+ #
30
+ # @return [self]
31
+ #
32
+ # @api pivate
33
+ #
34
+ def infect
35
+ callback = @callback
36
+ @original = target.method(:require)
37
+ target.module_eval do
38
+ undef :require
39
+ define_method(:require) do |logical_name|
40
+ callback.call(logical_name)
41
+ end
42
+ module_function :require
43
+ end
44
+ end
45
+
46
+ # Imperfectly desinfect kernel from highjack
47
+ #
48
+ # @return [self]
49
+ #
50
+ # @api private
51
+ #
52
+ def desinfect
53
+ original = @original
54
+ target.module_eval do
55
+ undef :require
56
+ define_method(:require) do |logical_name|
57
+ original.call(logical_name)
58
+ end
59
+ module_function :require
60
+ end
61
+ end
62
+
63
+ end # RequireHighjack
64
+ end # Mutant
@@ -31,7 +31,15 @@ module Mutant
31
31
  # @api private
32
32
  #
33
33
  def prepare
34
- scope.send(:undef_method, name)
34
+ expected_warnings =
35
+ if name.equal?(:initialize)
36
+ ["#{__FILE__}:#{__LINE__ + 5}: warning: undefining `initialize' may cause serious problems\n"]
37
+ else
38
+ []
39
+ end
40
+ WarningExpectation.new(expected_warnings).execute do
41
+ scope.send(:undef_method, name)
42
+ end
35
43
  self
36
44
  end
37
45
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Mutant
4
4
  # The current mutant version
5
- VERSION = '0.5.10'.freeze
5
+ VERSION = '0.5.11'.freeze
6
6
  end # Mutant
@@ -0,0 +1,40 @@
1
+ module Mutant
2
+ # A class to expect some warning message raising on absence of unexpected warnings
3
+ class WarningExpectation
4
+ include Adamantium::Flat, Concord.new(:expected)
5
+
6
+ # Error raised on expectation miss
7
+ class ExpectationError < RuntimeError
8
+ include Concord.new(:unexpected, :missing)
9
+
10
+ # Return exception message
11
+ #
12
+ # @return [String]
13
+ #
14
+ # @api private
15
+ #
16
+ def message
17
+ "Unexpected warnings: #{unexpected.inspect} missing warnigns: #{missing.inspect}"
18
+ end
19
+ end
20
+
21
+ # Execute blocks with warning expectations
22
+ #
23
+ # @return [self]
24
+ #
25
+ # @api private
26
+ #
27
+ def execute(&block)
28
+ warnings = WarningFilter.use do
29
+ block.call
30
+ end
31
+ missing = expected - warnings
32
+ unexpected = warnings - expected
33
+ if missing.any? or unexpected.any?
34
+ fail ExpectationError.new(unexpected, missing)
35
+ end
36
+ self
37
+ end
38
+
39
+ end # WarningExpectation
40
+ end # Mutant