mutant 0.3.0.beta4 → 0.3.0.beta5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/README.md +4 -4
  4. data/TODO +0 -9
  5. data/bin/mutant +2 -2
  6. data/bin/zombie +2 -2
  7. data/config/flay.yml +1 -1
  8. data/config/reek.yml +1 -2
  9. data/lib/mutant.rb +3 -0
  10. data/lib/mutant/cache.rb +30 -0
  11. data/lib/mutant/cli.rb +4 -1
  12. data/lib/mutant/cli/classifier.rb +17 -34
  13. data/lib/mutant/cli/classifier/method.rb +2 -2
  14. data/lib/mutant/cli/classifier/namespace.rb +1 -1
  15. data/lib/mutant/config.rb +1 -1
  16. data/lib/mutant/constants.rb +2 -0
  17. data/lib/mutant/differ.rb +3 -3
  18. data/lib/mutant/loader.rb +1 -1
  19. data/lib/mutant/matcher.rb +3 -3
  20. data/lib/mutant/matcher/method.rb +6 -73
  21. data/lib/mutant/matcher/method/finder.rb +72 -0
  22. data/lib/mutant/matcher/method/singleton.rb +2 -2
  23. data/lib/mutant/matcher/methods.rb +2 -2
  24. data/lib/mutant/matcher/namespace.rb +2 -2
  25. data/lib/mutant/matcher/scope.rb +2 -2
  26. data/lib/mutant/mutation.rb +4 -1
  27. data/lib/mutant/mutator/node.rb +1 -1
  28. data/lib/mutant/zombifier.rb +255 -0
  29. data/mutant.gemspec +3 -3
  30. data/spec/integration/mutant/zombie_spec.rb +3 -3
  31. data/spec/shared/method_matcher_behavior.rb +3 -4
  32. data/spec/spec_helper.rb +4 -0
  33. data/spec/unit/mutant/cli/class_methods/new_spec.rb +6 -6
  34. data/spec/unit/mutant/cli/classifier/class_methods/build_spec.rb +9 -8
  35. data/spec/unit/mutant/context/scope/root_spec.rb +3 -3
  36. data/spec/unit/mutant/killer/rspec/class_methods/new_spec.rb +7 -5
  37. data/spec/unit/mutant/matcher/method/instance/each_spec.rb +8 -7
  38. data/spec/unit/mutant/matcher/method/singleton/each_spec.rb +5 -4
  39. data/spec/unit/mutant/matcher/methods/instance/each_spec.rb +5 -4
  40. data/spec/unit/mutant/matcher/methods/singleton/each_spec.rb +5 -4
  41. data/spec/unit/mutant/matcher/namespace/each_spec.rb +5 -3
  42. data/spec/unit/mutant/strategy/rspec/dm2/lookup/method/instance/spec_files_spec.rb +1 -1
  43. data/spec/unit/mutant/strategy/rspec/dm2/lookup/method/singleton/spec_files_spec.rb +2 -2
  44. data/spec/unit/mutant/subject/context_spec.rb +2 -2
  45. data/spec/unit/mutant/subject/each_spec.rb +1 -1
  46. data/spec/unit/mutant/subject/node_spec.rb +2 -2
  47. metadata +9 -8
  48. data/spec/support/zombie.rb +0 -174
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 266262896da3866b48b440ae556a6510a15b351a
4
- data.tar.gz: e646685cea23129d46ead4f476940c4b37f27d36
3
+ metadata.gz: f3ab83b3fe3623d77e0bc897128c0790b6ebcd13
4
+ data.tar.gz: aabf8cc62a8349f0891cbcca381c682d18055384
5
5
  SHA512:
6
- metadata.gz: 11e1b5542cc90aa0333a47908a1dee8fe04a6c2ef49df2da4c140a6a273af7e13c868fb81a9f1a1866133b4308a4fe6bc672619a58fec895d948d95485ec8abb
7
- data.tar.gz: 7982b06a5e1276a0f64f04bda3009bc986e0b59dcbe94f032251d6a0944d13115d70a35b198e540f458274d5a13fe968c02b7207ad0783212d36b8f59b888f2c
6
+ metadata.gz: e816a8fd75024969b7a286d4b0e10b122526a768c5746f3615ac4b341d6538794195be8d2dff53f97fab916632e26111edd9156050787642f4197f131c654a0c
7
+ data.tar.gz: 393035855ab0e2d9b81203e221ae238edf54e92af5b911a290b04ef086fa7c4b7ba4757621141eb442f184bb80686c9f9ecf6ddce1455d717753584a48b28b21
data/.travis.yml CHANGED
@@ -10,7 +10,7 @@ rvm:
10
10
  matrix:
11
11
  allow_failures:
12
12
  - rvm: jruby-19mode # No fork(2) support, workaround planned
13
- - rvm: ruby-head # Broken at this time
13
+ - rvm: ruby-head # Broken at this time, bundler issue on travis
14
14
  - rvm: rbx-19mode # Broken at this time, yard/yardstick issue
15
15
  notifications:
16
16
  irc:
data/README.md CHANGED
@@ -10,8 +10,8 @@ Mutant is a mutation testing tool for ruby. It aims to be better than existing m
10
10
  The idea is that if code can be changed and your tests do not notice, either that code isn't being covered
11
11
  or it does not have a speced side effect.
12
12
 
13
- Mutant supports MRI and RBX gte 19-mode (1.9.3!), while support for jruby is planned. It should also work under any ruby
14
- engine that supports POSIX-fork(2) semantics.
13
+ Mutant supports MRI and RBX 1.9 and 2.0, while support for jruby is planned. It should also work under
14
+ any ruby engine that supports POSIX-fork(2) semantics.
15
15
 
16
16
  Only rspec2 is supported currently. This is subject to change.
17
17
 
@@ -27,8 +27,8 @@ The following projects adopted mutant, and aim 100% mutation coverage:
27
27
 
28
28
  * [axiom](https://github.com/dkubb/axiom)
29
29
  * [axiom-types](https://github.com/dkubb/axiom-types)
30
- * [rom-mapper](https://github.com/rom/rom-mapper)
31
- * [rom-session](https://github.com/rom/rom-session)
30
+ * [rom-mapper](https://github.com/rom-rb/rom-mapper)
31
+ * [rom-session](https://github.com/rom-rb/rom-session)
32
32
  * [event_bus](https://github.com/kevinrutherford/event_bus)
33
33
  * [virtus](https://github.com/solnic/virtus)
34
34
  * [quacky](https://github.com/benmoss/quacky)
data/TODO CHANGED
@@ -1,12 +1,7 @@
1
1
  Code:
2
2
  * Test mutant with dynamically created zombie.
3
- * Fix ugly code within default parameter mutations
4
3
  * Break up lib/mutant/mutator/node/send.rb in class specific files
5
4
  * Log all warnings through reporter, so remove random $stderr.puts calls
6
- * Use rational to represent percent values
7
-
8
- AST:
9
- * Use whitequarcks parser and the to be writte unparser. This fixes all RBX AST issues.
10
5
 
11
6
  Mutations:
12
7
  * Add binary operator specific mutations (YAY, finally reached this point)
@@ -34,9 +29,6 @@ Killers:
34
29
  * Add a general master <=> killer IPC interface. So different strategies of isolation
35
30
  (fork, vs jruby runtime creation) will work without big impact.
36
31
 
37
- Kill:
38
- * Introduce mutant Kill that acts as a DTO between killer and runner. (Simplifies reporting)
39
-
40
32
  Strategy:
41
33
  * Aggregate warnings on missing spec files
42
34
  * Provide "expicit files to kill with" strategy
@@ -46,5 +38,4 @@ Matcher:
46
38
  * Allow matches on define_method with literal name argument?
47
39
 
48
40
  jruby-support:
49
- * Use whitequarck parser
50
41
  * Create a runtime per mutation to kill mutations in isolation
data/bin/mutant CHANGED
@@ -9,8 +9,8 @@ require 'mutant'
9
9
  namespace =
10
10
  if File.basename($0) == 'zombie'
11
11
  $stderr.puts('Detected zombie environment...')
12
- require File.expand_path('../../spec/support/zombie.rb', __FILE__)
13
- Zombie.setup
12
+ Mutant::Zombifier.zombify
13
+ Zombie::Mutant
14
14
  else
15
15
  Mutant
16
16
  end
data/bin/zombie CHANGED
@@ -9,8 +9,8 @@ require 'mutant'
9
9
  namespace =
10
10
  if File.basename($0) == 'zombie'
11
11
  $stderr.puts('Detected zombie environment...')
12
- require File.expand_path('../../spec/support/zombie.rb', __FILE__)
13
- Zombie.setup
12
+ Mutant::Zombifier.zombify
13
+ Zombie::Mutant
14
14
  else
15
15
  Mutant
16
16
  end
data/config/flay.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 16
3
- total_score: 604
3
+ total_score: 616
data/config/reek.yml CHANGED
@@ -58,14 +58,13 @@ RepeatedConditional:
58
58
  exclude:
59
59
  - Mutant::Mutator
60
60
  - Mutant::Reporter::CLI
61
- - Mutant::Mutator::Node::Send
62
- - Mutant::Mutator::Node::If
63
61
  max_ifs: 1
64
62
  TooManyInstanceVariables:
65
63
  enabled: true
66
64
  exclude:
67
65
  - Mutant::CLI # 4 vars
68
66
  - Mutant::Killer # 4 vars
67
+ - Mutant::Mutator # 4 vars
69
68
  max_instance_variables: 3
70
69
  TooManyMethods:
71
70
  enabled: true
data/lib/mutant.rb CHANGED
@@ -22,6 +22,7 @@ require 'concord'
22
22
  module Mutant
23
23
  end
24
24
 
25
+ require 'mutant/cache'
25
26
  require 'mutant/node_helpers'
26
27
  require 'mutant/singleton_methods'
27
28
  require 'mutant/constants'
@@ -77,6 +78,7 @@ require 'mutant/subject/method'
77
78
  require 'mutant/matcher'
78
79
  require 'mutant/matcher/chain'
79
80
  require 'mutant/matcher/method'
81
+ require 'mutant/matcher/method/finder'
80
82
  require 'mutant/matcher/method/singleton'
81
83
  require 'mutant/matcher/method/instance'
82
84
  require 'mutant/matcher/methods'
@@ -112,3 +114,4 @@ require 'mutant/reporter/cli/printer/config'
112
114
  require 'mutant/reporter/cli/printer/subject'
113
115
  require 'mutant/reporter/cli/printer/killer'
114
116
  require 'mutant/reporter/cli/printer/mutation'
117
+ require 'mutant/zombifier'
@@ -0,0 +1,30 @@
1
+ module Mutant
2
+ # An AST cache
3
+ class Cache
4
+ # This is explicitly empty! Ask me if you are interested in reasons :D
5
+ include Equalizer.new
6
+
7
+ # Initialize object
8
+ #
9
+ # @return [undefined]
10
+ #
11
+ # @api private
12
+ #
13
+ def initialize
14
+ @cache = {}
15
+ end
16
+
17
+ # Return node for file
18
+ #
19
+ # @return [AST::Node]
20
+ #
21
+ # @api private
22
+ #
23
+ def parse(path)
24
+ @cache.fetch(path) do
25
+ @cache[path] = Parser::CurrentRuby.parse(File.read(path))
26
+ end
27
+ end
28
+
29
+ end # Cache
30
+ end # Mutant
data/lib/mutant/cli.rb CHANGED
@@ -41,6 +41,8 @@ module Mutant
41
41
  def initialize(arguments=[])
42
42
  @filters, @matchers = [], []
43
43
 
44
+ @cache = Mutant::Cache.new
45
+
44
46
  parse(arguments)
45
47
  strategy
46
48
  matcher
@@ -54,6 +56,7 @@ module Mutant
54
56
  #
55
57
  def config
56
58
  Config.new(
59
+ :cache => @cache,
57
60
  :debug => debug?,
58
61
  :matcher => matcher,
59
62
  :filter => filter,
@@ -212,7 +215,7 @@ module Mutant
212
215
  #
213
216
  def parse_matchers(patterns)
214
217
  patterns.each do |pattern|
215
- matcher = Classifier.build(pattern)
218
+ matcher = Classifier.build(@cache, pattern)
216
219
  @matchers << matcher if matcher
217
220
  end
218
221
  end
@@ -2,10 +2,13 @@ module Mutant
2
2
  class CLI
3
3
  # A classifier for input strings
4
4
  class Classifier < Matcher
5
- include AbstractType, Adamantium::Flat, Equalizer.new(:identification)
5
+ include AbstractType, Adamantium::Flat, Concord.new(:cache, :match)
6
+
7
+ include Equalizer.new(:identifier)
6
8
 
7
9
  SCOPE_NAME_PATTERN = /[A-Za-z][A-Za-z_0-9]*/.freeze
8
- METHOD_NAME_PATTERN = /[_A-Za-z][A-Za-z0-9_]*[!?=]?/.freeze
10
+ OPERATOR_PATTERN = Regexp.union(*OPERATOR_METHODS.map(&:to_s)).freeze
11
+ METHOD_NAME_PATTERN = /([_A-Za-z][A-Za-z0-9_]*[!?=]?|#{OPERATOR_PATTERN})/.freeze
9
12
  SCOPE_PATTERN = /(?:::)?#{SCOPE_NAME_PATTERN}(?:::#{SCOPE_NAME_PATTERN})*/.freeze
10
13
 
11
14
  SINGLETON_PATTERN = %r(\A(#{SCOPE_PATTERN})\z).freeze
@@ -32,15 +35,13 @@ module Mutant
32
35
  # @api private
33
36
  #
34
37
  def self.constant_lookup(location)
35
- location.gsub(%r(\A::), '').split('::').inject(::Object) do |parent, name|
38
+ location.gsub(%r(\A::), '').split('::').inject(Object) do |parent, name|
36
39
  parent.const_get(name)
37
40
  end
38
41
  end
39
42
 
40
43
  # Return matchers for input
41
44
  #
42
- # @param [String] input
43
- #
44
45
  # @return [Classifier]
45
46
  # if a classifier handles the input
46
47
  #
@@ -49,9 +50,9 @@ module Mutant
49
50
  #
50
51
  # @api private
51
52
  #
52
- def self.build(input)
53
+ def self.build(*arguments)
53
54
  classifiers = REGISTRY.map do |descendant|
54
- descendant.run(input)
55
+ descendant.run(*arguments)
55
56
  end.compact
56
57
 
57
58
  raise if classifiers.length > 1
@@ -61,6 +62,10 @@ module Mutant
61
62
 
62
63
  # Run classifier
63
64
  #
65
+ # @param [Cache] cache
66
+ #
67
+ # @param [String] input
68
+ #
64
69
  # @return [Classifier]
65
70
  # if input is handled by classifier
66
71
  #
@@ -69,11 +74,11 @@ module Mutant
69
74
  #
70
75
  # @api private
71
76
  #
72
- def self.run(input)
77
+ def self.run(cache, input)
73
78
  match = self::REGEXP.match(input)
74
79
  return unless match
75
80
 
76
- new(match)
81
+ new(cache, match)
77
82
  end
78
83
 
79
84
  # No protected_class_method in ruby :(
@@ -95,38 +100,16 @@ module Mutant
95
100
  self
96
101
  end
97
102
 
98
- # Return identification
103
+ # Return identifier
99
104
  #
100
105
  # @return [String]
101
106
  #
102
107
  # @api private
103
108
  #
104
- def identification
109
+ def identifier
105
110
  match.to_s
106
111
  end
107
- memoize :identification
108
-
109
- private
110
-
111
- # Initialize object
112
- #
113
- # @param [MatchData] match
114
- #
115
- # @return [undefined]
116
- #
117
- # @api private
118
- #
119
- def initialize(match)
120
- @match = match
121
- end
122
-
123
- # Return match
124
- #
125
- # @return [MatchData]
126
- #
127
- # @api private
128
- #
129
- attr_reader :match
112
+ memoize :identifier
130
113
 
131
114
  # Return matcher
132
115
  #
@@ -24,7 +24,7 @@ module Mutant
24
24
  # @api private
25
25
  #
26
26
  def matcher
27
- methods_matcher.matcher.new(scope, method)
27
+ methods_matcher.matcher.new(cache, scope, method)
28
28
  end
29
29
  memoize :matcher
30
30
 
@@ -90,7 +90,7 @@ module Mutant
90
90
  # @api private
91
91
  #
92
92
  def methods_matcher
93
- TABLE.fetch(scope_symbol).new(scope)
93
+ TABLE.fetch(scope_symbol).new(cache, scope)
94
94
  end
95
95
  memoize :methods_matcher
96
96
 
@@ -14,7 +14,7 @@ module Mutant
14
14
  # @api private
15
15
  #
16
16
  def matcher
17
- self.class::MATCHER.new(namespace)
17
+ self.class::MATCHER.new(cache, namespace)
18
18
  end
19
19
 
20
20
  # Return namespace
data/lib/mutant/config.rb CHANGED
@@ -2,7 +2,7 @@ module Mutant
2
2
  # The configuration of a mutator run
3
3
  class Config
4
4
  include Adamantium::Flat, Anima.new(
5
- :debug, :strategy, :matcher, :filter, :reporter
5
+ :cache, :debug, :strategy, :matcher, :filter, :reporter
6
6
  )
7
7
 
8
8
  # Enumerate subjects
@@ -44,6 +44,8 @@ module Mutant
44
44
 
45
45
  BINARY_METHOD_OPERATORS = (OPERATOR_EXPANSIONS.keys - (INDEX_OPERATORS + UNARY_METHOD_OPERATORS)).to_set.freeze
46
46
 
47
+ OPERATOR_METHODS = OPERATOR_EXPANSIONS.keys + INDEX_OPERATORS + UNARY_METHOD_OPERATORS
48
+
47
49
  # Hopefully all types parser does generate
48
50
  NODE_TYPES = [
49
51
  :lvasgn, :ivasgn, :cvasgn, :gvasgn,
data/lib/mutant/differ.rb CHANGED
@@ -3,6 +3,9 @@ module Mutant
3
3
  class Differ
4
4
  include Adamantium::Flat
5
5
 
6
+ FORMAT = :unified
7
+ CONTEXT_LINES = 3
8
+
6
9
  # Return source diff
7
10
  #
8
11
  # @return [String]
@@ -35,9 +38,6 @@ module Mutant
35
38
 
36
39
  private
37
40
 
38
- FORMAT = :unified
39
- CONTEXT_LINES = 3
40
-
41
41
  # Initialize differ object
42
42
  #
43
43
  # @param [String] old
data/lib/mutant/loader.rb CHANGED
@@ -40,7 +40,7 @@ module Mutant
40
40
  # @api private
41
41
  #
42
42
  def run
43
- eval(source, TOPLEVEL_BINDING, @subject.source_path, @subject.source_line)
43
+ eval(source, TOPLEVEL_BINDING, @subject.source_path.to_s, @subject.source_line)
44
44
  end
45
45
 
46
46
  # Return source
@@ -15,10 +15,10 @@ module Mutant
15
15
  #
16
16
  # @api private
17
17
  #
18
- def self.each(input, &block)
19
- return to_enum(__method__, input) unless block_given?
18
+ def self.each(cache, input, &block)
19
+ return to_enum(__method__, cache, input) unless block_given?
20
20
 
21
- new(input).each(&block)
21
+ new(cache, input).each(&block)
22
22
 
23
23
  self
24
24
  end
@@ -2,7 +2,7 @@ module Mutant
2
2
  class Matcher
3
3
  # Matcher for subjects that are a specific method
4
4
  class Method < self
5
- include Adamantium::Flat, Concord::Public.new(:scope, :method)
5
+ include Adamantium::Flat, Concord::Public.new(:cache, :scope, :method)
6
6
 
7
7
  # Methods within rbx kernel directory are precompiled and their source
8
8
  # cannot be accessed via reading source location
@@ -18,13 +18,12 @@ module Mutant
18
18
  #
19
19
  # @api private
20
20
  #
21
- def each(&block)
21
+ def each
22
22
  return to_enum unless block_given?
23
23
 
24
- return self if skip?
25
-
26
- util = subject
27
- yield util if util
24
+ unless skip?
25
+ yield subject if subject
26
+ end
28
27
 
29
28
  self
30
29
  end
@@ -78,7 +77,7 @@ module Mutant
78
77
  # @api private
79
78
  #
80
79
  def ast
81
- Parser::CurrentRuby.parse(File.read(source_path))
80
+ cache.parse(source_path)
82
81
  end
83
82
 
84
83
  # Return path to source
@@ -128,72 +127,6 @@ module Mutant
128
127
  end
129
128
  memoize :subject
130
129
 
131
- # Visitor to find last match inside AST
132
- class Finder
133
-
134
- # Run finder
135
- #
136
- # @param [Parser::AST::Node]
137
- #
138
- # @return [Parser::AST::Node]
139
- # if found
140
- #
141
- # @return [nil]
142
- # otherwise
143
- #
144
- # @api private
145
- #
146
- #
147
- def self.run(root, &predicate)
148
- new(root, predicate).match
149
- end
150
-
151
- private_class_method :new
152
-
153
- # Return match
154
- #
155
- # @return [Parser::AST::Node]
156
- #
157
- # @api private
158
- #
159
- attr_reader :match
160
-
161
- private
162
-
163
- # Initialize object
164
- #
165
- # @param [Parer::AST::Node]
166
- #
167
- # @return [undefined]
168
- #
169
- # @api private
170
- #
171
- #
172
- def initialize(root, predicate)
173
- @root, @predicate = root, predicate
174
- visit(root)
175
- end
176
-
177
- # Visit node
178
- #
179
- # @param [Parser::AST::Node] node
180
- #
181
- # @return [undefined]
182
- #
183
- # @api private
184
- #
185
- def visit(node)
186
- if @predicate.call(node)
187
- @match = node
188
- end
189
-
190
- node.children.each do |child|
191
- visit(child) if child.kind_of?(Parser::AST::Node)
192
- end
193
- end
194
-
195
- end # Finder
196
-
197
130
  # Return matched node
198
131
  #
199
132
  # @return [Parser::AST::Node]