mutant 0.3.0.beta22 → 0.3.0.rc1

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -5
  3. data/Gemfile.devtools +1 -1
  4. data/README.md +22 -50
  5. data/config/flay.yml +1 -1
  6. data/config/flog.yml +1 -1
  7. data/config/reek.yml +9 -2
  8. data/lib/mutant.rb +3 -4
  9. data/lib/mutant/cli.rb +41 -35
  10. data/lib/mutant/cli/classifier.rb +1 -1
  11. data/lib/mutant/config.rb +2 -1
  12. data/lib/mutant/context/scope.rb +14 -1
  13. data/lib/mutant/killer.rb +10 -0
  14. data/lib/mutant/killer/rspec.rb +52 -18
  15. data/lib/mutant/matcher/method.rb +4 -4
  16. data/lib/mutant/mutator/node/generic.rb +1 -1
  17. data/lib/mutant/mutator/node/literal/float.rb +2 -0
  18. data/lib/mutant/mutator/node/nthref.rb +29 -0
  19. data/lib/mutant/mutator/node/send.rb +17 -0
  20. data/lib/mutant/reporter/cli/printer/killer.rb +9 -3
  21. data/lib/mutant/strategy.rb +9 -9
  22. data/lib/mutant/strategy/rspec.rb +51 -41
  23. data/lib/mutant/subject.rb +16 -6
  24. data/lib/mutant/subject/method.rb +10 -10
  25. data/lib/mutant/version.rb +7 -0
  26. data/lib/mutant/zombifier.rb +2 -3
  27. data/mutant.gemspec +6 -4
  28. data/spec/integration/mutant/rspec_spec.rb +26 -0
  29. data/spec/unit/mutant/cli/class_methods/new_spec.rb +33 -8
  30. data/spec/unit/mutant/killer/rspec/class_methods/new_spec.rb +4 -0
  31. data/spec/unit/mutant/mutator/node/nthref/mutation_spec.rb +19 -0
  32. data/spec/unit/mutant/mutator/node/send/mutation_spec.rb +29 -0
  33. metadata +14 -20
  34. data/lib/mutant/strategy/method_expansion.rb +0 -53
  35. data/lib/mutant/strategy/rspec/dm2.rb +0 -24
  36. data/lib/mutant/strategy/rspec/dm2/lookup.rb +0 -63
  37. data/lib/mutant/strategy/rspec/dm2/lookup/method.rb +0 -145
  38. data/spec/integration/mutant/rspec_killer_spec.rb +0 -29
  39. data/spec/unit/mutant/strategy/method_expansion/class_methods/run_spec.rb +0 -51
  40. data/spec/unit/mutant/strategy/rspec/dm2/lookup/method/instance/spec_files_spec.rb +0 -73
  41. data/spec/unit/mutant/strategy/rspec/dm2/lookup/method/singleton/spec_files_spec.rb +0 -62
@@ -54,16 +54,16 @@ module Mutant
54
54
  #
55
55
  def skip?
56
56
  location = source_location
57
- if location.nil? or BLACKLIST.match(location.first)
57
+ if location.nil? || BLACKLIST.match(location.first)
58
58
  message = sprintf(
59
59
  '%s does not have valid source location unable to emit matcher',
60
60
  method.inspect
61
61
  )
62
62
  $stderr.puts(message)
63
- return true
63
+ true
64
+ else
65
+ false
64
66
  end
65
-
66
- false
67
67
  end
68
68
 
69
69
  # Return method name
@@ -15,7 +15,7 @@ module Mutant
15
15
  :blockarg, :op_asgn, :and_asgn,
16
16
  :regopt, :restarg, :resbody, :retry, :arg_expr,
17
17
  :kwrestarg, :kwoptarg, :kwarg, :undef, :module, :empty,
18
- :alias, :for, :xstr, :back_ref, :nth_ref, :class,
18
+ :alias, :for, :xstr, :back_ref, :class,
19
19
  :sclass, :match_with_lvasgn, :match_current_line, :or_asgn, :kwbegin
20
20
  )
21
21
 
@@ -15,6 +15,8 @@ module Mutant
15
15
  #
16
16
  # @return [undefined]
17
17
  #
18
+ # @api private
19
+ #
18
20
  def dispatch
19
21
  emit_nil
20
22
  emit_values(values)
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ module Mutant
4
+ class Mutator
5
+ class Node
6
+ # Mutator for nth-ref nodes
7
+ class NthRef < self
8
+
9
+ handle :nth_ref
10
+
11
+ children :number
12
+
13
+ private
14
+
15
+ # Perform dispatch
16
+ #
17
+ # @return [undefined]
18
+ #
19
+ # @api private
20
+ #
21
+ def dispatch
22
+ emit_number(number - 1)
23
+ emit_number(number + 1)
24
+ end
25
+
26
+ end # NthRef
27
+ end # Node
28
+ end # Mutator
29
+ end # Mutant
@@ -11,6 +11,11 @@ module Mutant
11
11
 
12
12
  children :receiver, :selector
13
13
 
14
+ SELECTOR_REPLACEMENTS = {
15
+ :send => :public_send,
16
+ :gsub => :sub
17
+ }.freeze
18
+
14
19
  INDEX_REFERENCE = :[]
15
20
  INDEX_ASSIGN = :[]=
16
21
  ASSIGN_SUFFIX = :'='
@@ -98,11 +103,23 @@ module Mutant
98
103
  #
99
104
  def normal_dispatch
100
105
  emit_naked_receiver
106
+ emit_selector_mutations
101
107
  mutate_receiver
102
108
  emit_argument_propagation
103
109
  mutate_arguments
104
110
  end
105
111
 
112
+ # Emit selector mutations
113
+ #
114
+ # @return [undefined]
115
+ #
116
+ # @api private
117
+ #
118
+ def emit_selector_mutations
119
+ replacement = SELECTOR_REPLACEMENTS.fetch(selector) { return }
120
+ emit_selector(replacement)
121
+ end
122
+
106
123
  # Emit naked receiver mutation
107
124
  #
108
125
  # @return [undefined]
@@ -10,6 +10,9 @@ module Mutant
10
10
 
11
11
  handle(Mutant::Killer::Forked)
12
12
 
13
+ SUCCESS = '.'.freeze
14
+ FAILURE = 'F'.freeze
15
+
13
16
  # Run printer
14
17
  #
15
18
  # @return [undefined]
@@ -18,12 +21,14 @@ module Mutant
18
21
  #
19
22
  def run
20
23
  if success?
21
- char('.', Color::GREEN)
22
- return
24
+ char(SUCCESS, Color::GREEN)
25
+ else
26
+ char(FAILURE, Color::RED)
23
27
  end
24
- char('F', Color::RED)
25
28
  end
26
29
 
30
+ private
31
+
27
32
  # Write colorized char
28
33
  #
29
34
  # @param [String] char
@@ -37,6 +42,7 @@ module Mutant
37
42
  output.write(colorize(color, char))
38
43
  output.flush
39
44
  end
45
+
40
46
  end # Killer
41
47
  end # Printer
42
48
  end # CLI
@@ -6,24 +6,22 @@ module Mutant
6
6
  class Strategy
7
7
  include AbstractType, Adamantium::Flat
8
8
 
9
- # Perform setup
9
+ # Perform strategy setup
10
10
  #
11
11
  # @return [self]
12
12
  #
13
13
  # @api private
14
14
  #
15
- def self.setup
16
- self
15
+ def setup
17
16
  end
18
17
 
19
- # Perform teardown
18
+ # Perform strategy teardown
20
19
  #
21
20
  # @return [self]
22
21
  #
23
22
  # @api private
24
23
  #
25
- def self.teardown
26
- self
24
+ def teardown
27
25
  end
28
26
 
29
27
  # Kill mutation
@@ -34,18 +32,20 @@ module Mutant
34
32
  #
35
33
  # @api private
36
34
  #
37
- def self.kill(mutation)
35
+ def kill(mutation)
38
36
  killer.new(self, mutation)
39
37
  end
40
38
 
39
+ private
40
+
41
41
  # Return killer
42
42
  #
43
43
  # @return [Class:Killer]
44
44
  #
45
45
  # @api private
46
46
  #
47
- def self.killer
48
- self::KILLER
47
+ def killer
48
+ self.class::KILLER
49
49
  end
50
50
 
51
51
  end # Strategy
@@ -2,9 +2,9 @@
2
2
 
3
3
  module Mutant
4
4
  class Strategy
5
-
6
- # Rspec strategy base class
5
+ # Rspec killer strategy
7
6
  class Rspec < self
7
+ include Equalizer.new
8
8
 
9
9
  KILLER = Killer::Forking.new(Killer::Rspec)
10
10
 
@@ -14,52 +14,62 @@ module Mutant
14
14
  #
15
15
  # @api private
16
16
  #
17
- def self.setup
18
- require('./spec/spec_helper.rb')
17
+ def setup
18
+ output = StringIO.new
19
+ configuration.error_stream = output
20
+ configuration.output_stream = output
21
+ options.configure(configuration)
22
+ configuration.load_spec_files
19
23
  self
20
24
  end
25
+ memoize :setup
21
26
 
22
- # Run all unit specs per mutation
23
- class Unit < self
24
-
25
- # Return file name pattern for mutation
26
- #
27
- # @return [Enumerable<String>]
28
- #
29
- # @api private
30
- #
31
- def self.spec_files(_mutation)
32
- Dir['spec/unit/**/*_spec.rb']
33
- end
34
- end # Unit
27
+ # Return configuration
28
+ #
29
+ # @return [RSpec::Core::Configuration]
30
+ #
31
+ # @api private
32
+ #
33
+ def configuration
34
+ RSpec::Core::Configuration.new
35
+ end
36
+ memoize :configuration, :freezer => :noop
35
37
 
36
- # Run all integration specs per mutation
37
- class Integration < self
38
+ # Return example groups
39
+ #
40
+ # @return [Enumerable<RSpec::Core::ExampleGroup>]
41
+ #
42
+ # @api private
43
+ #
44
+ def example_groups
45
+ world.example_groups
46
+ end
38
47
 
39
- # Return file name pattern for mutation
40
- #
41
- # @return [Mutation]
42
- #
43
- # @api private
44
- #
45
- def self.spec_files(_mutation)
46
- Dir['spec/integration/**/*_spec.rb']
47
- end
48
- end # Integration
48
+ private
49
49
 
50
- # Run all specs per mutation
51
- class Full < self
50
+ # Return world
51
+ #
52
+ # @return [RSpec::Core::World]
53
+ #
54
+ # @api private
55
+ #
56
+ def world
57
+ RSpec.world
58
+ end
59
+ memoize :world, :freezer => :noop
52
60
 
53
- # Return spec files
54
- #
55
- # @return [Enumerable<String>]
56
- #
57
- # @api private
58
- #
59
- def self.spec_files(_mutation)
60
- Dir['spec/**/*_spec.rb']
61
- end
62
- end # Full
61
+ # Return options
62
+ #
63
+ # @return [RSpec::Core::ConfigurationOptions]
64
+ #
65
+ # @api private
66
+ #
67
+ def options
68
+ options = RSpec::Core::ConfigurationOptions.new(%w(--fail-fast spec))
69
+ options.parse_options
70
+ options
71
+ end
72
+ memoize :options, :freezer => :noop
63
73
 
64
74
  end # Rspec
65
75
  end # Strategy
@@ -46,7 +46,7 @@ module Mutant
46
46
  # @api private
47
47
  #
48
48
  def identification
49
- "#{subtype}:#{source_path}:#{source_line}"
49
+ "#{match_expression}:#{source_path}:#{source_line}"
50
50
  end
51
51
  memoize :identification
52
52
 
@@ -84,16 +84,26 @@ module Mutant
84
84
  end
85
85
  memoize :original_root
86
86
 
87
- private
88
-
89
- # Return subtype identifier
87
+ # Return match expression
90
88
  #
91
89
  # @return [String]
92
90
  #
93
91
  # @api private
94
92
  #
95
- abstract_method :subtype
96
- private :subtype
93
+ abstract_method :match_expression
94
+
95
+ # Return match prefixes
96
+ #
97
+ # @return [Enumerable<String>]
98
+ #
99
+ # @api private
100
+ #
101
+ def match_prefixes
102
+ [match_expression].concat(context.match_prefixes)
103
+ end
104
+ memoize :match_prefixes
105
+
106
+ private
97
107
 
98
108
  # Return neutral mutation
99
109
  #
@@ -27,6 +27,16 @@ module Mutant
27
27
  node.children[self.class::NAME_INDEX]
28
28
  end
29
29
 
30
+ # Return match expression
31
+ #
32
+ # @return [String]
33
+ #
34
+ # @api private
35
+ #
36
+ def match_expression
37
+ "#{context.identification}#{self.class::SYMBOL}#{name}"
38
+ end
39
+
30
40
  private
31
41
 
32
42
  # Return mutations
@@ -54,16 +64,6 @@ module Mutant
54
64
  context.scope
55
65
  end
56
66
 
57
- # Return subtype identifier
58
- #
59
- # @return [String]
60
- #
61
- # @api private
62
- #
63
- def subtype
64
- "#{context.identification}#{self.class::SYMBOL}#{name}"
65
- end
66
-
67
67
  end # Method
68
68
  end # Subject
69
69
  end # Mutant
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ module Mutant
4
+ # The current mutant version
5
+ VERSION = '0.3.0.rc1'.freeze
6
+ end # Mutant
7
+
@@ -151,13 +151,12 @@ module Mutant
151
151
 
152
152
  $LOAD_PATH.each do |path|
153
153
  path = Pathname.new(path).join(file_name)
154
- if path.exist?
155
- $stderr.puts "Loading #{path}"
154
+ if path.file?
156
155
  return new(path)
157
156
  end
158
157
  end
159
158
 
160
- $stderr.puts "Cannot find #{file_name} in $LOAD_PATH"
159
+ $stderr.puts "Cannot find file #{file_name} in $LOAD_PATH"
161
160
  nil
162
161
  end
163
162
 
data/mutant.gemspec CHANGED
@@ -1,8 +1,10 @@
1
1
  # encoding: utf-8
2
+ #
3
+ require File.expand_path('../lib/mutant/version', __FILE__)
2
4
 
3
5
  Gem::Specification.new do |gem|
4
6
  gem.name = 'mutant'
5
- gem.version = '0.3.0.beta22'
7
+ gem.version = Mutant::VERSION.dup
6
8
  gem.authors = ['Markus Schirp']
7
9
  gem.email = ['mbj@schirp-dso.com']
8
10
  gem.description = 'Mutation testing for ruby'
@@ -16,15 +18,15 @@ Gem::Specification.new do |gem|
16
18
  gem.extra_rdoc_files = %w[TODO LICENSE]
17
19
  gem.executables = %w[mutant]
18
20
 
19
- gem.add_runtime_dependency('parser', '~> 2.0.0.pre3')
20
- gem.add_runtime_dependency('unparser', '~> 0.0.13')
21
+ gem.add_runtime_dependency('parser', '~> 2.0.0.pre6')
22
+ gem.add_runtime_dependency('unparser', '~> 0.0.14')
21
23
  gem.add_runtime_dependency('ice_nine', '~> 0.8.0')
22
24
  gem.add_runtime_dependency('descendants_tracker', '~> 0.0.1')
23
25
  gem.add_runtime_dependency('adamantium', '~> 0.0.10')
24
26
  gem.add_runtime_dependency('equalizer', '~> 0.0.5')
25
27
  gem.add_runtime_dependency('inflecto', '~> 0.0.2')
26
28
  gem.add_runtime_dependency('anima', '~> 0.0.6')
27
- gem.add_runtime_dependency('concord', '~> 0.1.1')
29
+ gem.add_runtime_dependency('concord', '~> 0.1.3')
28
30
  gem.add_runtime_dependency('rspec', '~> 2.14.1')
29
31
 
30
32
  gem.add_development_dependency('bundler', '~> 1.3', '>= 1.3.5')
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Mutant, 'rspec integration' do
6
+
7
+ around do |example|
8
+ Dir.chdir(TestApp.root) do
9
+ example.run
10
+ end
11
+ end
12
+
13
+ specify 'it allows to kill mutations' do
14
+ Kernel.system('bundle exec mutant --rspec ::TestApp::Literal#string').should be(true)
15
+ end
16
+
17
+ pending 'fails to kill mutations when they are not covered' do
18
+ cli = 'bundle exec mutant --rspec ::TestApp::Literal#uncovered_string'
19
+ Kernel.system(cli).should be(false)
20
+ end
21
+
22
+ pending 'fails when some mutations when are not covered' do
23
+ cli = 'bundle exec mutant --rspec ::TestApp::Literal'
24
+ Kernel.system(cli).should be(false)
25
+ end
26
+ end