mutant 0.5.23 → 0.5.24

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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +10 -0
  3. data/config/flay.yml +1 -1
  4. data/config/reek.yml +19 -19
  5. data/lib/mutant.rb +12 -39
  6. data/lib/mutant/ast.rb +5 -0
  7. data/lib/mutant/ast/meta.rb +131 -0
  8. data/lib/mutant/ast/named_children.rb +98 -0
  9. data/lib/mutant/ast/node_predicates.rb +19 -0
  10. data/lib/mutant/ast/nodes.rb +21 -0
  11. data/lib/mutant/ast/sexp.rb +34 -0
  12. data/lib/mutant/ast/types.rb +48 -0
  13. data/lib/mutant/cache.rb +3 -2
  14. data/lib/mutant/cli.rb +11 -151
  15. data/lib/mutant/config.rb +22 -2
  16. data/lib/mutant/context/scope.rb +11 -21
  17. data/lib/mutant/delegator.rb +2 -0
  18. data/lib/mutant/diff.rb +7 -3
  19. data/lib/mutant/env.rb +49 -0
  20. data/lib/mutant/expression.rb +36 -8
  21. data/lib/mutant/expression/methods.rb +62 -0
  22. data/lib/mutant/expression/namespace.rb +41 -28
  23. data/lib/mutant/{strategy.rb → integration.rb} +12 -31
  24. data/lib/mutant/isolation.rb +1 -1
  25. data/lib/mutant/matcher.rb +1 -21
  26. data/lib/mutant/matcher/builder.rb +142 -0
  27. data/lib/mutant/matcher/method.rb +3 -7
  28. data/lib/mutant/matcher/method/instance.rb +6 -5
  29. data/lib/mutant/matcher/method/singleton.rb +2 -7
  30. data/lib/mutant/matcher/methods.rb +11 -14
  31. data/lib/mutant/matcher/namespace.rb +31 -39
  32. data/lib/mutant/matcher/scope.rb +13 -2
  33. data/lib/mutant/meta.rb +0 -1
  34. data/lib/mutant/meta/example/dsl.rb +5 -1
  35. data/lib/mutant/mutator/node.rb +16 -44
  36. data/lib/mutant/mutator/node/or_asgn.rb +1 -1
  37. data/lib/mutant/mutator/node/send.rb +5 -60
  38. data/lib/mutant/mutator/node/super.rb +2 -5
  39. data/lib/mutant/mutator/registry.rb +1 -1
  40. data/lib/mutant/reporter.rb +10 -0
  41. data/lib/mutant/reporter/cli.rb +13 -0
  42. data/lib/mutant/reporter/cli/printer.rb +2 -0
  43. data/lib/mutant/reporter/cli/progress/config.rb +1 -1
  44. data/lib/mutant/reporter/cli/progress/noop.rb +2 -0
  45. data/lib/mutant/reporter/cli/registry.rb +2 -0
  46. data/lib/mutant/reporter/null.rb +12 -0
  47. data/lib/mutant/reporter/trace.rb +4 -0
  48. data/lib/mutant/require_highjack.rb +2 -2
  49. data/lib/mutant/rspec.rb +0 -0
  50. data/lib/mutant/runner.rb +2 -0
  51. data/lib/mutant/runner/config.rb +8 -8
  52. data/lib/mutant/runner/killer.rb +5 -0
  53. data/lib/mutant/runner/subject.rb +1 -1
  54. data/lib/mutant/subject.rb +8 -8
  55. data/lib/mutant/subject/method.rb +3 -2
  56. data/lib/mutant/subject/method/instance.rb +1 -1
  57. data/lib/mutant/test.rb +7 -65
  58. data/lib/mutant/test/report.rb +59 -0
  59. data/lib/mutant/version.rb +1 -1
  60. data/lib/mutant/warning_filter.rb +2 -0
  61. data/lib/mutant/zombifier.rb +3 -0
  62. data/lib/mutant/zombifier/file.rb +1 -1
  63. data/meta/or_asgn.rb +11 -0
  64. data/meta/send.rb +1 -1
  65. data/mutant-rspec.gemspec +1 -1
  66. data/mutant.gemspec +1 -1
  67. data/spec/integration/mutant/corpus_spec.rb +2 -0
  68. data/spec/integration/mutant/test_mutator_handles_types_spec.rb +2 -2
  69. data/spec/spec_helper.rb +4 -3
  70. data/spec/unit/mutant/cli_new_spec.rb +11 -11
  71. data/spec/unit/mutant/expression/methods_spec.rb +43 -0
  72. data/spec/unit/mutant/expression/namespace/flat_spec.rb +1 -1
  73. data/spec/unit/mutant/expression/namespace/recursive_spec.rb +19 -5
  74. data/spec/unit/mutant/{strategy_spec.rb → integration_spec.rb} +1 -1
  75. data/spec/unit/mutant/isolation_spec.rb +3 -1
  76. data/spec/unit/mutant/matcher/method/instance_spec.rb +5 -5
  77. data/spec/unit/mutant/matcher/method/singleton_spec.rb +8 -8
  78. data/spec/unit/mutant/matcher/methods/instance_spec.rb +5 -8
  79. data/spec/unit/mutant/matcher/methods/singleton_spec.rb +5 -5
  80. data/spec/unit/mutant/matcher/namespace_spec.rb +18 -23
  81. data/spec/unit/mutant/mutation_spec.rb +1 -1
  82. data/spec/unit/mutant/runner/config_spec.rb +4 -5
  83. data/spec/unit/mutant/runner/mutation_spec.rb +21 -21
  84. data/spec/unit/mutant/runner/subject_spec.rb +6 -6
  85. data/spec/unit/mutant/subject/method/instance_spec.rb +0 -4
  86. data/spec/unit/mutant/subject/method/singleton_spec.rb +0 -1
  87. data/spec/unit/mutant/subject_spec.rb +3 -3
  88. metadata +20 -6
  89. data/lib/mutant/node_helpers.rb +0 -52
@@ -28,6 +28,7 @@ module Mutant
28
28
  # @return [Boolean]
29
29
  #
30
30
  # @api private
31
+ #
31
32
  def success?
32
33
  @report.success?
33
34
  end
@@ -36,6 +37,8 @@ module Mutant
36
37
  #
37
38
  # @return [Boolean]
38
39
  #
40
+ # @api private
41
+ #
39
42
  def mutation_dead?
40
43
  test_report = report.test_report
41
44
  killer.mutation.should_fail? && test_report.failed?
@@ -59,6 +62,8 @@ module Mutant
59
62
 
60
63
  # Run killer
61
64
  #
65
+ # @return [undefined]
66
+ #
62
67
  # @api private
63
68
  #
64
69
  def run
@@ -64,7 +64,7 @@ module Mutant
64
64
  # @api private
65
65
  #
66
66
  def tests
67
- config.strategy.tests(subject)
67
+ config.tests(subject)
68
68
  end
69
69
  memoize :tests
70
70
 
@@ -54,7 +54,7 @@ module Mutant
54
54
  # @api private
55
55
  #
56
56
  def identification
57
- "#{match_expression}:#{source_path}:#{source_line}"
57
+ "#{expression.syntax}:#{source_path}:#{source_line}"
58
58
  end
59
59
  memoize :identification
60
60
 
@@ -94,22 +94,22 @@ module Mutant
94
94
 
95
95
  # Return match expression
96
96
  #
97
- # @return [String]
97
+ # @return [Expression]
98
98
  #
99
99
  # @api private
100
100
  #
101
- abstract_method :match_expression
101
+ abstract_method :expression
102
102
 
103
- # Return match prefixes
103
+ # Return match expressions
104
104
  #
105
- # @return [Enumerable<String>]
105
+ # @return [Enumerable<Expression>]
106
106
  #
107
107
  # @api private
108
108
  #
109
- def match_prefixes
110
- [match_expression].concat(context.match_prefixes)
109
+ def match_expressions
110
+ [expression].concat(context.match_expressions)
111
111
  end
112
- memoize :match_prefixes
112
+ memoize :match_expressions
113
113
 
114
114
  private
115
115
 
@@ -27,9 +27,10 @@ module Mutant
27
27
  #
28
28
  # @api private
29
29
  #
30
- def match_expression
31
- "#{context.identification}#{self.class::SYMBOL}#{name}"
30
+ def expression
31
+ Expression.parse("#{context.identification}#{self.class::SYMBOL}#{name}")
32
32
  end
33
+ memoize :expression
33
34
 
34
35
  private
35
36
 
@@ -39,7 +39,7 @@ module Mutant
39
39
 
40
40
  # Mutator for memoized instance methods
41
41
  class Memoized < self
42
- include NodeHelpers
42
+ include AST::Sexp
43
43
 
44
44
  # Return source
45
45
  #
@@ -1,65 +1,7 @@
1
1
  module Mutant
2
2
  # Abstract base class for test that might kill a mutation
3
3
  class Test
4
- include AbstractType, Adamantium::Flat
5
-
6
- # Object to report test status
7
- class Report
8
- include Adamantium::Flat, Anima::Update, Anima.new(
9
- :test,
10
- :output,
11
- :success
12
- )
13
-
14
- alias_method :success?, :success
15
-
16
- # Test if test failed
17
- #
18
- # @return [Boolean]
19
- #
20
- # @api private
21
- #
22
- def failed?
23
- !success?
24
- end
25
-
26
- # Return marshallable data
27
- #
28
- # NOTE:
29
- #
30
- # The test is intentionally NOT part of the mashalled data.
31
- # In rspec the example group cannot deterministically being marshalled, because
32
- # they reference a crazy mix of IO objects, global objects etc.
33
- #
34
- # @return [Array]
35
- #
36
- # @api private
37
- #
38
- def marshal_dump
39
- [@output, @success]
40
- end
41
-
42
- # Load marshalled data
43
- #
44
- # @param [Array] arry
45
- #
46
- # @return [undefined]
47
- #
48
- # @api private
49
- #
50
- def marshal_load(array)
51
- @output, @success = array
52
- end
53
-
54
- end # Report
55
-
56
- # Run tests
57
- #
58
- # @return [Test::Result]
59
- #
60
- # @api private
61
- #
62
- abstract_method :run
4
+ include Adamantium::Flat, Concord::Public.new(:integration, :expression)
63
5
 
64
6
  # Return test identification
65
7
  #
@@ -68,19 +10,19 @@ module Mutant
68
10
  # @api private
69
11
  #
70
12
  def identification
71
- "#{self.class::PREFIX}:#{subject_identification}"
13
+ "#{integration.name}:#{expression.syntax}"
72
14
  end
73
15
  memoize :identification
74
16
 
75
- # Return subject identification
17
+ # Run test, return report
76
18
  #
77
- # This method is used for current mutants primitive test selection.
78
- #
79
- # @return [String]
19
+ # @return [Report]
80
20
  #
81
21
  # @api private
82
22
  #
83
- abstract_method :subject_identification
23
+ def run
24
+ integration.run(self)
25
+ end
84
26
 
85
27
  end # Test
86
28
  end # Mutant
@@ -0,0 +1,59 @@
1
+ module Mutant
2
+ class Test
3
+ # Object to report test status
4
+ class Report
5
+ include Adamantium::Flat, Anima::Update, Anima.new(
6
+ :test,
7
+ :output,
8
+ :success
9
+ )
10
+
11
+ # Test if test was successful
12
+ #
13
+ # @return [Boolean]
14
+ #
15
+ # @api private
16
+ #
17
+ alias_method :success?, :success
18
+
19
+ # Test if test failed
20
+ #
21
+ # @return [Boolean]
22
+ #
23
+ # @api private
24
+ #
25
+ def failed?
26
+ !success?
27
+ end
28
+
29
+ # Return marshallable data
30
+ #
31
+ # NOTE:
32
+ #
33
+ # The test is intentionally NOT part of the mashalled data.
34
+ # In rspec the example group cannot deterministically being marshalled, because
35
+ # they reference a crazy mix of IO objects, global objects etc.
36
+ #
37
+ # @return [Array]
38
+ #
39
+ # @api private
40
+ #
41
+ def marshal_dump
42
+ [@output, @success]
43
+ end
44
+
45
+ # Load marshalled data
46
+ #
47
+ # @param [Array] arry
48
+ #
49
+ # @return [undefined]
50
+ #
51
+ # @api private
52
+ #
53
+ def marshal_load(array)
54
+ @output, @success = array
55
+ end
56
+
57
+ end # Report
58
+ end # Test
59
+ end # Mutant
@@ -1,4 +1,4 @@
1
1
  module Mutant
2
2
  # The current mutant version
3
- VERSION = '0.5.23'.freeze
3
+ VERSION = '0.5.24'.freeze
4
4
  end # Mutant
@@ -45,6 +45,8 @@ module Mutant
45
45
  #
46
46
  # @return [self]
47
47
  #
48
+ # @api private
49
+ #
48
50
  def write(message)
49
51
  if WARNING_PATTERN =~ message
50
52
  warnings << message
@@ -34,10 +34,13 @@ module Mutant
34
34
  # @param [String] logical_name
35
35
  # @param [Symbol] namespace
36
36
  #
37
+ # @return [self]
38
+ #
37
39
  # @api private
38
40
  #
39
41
  def self.run(logical_name, namespace)
40
42
  new(namespace).run(logical_name)
43
+ self
41
44
  end
42
45
 
43
46
  # Run zombifier
@@ -2,7 +2,7 @@ module Mutant
2
2
  class Zombifier
3
3
  # File containing source beeing zombified
4
4
  class File
5
- include NodeHelpers, Adamantium::Flat, Concord::Public.new(:path)
5
+ include Adamantium::Flat, Concord::Public.new(:path), AST::Sexp
6
6
 
7
7
  # Zombify contents of file
8
8
  #
@@ -22,3 +22,14 @@ Mutant::Meta::Example.add do
22
22
  mutation '@a ||= -1'
23
23
  mutation '@a ||= 2'
24
24
  end
25
+
26
+ Mutant::Meta::Example.add do
27
+ source 'foo[:bar] ||= 1'
28
+
29
+ singleton_mutations
30
+ mutation 'foo[:bar] ||= nil'
31
+ mutation 'foo[:bar] ||= self'
32
+ mutation 'foo[:bar] ||= 0'
33
+ mutation 'foo[:bar] ||= -1'
34
+ mutation 'foo[:bar] ||= 2'
35
+ end
@@ -259,7 +259,7 @@ Mutant::Meta::Example.add do
259
259
  mutation 'self[*bar]'
260
260
  end
261
261
 
262
- (Mutant::BINARY_METHOD_OPERATORS - [:==, :eql?]).each do |operator|
262
+ (Mutant::AST::Types::BINARY_METHOD_OPERATORS - [:==, :eql?]).each do |operator|
263
263
  Mutant::Meta::Example.add do
264
264
  source "true #{operator} false"
265
265
 
@@ -13,7 +13,7 @@ Gem::Specification.new do |gem|
13
13
  gem.license = 'MIT'
14
14
 
15
15
  gem.require_paths = %w[lib]
16
- gem.files = `git ls-files -- lib/mutant/rspec.rb lib/mutant/rspec`.split("\n")
16
+ gem.files = `git ls-files -- lib/mutant/integration/rspec{,2,3}.rb`.split("\n")
17
17
  gem.test_files = `git ls-files -- spec/{unit/mutant/rspec,integration/rspec}`.split("\n")
18
18
  gem.extra_rdoc_files = %w[TODO LICENSE]
19
19
 
@@ -14,7 +14,7 @@ Gem::Specification.new do |gem|
14
14
 
15
15
  gem.require_paths = %w[lib]
16
16
 
17
- mutant_rspec_files = `git ls-files -- lib/mutant{-,/}rspec.rb lib/mutant/rspec`.split("\n")
17
+ mutant_rspec_files = `git ls-files -- lib/mutant/integration/rspec{,2,3}.rb`.split("\n")
18
18
 
19
19
  gem.files = `git ls-files`.split("\n") - mutant_rspec_files
20
20
  gem.test_files = `git ls-files -- spec/{unit,integration}`.split("\n")
@@ -42,6 +42,8 @@ describe 'Mutant on ruby corpus' do
42
42
  devtools << "gem 'mutant', path: '#{relative}'\n"
43
43
  devtools << "gem 'mutant-rspec', path: '#{relative}'\n"
44
44
  File.write(repo_path.join('Gemfile.devtools'), devtools)
45
+ lockfile = repo_path.join('Gemfile.lock')
46
+ lockfile.delete if lockfile.exist?
45
47
  Bundler.with_clean_env do
46
48
  system('bundle install')
47
49
  system(%W[bundle exec mutant -I lib -r #{name} --score #{expect_coverage} --use rspec #{namespace}*])
@@ -3,8 +3,8 @@ require 'spec_helper'
3
3
  describe do
4
4
 
5
5
  specify 'mutant should not crash for any node parser can generate' do
6
- Mutant::NODE_TYPES.each do |type|
7
- Mutant::Mutator::Registry.lookup(Mutant::NodeHelpers.s(type))
6
+ Mutant::AST::Types::ALL.each do |type|
7
+ Mutant::Mutator::Registry.lookup(s(type))
8
8
  end
9
9
  end
10
10
  end
@@ -30,8 +30,8 @@ $LOAD_PATH << File.join(TestApp.root, 'lib')
30
30
  require 'test_app'
31
31
 
32
32
  module Fixtures
33
- AST_CACHE = Mutant::Cache.new
34
- end
33
+ BOOT_ENV = Mutant::Env::Boot.new(Mutant::Reporter::CLI.new(STDERR), Mutant::Cache.new)
34
+ end # Fixtures
35
35
 
36
36
  module ParserHelper
37
37
  def generate(node)
@@ -46,7 +46,8 @@ end
46
46
  RSpec.configure do |config|
47
47
  config.include(CompressHelper)
48
48
  config.include(ParserHelper)
49
- config.include(Mutant::NodeHelpers)
49
+ config.include(Mutant::AST::Sexp)
50
+
50
51
  config.expect_with :rspec do |rspec|
51
52
  rspec.syntax = :expect
52
53
  end
@@ -11,7 +11,7 @@ end
11
11
  shared_examples_for 'a cli parser' do
12
12
  subject { cli.config }
13
13
 
14
- it { expect(subject.strategy).to eql(expected_strategy) }
14
+ it { expect(subject.integration).to eql(expected_integration) }
15
15
  it { expect(subject.reporter).to eql(expected_reporter) }
16
16
  it { expect(subject.matcher).to eql(expected_matcher) }
17
17
  end
@@ -25,12 +25,12 @@ describe Mutant::CLI, '.new' do
25
25
  end
26
26
 
27
27
  # Defaults
28
- let(:expected_filter) { Morpher.evaluator(s(:true)) }
29
- let(:expected_strategy) { Mutant::Strategy::Null.new }
30
- let(:expected_reporter) { Mutant::Reporter::CLI.new($stdout) }
28
+ let(:expected_filter) { Morpher.evaluator(s(:true)) }
29
+ let(:expected_integration) { Mutant::Integration::Null.new }
30
+ let(:expected_reporter) { Mutant::Reporter::CLI.new($stdout) }
31
31
 
32
- let(:ns) { Mutant::Matcher }
33
- let(:cache) { Mutant::Cache.new }
32
+ let(:ns) { Mutant::Matcher }
33
+ let(:env) { Fixtures::BOOT_ENV }
34
34
 
35
35
  let(:cli) { object.new(arguments) }
36
36
 
@@ -71,7 +71,7 @@ describe Mutant::CLI, '.new' do
71
71
  let(:arguments) { %w[TestApp::Literal#float] }
72
72
 
73
73
  let(:expected_matcher) do
74
- ns::Method::Instance.new(cache, TestApp::Literal, TestApp::Literal.instance_method(:float))
74
+ ns::Method::Instance.new(env, TestApp::Literal, TestApp::Literal.instance_method(:float))
75
75
  end
76
76
 
77
77
  it_should_behave_like 'a cli parser'
@@ -80,7 +80,7 @@ describe Mutant::CLI, '.new' do
80
80
  context 'with debug flag' do
81
81
  let(:pattern) { 'TestApp*' }
82
82
  let(:arguments) { %W[--debug #{pattern}] }
83
- let(:expected_matcher) { ns::Namespace.new(cache, 'TestApp') }
83
+ let(:expected_matcher) { ns::Namespace.new(env, Mutant::Expression.parse(pattern)) }
84
84
 
85
85
  it_should_behave_like 'a cli parser'
86
86
 
@@ -92,7 +92,7 @@ describe Mutant::CLI, '.new' do
92
92
  context 'with zombie flag' do
93
93
  let(:pattern) { 'TestApp*' }
94
94
  let(:arguments) { %W[--zombie #{pattern}] }
95
- let(:expected_matcher) { ns::Namespace.new(cache, 'TestApp') }
95
+ let(:expected_matcher) { ns::Namespace.new(env, Mutant::Expression.parse(pattern)) }
96
96
 
97
97
  it_should_behave_like 'a cli parser'
98
98
 
@@ -104,7 +104,7 @@ describe Mutant::CLI, '.new' do
104
104
  context 'with namespace pattern' do
105
105
  let(:pattern) { 'TestApp*' }
106
106
  let(:arguments) { [pattern] }
107
- let(:expected_matcher) { ns::Namespace.new(cache, 'TestApp') }
107
+ let(:expected_matcher) { ns::Namespace.new(env, Mutant::Expression.parse(pattern)) }
108
108
 
109
109
  it_should_behave_like 'a cli parser'
110
110
  end
@@ -124,7 +124,7 @@ describe Mutant::CLI, '.new' do
124
124
 
125
125
  let(:expected_matcher) do
126
126
  matcher = ns::Method::Instance.new(
127
- cache,
127
+ env,
128
128
  TestApp::Literal, TestApp::Literal.instance_method(:float)
129
129
  )
130
130
  predicate = Morpher.compile(