mutant 0.5.19 → 0.5.20

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -4
  3. data/Changelog.md +9 -0
  4. data/Gemfile.devtools +15 -11
  5. data/README.md +10 -6
  6. data/config/flay.yml +1 -1
  7. data/config/reek.yml +3 -1
  8. data/lib/mutant.rb +41 -41
  9. data/lib/mutant/cache.rb +0 -2
  10. data/lib/mutant/cli.rb +6 -8
  11. data/lib/mutant/color.rb +0 -2
  12. data/lib/mutant/config.rb +0 -2
  13. data/lib/mutant/context.rb +0 -2
  14. data/lib/mutant/context/scope.rb +0 -2
  15. data/lib/mutant/delegator.rb +2 -0
  16. data/lib/mutant/diff.rb +4 -16
  17. data/lib/mutant/expression.rb +8 -7
  18. data/lib/mutant/expression/method.rb +3 -9
  19. data/lib/mutant/isolation.rb +65 -0
  20. data/lib/mutant/killer.rb +1 -3
  21. data/lib/mutant/loader.rb +0 -2
  22. data/lib/mutant/matcher.rb +0 -2
  23. data/lib/mutant/matcher/chain.rb +0 -2
  24. data/lib/mutant/matcher/filter.rb +0 -2
  25. data/lib/mutant/matcher/method.rb +1 -2
  26. data/lib/mutant/matcher/method/finder.rb +0 -2
  27. data/lib/mutant/matcher/method/instance.rb +1 -3
  28. data/lib/mutant/matcher/method/singleton.rb +0 -2
  29. data/lib/mutant/matcher/methods.rb +0 -2
  30. data/lib/mutant/matcher/namespace.rb +2 -6
  31. data/lib/mutant/matcher/null.rb +0 -2
  32. data/lib/mutant/matcher/scope.rb +0 -2
  33. data/lib/mutant/meta/example.rb +57 -18
  34. data/lib/mutant/meta/example/dsl.rb +10 -13
  35. data/lib/mutant/mutation.rb +0 -2
  36. data/lib/mutant/mutation/evil.rb +0 -2
  37. data/lib/mutant/mutation/neutral.rb +0 -2
  38. data/lib/mutant/mutator.rb +4 -31
  39. data/lib/mutant/mutator/node.rb +19 -18
  40. data/lib/mutant/mutator/node/and_asgn.rb +3 -6
  41. data/lib/mutant/mutator/node/argument.rb +0 -2
  42. data/lib/mutant/mutator/node/arguments.rb +47 -4
  43. data/lib/mutant/mutator/node/begin.rb +3 -10
  44. data/lib/mutant/mutator/node/binary.rb +0 -2
  45. data/lib/mutant/mutator/node/block.rb +0 -2
  46. data/lib/mutant/mutator/node/blockarg.rb +0 -2
  47. data/lib/mutant/mutator/node/break.rb +0 -2
  48. data/lib/mutant/mutator/node/case.rb +3 -6
  49. data/lib/mutant/mutator/node/conditional_loop.rb +0 -2
  50. data/lib/mutant/mutator/node/const.rb +1 -3
  51. data/lib/mutant/mutator/node/define.rb +0 -2
  52. data/lib/mutant/mutator/node/defined.rb +1 -3
  53. data/lib/mutant/mutator/node/dstr.rb +0 -2
  54. data/lib/mutant/mutator/node/dsym.rb +0 -2
  55. data/lib/mutant/mutator/node/generic.rb +0 -2
  56. data/lib/mutant/mutator/node/if.rb +8 -12
  57. data/lib/mutant/mutator/node/kwbegin.rb +0 -2
  58. data/lib/mutant/mutator/node/literal.rb +0 -2
  59. data/lib/mutant/mutator/node/literal/array.rb +2 -5
  60. data/lib/mutant/mutator/node/literal/boolean.rb +0 -2
  61. data/lib/mutant/mutator/node/literal/fixnum.rb +0 -2
  62. data/lib/mutant/mutator/node/literal/float.rb +0 -2
  63. data/lib/mutant/mutator/node/literal/hash.rb +0 -2
  64. data/lib/mutant/mutator/node/literal/nil.rb +0 -2
  65. data/lib/mutant/mutator/node/literal/range.rb +0 -2
  66. data/lib/mutant/mutator/node/literal/regex.rb +2 -4
  67. data/lib/mutant/mutator/node/literal/string.rb +0 -2
  68. data/lib/mutant/mutator/node/literal/symbol.rb +0 -2
  69. data/lib/mutant/mutator/node/masgn.rb +0 -2
  70. data/lib/mutant/mutator/node/mlhs.rb +0 -2
  71. data/lib/mutant/mutator/node/named_value/access.rb +0 -2
  72. data/lib/mutant/mutator/node/named_value/constant_assignment.rb +0 -2
  73. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +0 -2
  74. data/lib/mutant/mutator/node/next.rb +0 -3
  75. data/lib/mutant/mutator/node/noop.rb +0 -2
  76. data/lib/mutant/mutator/node/nthref.rb +0 -2
  77. data/lib/mutant/mutator/node/op_asgn.rb +2 -4
  78. data/lib/mutant/mutator/node/or_asgn.rb +4 -7
  79. data/lib/mutant/mutator/node/resbody.rb +1 -1
  80. data/lib/mutant/mutator/node/rescue.rb +0 -2
  81. data/lib/mutant/mutator/node/restarg.rb +0 -2
  82. data/lib/mutant/mutator/node/return.rb +3 -6
  83. data/lib/mutant/mutator/node/send.rb +14 -8
  84. data/lib/mutant/mutator/node/send/attribute_assignment.rb +0 -2
  85. data/lib/mutant/mutator/node/send/binary.rb +1 -3
  86. data/lib/mutant/mutator/node/splat.rb +0 -2
  87. data/lib/mutant/mutator/node/super.rb +0 -2
  88. data/lib/mutant/mutator/node/when.rb +0 -2
  89. data/lib/mutant/mutator/node/yield.rb +0 -2
  90. data/lib/mutant/mutator/node/zsuper.rb +0 -2
  91. data/lib/mutant/mutator/registry.rb +0 -2
  92. data/lib/mutant/mutator/util.rb +0 -2
  93. data/lib/mutant/mutator/util/array.rb +0 -2
  94. data/lib/mutant/mutator/util/symbol.rb +0 -2
  95. data/lib/mutant/node_helpers.rb +11 -2
  96. data/lib/mutant/reporter.rb +0 -2
  97. data/lib/mutant/reporter/cli.rb +0 -2
  98. data/lib/mutant/reporter/cli/printer.rb +0 -2
  99. data/lib/mutant/reporter/cli/progress.rb +0 -2
  100. data/lib/mutant/reporter/cli/report.rb +0 -2
  101. data/lib/mutant/reporter/cli/report/config.rb +0 -2
  102. data/lib/mutant/reporter/cli/report/mutation.rb +5 -5
  103. data/lib/mutant/reporter/cli/report/subject.rb +0 -2
  104. data/lib/mutant/reporter/null.rb +0 -2
  105. data/lib/mutant/runner.rb +0 -2
  106. data/lib/mutant/runner/config.rb +0 -2
  107. data/lib/mutant/runner/mutation.rb +0 -2
  108. data/lib/mutant/runner/subject.rb +0 -2
  109. data/lib/mutant/strategy.rb +0 -2
  110. data/lib/mutant/subject.rb +0 -2
  111. data/lib/mutant/subject/method.rb +0 -2
  112. data/lib/mutant/subject/method/instance.rb +1 -3
  113. data/lib/mutant/subject/method/singleton.rb +0 -2
  114. data/lib/mutant/version.rb +1 -3
  115. data/lib/mutant/zombifier.rb +0 -2
  116. data/meta/begin.rb +10 -0
  117. data/meta/case.rb +0 -2
  118. data/meta/def.rb +19 -0
  119. data/mutant-rspec.gemspec +1 -1
  120. data/mutant.gemspec +1 -1
  121. data/spec/integration/mutant/corpus_spec.rb +81 -22
  122. data/spec/integration/mutant/null_spec.rb +1 -3
  123. data/spec/integration/mutant/rspec_spec.rb +6 -8
  124. data/spec/integration/mutant/test_mutator_handles_types_spec.rb +0 -2
  125. data/spec/integration/mutant/zombie_spec.rb +0 -2
  126. data/spec/integrations.yml +12 -1
  127. data/spec/shared/method_matcher_behavior.rb +0 -2
  128. data/spec/spec_helper.rb +0 -2
  129. data/spec/support/compress_helper.rb +0 -2
  130. data/spec/support/ice_nine_config.rb +0 -2
  131. data/spec/support/rspec.rb +0 -2
  132. data/spec/support/test_app.rb +0 -2
  133. data/spec/unit/mutant/cli_new_spec.rb +0 -2
  134. data/spec/unit/mutant/cli_run_spec.rb +0 -2
  135. data/spec/unit/mutant/context/root_spec.rb +0 -2
  136. data/spec/unit/mutant/context/scope/root_spec.rb +0 -2
  137. data/spec/unit/mutant/context/scope/unqualified_name_spec.rb +0 -2
  138. data/spec/unit/mutant/diff_spec.rb +0 -2
  139. data/spec/unit/mutant/expression/method_spec.rb +2 -4
  140. data/spec/unit/mutant/expression/namespace/flat_spec.rb +1 -3
  141. data/spec/unit/mutant/expression/namespace/recursive_spec.rb +10 -6
  142. data/spec/unit/mutant/isolation_spec.rb +61 -0
  143. data/spec/unit/mutant/loader/eval_spec.rb +0 -2
  144. data/spec/unit/mutant/matcher/chain_spec.rb +4 -15
  145. data/spec/unit/mutant/matcher/method/instance_spec.rb +0 -2
  146. data/spec/unit/mutant/matcher/method/singleton_spec.rb +0 -2
  147. data/spec/unit/mutant/matcher/methods/instance_spec.rb +0 -2
  148. data/spec/unit/mutant/matcher/methods/singleton_spec.rb +0 -2
  149. data/spec/unit/mutant/matcher/namespace_spec.rb +33 -31
  150. data/spec/unit/mutant/mutation_spec.rb +0 -2
  151. data/spec/unit/mutant/runner/config_spec.rb +0 -2
  152. data/spec/unit/mutant/runner/mutation_spec.rb +0 -2
  153. data/spec/unit/mutant/runner/subject_spec.rb +0 -2
  154. data/spec/unit/mutant/strategy_spec.rb +0 -2
  155. data/spec/unit/mutant/subject/context_spec.rb +0 -2
  156. data/spec/unit/mutant/subject/mutations_spec.rb +0 -2
  157. data/spec/unit/mutant/subject/node_spec.rb +0 -2
  158. data/spec/unit/mutant/subject_spec.rb +0 -2
  159. data/spec/unit/mutant/warning_filter_spec.rb +6 -0
  160. data/spec/unit/mutant_spec.rb +0 -55
  161. data/test_app/Gemfile.devtools +15 -11
  162. data/test_app/Gemfile.rspec3 +2 -2
  163. metadata +7 -7
  164. data/lib/mutant/constants.rb +0 -45
  165. data/spec/unit/mutant/mutator_spec.rb +0 -29
@@ -0,0 +1,65 @@
1
+ module Mutant
2
+ # Module providing isolationg
3
+ module Isolation
4
+ Error = Class.new(RuntimeError)
5
+
6
+ # Call block in isolation
7
+ #
8
+ # This isolation implements the fork strategy.
9
+ # Future strategies will probably use a process pool that can
10
+ # handle multiple mutation kills, in-isolation at once.
11
+ #
12
+ # @return [Object]
13
+ #
14
+ # @raise [Error]
15
+ #
16
+ # @api private
17
+ #
18
+ def self.call(&block)
19
+ reader, writer = IO.pipe.each(&:binmode)
20
+
21
+ pid = fork
22
+
23
+ if pid.nil?
24
+ begin
25
+ reader.close
26
+ writer.write(Marshal.dump(block.call))
27
+ Kernel.exit!(0)
28
+ ensure
29
+ Kernel.exit!(1)
30
+ end
31
+ end
32
+
33
+ writer.close
34
+
35
+ read_result(reader, pid)
36
+ end
37
+
38
+ # Read result from child process
39
+ #
40
+ # @param [IO] reader
41
+ # @param [Fixnum] pid
42
+ #
43
+ # @return [Object]
44
+ #
45
+ # @raise [Error]
46
+ #
47
+ def self.read_result(reader, pid)
48
+ begin
49
+ data = Marshal.load(reader.read)
50
+ rescue ArgumentError, TypeError
51
+ raise Error, 'Childprocess wrote un-unmarshallable data'
52
+ end
53
+
54
+ status = Process.waitpid2(pid).last
55
+
56
+ unless status.exitstatus.zero?
57
+ raise Error, "Childprocess exited with nonzero exit status: #{status.exitstatus}"
58
+ end
59
+
60
+ data
61
+ end
62
+ private_class_method :read_result
63
+
64
+ end # Isolator
65
+ end # Mutant
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  # Mutation killer
5
3
  class Killer
@@ -31,7 +29,7 @@ module Mutant
31
29
  # @api private
32
30
  #
33
31
  def run
34
- test_report = Mutant.isolate do
32
+ test_report = Isolation.call do
35
33
  mutation.insert
36
34
  test.run
37
35
  end
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  # Base class for code loaders
5
3
  class Loader
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  # Abstract matcher to find subjects to mutate
5
3
  class Matcher
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  class Matcher
5
3
  # A chain of matchers
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  class Matcher
5
3
  # Matcher filter
@@ -1,10 +1,9 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  class Matcher
5
3
  # Matcher for subjects that are a specific method
6
4
  class Method < self
7
5
  include Adamantium::Flat, Concord::Public.new(:cache, :scope, :method)
6
+ include Equalizer.new(:identification)
8
7
 
9
8
  # Methods within rbx kernel directory are precompiled and their source
10
9
  # cannot be accessed via reading source location
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  class Matcher
5
3
  class Method
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  class Matcher
5
3
  class Method
@@ -19,7 +17,7 @@ module Mutant
19
17
  #
20
18
  def self.build(cache, scope, method)
21
19
  name = method.name
22
- if scope.ancestors.include?(::Memoizable) and scope.memoized?(name)
20
+ if scope.ancestors.include?(::Memoizable) && scope.memoized?(name)
23
21
  return Memoized.new(cache, scope, method)
24
22
  end
25
23
  super
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  class Matcher
5
3
  class Method
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  class Matcher
5
3
  # Abstract base class for matcher that returns method subjects from scope
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  class Matcher
5
3
 
@@ -36,7 +34,7 @@ module Mutant
36
34
  # @api private
37
35
  #
38
36
  def pattern
39
- /\A#{Regexp.escape(namespace)}(?:::)?/
37
+ /\A#{Regexp.escape(namespace)}(?:\z|::)/
40
38
  end
41
39
  memoize :pattern
42
40
 
@@ -95,9 +93,7 @@ Fix your lib to support normal ruby semantics!
95
93
  MESSAGE
96
94
  return
97
95
  end
98
- if pattern =~ name
99
- yield scope
100
- end
96
+ yield scope if pattern =~ name
101
97
  end
102
98
 
103
99
  end # Namespace
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  class Matcher
5
3
  # A null matcher, that does not match any subjects
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  class Matcher
5
3
  # Matcher for specific namespace
@@ -15,20 +15,33 @@ module Mutant
15
15
  Verification.new(self, generated)
16
16
  end
17
17
 
18
+ # Return source
19
+ #
20
+ # @return [String]
21
+ #
22
+ # @api private
23
+ #
24
+ def source
25
+ Unparser.unparse(node)
26
+ end
27
+ memoize :source
28
+
18
29
  # Return generated mutations
19
30
  #
20
- # @return [Emumerable<Parser::AST::Node>]
31
+ # @return [Emumerable<Mutant::Mutation>]
21
32
  #
22
33
  # @api private
23
34
  #
24
35
  def generated
25
- Mutant::Mutator.each(node).to_a
36
+ Mutant::Mutator.each(node).map do |node|
37
+ Mutant::Mutation::Evil.new(self, node)
38
+ end
26
39
  end
27
40
  memoize :generated
28
41
 
29
42
  # Example verification
30
43
  class Verification
31
- include Adamantium::Flat, Concord.new(:example, :generated)
44
+ include Adamantium::Flat, Concord.new(:example, :mutations)
32
45
 
33
46
  # Test if mutation was verified successfully
34
47
  #
@@ -37,7 +50,7 @@ module Mutant
37
50
  # @api private
38
51
  #
39
52
  def success?
40
- unparser.success? && missing.empty? && unexpected.empty?
53
+ unparser.success? && missing.empty? && unexpected.empty? && no_diffs.empty?
41
54
  end
42
55
 
43
56
  # Return error report
@@ -62,10 +75,21 @@ module Mutant
62
75
  # @api private
63
76
  #
64
77
  def unexpected
65
- generated - example.mutations
78
+ mutations.map(&:node) - example.mutations
66
79
  end
67
80
  memoize :unexpected
68
81
 
82
+ # Return mutations with no diff to original
83
+ #
84
+ # @return [Enumerable<Mutation>]
85
+ #
86
+ # @api private
87
+ #
88
+ def no_diffs
89
+ mutations.select { |mutation| mutation.source.eql?(example.source) }
90
+ end
91
+ memoize :no_diffs
92
+
69
93
  # Return mutation report
70
94
  #
71
95
  # @return [String]
@@ -78,9 +102,10 @@ module Mutant
78
102
  'Original-AST:',
79
103
  original_node.inspect,
80
104
  'Original-Source:',
81
- Unparser.unparse(original_node),
105
+ example.source,
82
106
  *missing_report,
83
- *unexpected_report
107
+ *unexpected_report,
108
+ *no_diff_report
84
109
  ].join("\n======\n")
85
110
  end
86
111
 
@@ -91,9 +116,25 @@ module Mutant
91
116
  # @api private
92
117
  #
93
118
  def missing_report
94
- if missing.any?
95
- ['Missing mutations:', missing.map(&method(:format_mutation)).join("\n-----\n")]
96
- end
119
+ [
120
+ 'Missing mutations:',
121
+ missing.map(&method(:format_mutation)).join("\n-----\n")
122
+ ] if missing.any?
123
+ end
124
+
125
+ # Return no diff report
126
+ #
127
+ # @return [Array, nil]
128
+ #
129
+ # @api private
130
+ #
131
+ def no_diff_report
132
+ [
133
+ 'No source diffs to original:',
134
+ no_diffs.map do |mutation|
135
+ "#{mutation.node.inspect}\n#{mutation.source}"
136
+ end
137
+ ] if no_diffs.any?
97
138
  end
98
139
 
99
140
  # Return unexpected report
@@ -103,12 +144,10 @@ module Mutant
103
144
  # @api private
104
145
  #
105
146
  def unexpected_report
106
- if unexpected.any?
107
- [
108
- 'Unexpected mutations:',
109
- unexpected.map(&method(:format_mutation)).join("\n-----\n")
110
- ]
111
- end
147
+ [
148
+ 'Unexpected mutations:',
149
+ unexpected.map(&method(:format_mutation)).join("\n-----\n")
150
+ ] if unexpected.any?
112
151
  end
113
152
 
114
153
  # Format mutation
@@ -124,14 +163,14 @@ module Mutant
124
163
  ].join("\n")
125
164
  end
126
165
 
127
- # Return missing mutationso
166
+ # Return missing mutations
128
167
  #
129
168
  # @return [Array<Parser::AST::Node>]
130
169
  #
131
170
  # @api private
132
171
  #
133
172
  def missing
134
- example.mutations - generated
173
+ example.mutations - mutations.map(&:node)
135
174
  end
136
175
  memoize :missing
137
176
 
@@ -81,8 +81,8 @@ module Mutant
81
81
  # @api private
82
82
  #
83
83
  def singleton_mutations
84
- mutation 'nil'
85
- mutation 'self'
84
+ mutation('nil')
85
+ mutation('self')
86
86
  end
87
87
 
88
88
  # Helper method to coerce input to node
@@ -97,17 +97,14 @@ module Mutant
97
97
  # @api private
98
98
  #
99
99
  def node(input)
100
- node =
101
- case input
102
- when String
103
- Parser::CurrentRuby.parse(input)
104
- when Parser::AST::Node
105
- input
106
- else
107
- raise "Cannot coerce to node: #{source.inspect}"
108
- end
109
-
110
- Unparser::Preprocessor.run(node)
100
+ case input
101
+ when String
102
+ Unparser::Preprocessor.run(Parser::CurrentRuby.parse(input))
103
+ when Parser::AST::Node
104
+ input
105
+ else
106
+ raise "Cannot coerce to node: #{source.inspect}"
107
+ end
111
108
  end
112
109
 
113
110
  end # DSL
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  # Represent a mutated node with its subject
5
3
  class Mutation
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  class Mutation
5
3
  # Evul mutation
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  class Mutation
5
3
  # Neutral mutation
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
-
3
1
  module Mutant
4
2
  # Generator for mutations
5
3
  class Mutator
@@ -7,7 +5,10 @@ module Mutant
7
5
 
8
6
  # Run mutator on input
9
7
  #
10
- # @param [Parser::AST::Node] node
8
+ # @param [Object] input
9
+ # the input to mutate
10
+ #
11
+ # @param [Mutator] parent
11
12
  #
12
13
  # @return [self]
13
14
  #
@@ -119,34 +120,6 @@ module Mutant
119
120
  emit!(object)
120
121
  end
121
122
 
122
- # Maximum amount of tries to generate a new object
123
- MAX_TRIES = 3
124
-
125
- # Call block until it generates a mutation
126
- #
127
- # @yield
128
- # Execute block until object is generated where new?(object) returns true
129
- #
130
- # @return [self]
131
- #
132
- # @raise [RuntimeError]
133
- # raises RuntimeError when no new node can be generated after MAX_TRIES.
134
- #
135
- # @api private
136
- #
137
- def emit_new
138
- MAX_TRIES.times do
139
- object = yield
140
-
141
- if new?(object)
142
- emit!(object)
143
- return
144
- end
145
- end
146
-
147
- raise "New AST could not be generated after #{MAX_TRIES} attempts"
148
- end
149
-
150
123
  # Call block with node
151
124
  #
152
125
  # @param [Parser::AST::Node] node