mutant 0.3.0.beta4 → 0.3.0.beta5

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 (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]