mutant 0.8.9 → 0.8.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +4 -0
  3. data/config/devtools.yml +1 -1
  4. data/config/flay.yml +1 -1
  5. data/lib/mutant.rb +1 -2
  6. data/lib/mutant/ast/types.rb +3 -2
  7. data/lib/mutant/context.rb +78 -6
  8. data/lib/mutant/diff.rb +7 -13
  9. data/lib/mutant/matcher/method.rb +2 -2
  10. data/lib/mutant/mutation.rb +1 -1
  11. data/lib/mutant/mutator/node/generic.rb +1 -1
  12. data/lib/mutant/reporter/cli/printer.rb +25 -1
  13. data/lib/mutant/reporter/sequence.rb +22 -0
  14. data/lib/mutant/result.rb +4 -9
  15. data/lib/mutant/version.rb +1 -1
  16. data/meta/dsym.rb +7 -7
  17. data/mutant.gemspec +5 -5
  18. data/spec/spec_helper.rb +0 -1
  19. data/spec/unit/mutant/ast/named_children_spec.rb +12 -2
  20. data/spec/unit/mutant/context_spec.rb +85 -1
  21. data/spec/unit/mutant/diff_spec.rb +9 -0
  22. data/spec/unit/mutant/mutation_spec.rb +55 -4
  23. data/spec/unit/mutant/reporter/cli/printer/mutation_result_spec.rb +3 -3
  24. data/spec/unit/mutant/reporter/cli/printer_spec.rb +27 -6
  25. data/spec/unit/mutant/reporter/null_spec.rb +5 -15
  26. data/spec/unit/mutant/reporter/sequence_spec.rb +29 -0
  27. data/spec/unit/mutant/result/class_methods_spec.rb +49 -0
  28. data/spec/unit/mutant/result/mutation_spec.rb +49 -0
  29. data/spec/unit/mutant/result/subject_spec.rb +84 -19
  30. data/spec/unit/mutant/result_spec.rb +18 -10
  31. data/spec/unit/mutant/subject/method/instance_spec.rb +3 -3
  32. data/spec/unit/mutant/subject/method/singleton_spec.rb +1 -1
  33. metadata +19 -22
  34. data/lib/mutant/context/scope.rb +0 -94
  35. data/lib/mutant/delegator.rb +0 -43
  36. data/spec/unit/mutant/context/root_spec.rb +0 -11
  37. data/spec/unit/mutant/context/scope/root_spec.rb +0 -32
  38. data/spec/unit/mutant/context/scope/unqualified_name_spec.rb +0 -25
  39. data/spec/unit/mutant/context/scope_spec.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6629a951cb8a81c688c0adc0acac1f3ff41f13b6
4
- data.tar.gz: b8e775063ca733fe989083a38c31ef680135cbc1
3
+ metadata.gz: 37c56c026986c92dd3afb4306adaea0d80b130f2
4
+ data.tar.gz: 0513911185fbe4a6967146e75ebec39e77537875
5
5
  SHA512:
6
- metadata.gz: f11084993d2875f56e6fd86174eaae8a1b8681feee2f81d4442914fc63ab9a7e8a9be395145b55b424519f5c7975f114bf5adf0c7e01ce1dca5b2aa091fd0364
7
- data.tar.gz: 44e651cc4312db4388d975f1955791966055c67fc4a648a297e34535d9f47cea0588e94921098416d2a0636d0d4d26e017a75377960beeb28dcfa8ad555032c0
6
+ metadata.gz: 838d0e823d5666c5b615aa4c3f94af69c90034873cf07bc113524dfad2b265cdda16b350d189e410ff0229657fc425ddb0d34d442f19091a11151f2757f10b06
7
+ data.tar.gz: 60493346edfd51f98857e94c286de02c6293933ad379331f4513f18ec82b35f5c93ae5b6f14f8a196ac69d071004ba8529bd0ef902acf8ea8a32eb471b41f4b3
@@ -1,3 +1,7 @@
1
+ # v0.8.10 2016-01-24
2
+
3
+ * Add support for parser 2.3 (via unparser 0.2.5)
4
+
1
5
  # v0.8.9 2016-01-05
2
6
 
3
7
  * Add mutation from `Hash#[]` to `Hash#key?` (#511)
@@ -1,2 +1,2 @@
1
1
  ---
2
- unit_test_timeout: 10
2
+ unit_test_timeout: 10.0
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 18
3
- total_score: 1173
3
+ total_score: 1171
@@ -61,7 +61,6 @@ require 'mutant/actor/sender'
61
61
  require 'mutant/actor/mailbox'
62
62
  require 'mutant/actor/env'
63
63
  require 'mutant/parser'
64
- require 'mutant/delegator'
65
64
  require 'mutant/isolation'
66
65
  require 'mutant/parallel'
67
66
  require 'mutant/parallel/master'
@@ -131,7 +130,6 @@ require 'mutant/mutator/node/rescue'
131
130
  require 'mutant/mutator/node/match_current_line'
132
131
  require 'mutant/loader'
133
132
  require 'mutant/context'
134
- require 'mutant/context/scope'
135
133
  require 'mutant/scope'
136
134
  require 'mutant/subject'
137
135
  require 'mutant/subject/method'
@@ -168,6 +166,7 @@ require 'mutant/runner/sink'
168
166
  require 'mutant/result'
169
167
  require 'mutant/reporter'
170
168
  require 'mutant/reporter/null'
169
+ require 'mutant/reporter/sequence'
171
170
  require 'mutant/reporter/cli'
172
171
  require 'mutant/reporter/cli/printer'
173
172
  require 'mutant/reporter/cli/printer/config'
@@ -41,8 +41,9 @@ module Mutant
41
41
  # Nodes that are NOT generated by parser but used by mutant / unparser.
42
42
  EXTRA = symbolset.(%w[empty])
43
43
 
44
- # Nodes that are currently missing from parser META
45
- MISSING = symbolset.(%w[rational])
44
+ # Nodes that are currently missing from Parser::Meta::NODE_TYPES
45
+ # * https://github.com/whitequark/parser/pull/251
46
+ MISSING = symbolset.(%w[csend])
46
47
 
47
48
  # All node types mutant handles
48
49
  ALL = symbolset.((Parser::Meta::NODE_TYPES + EXTRA + MISSING) - BLACKLIST)
@@ -1,19 +1,91 @@
1
1
  module Mutant
2
2
  # An abstract context where mutations can be applied to.
3
3
  class Context
4
- include Adamantium::Flat, AbstractType, Concord::Public.new(:source_path)
4
+ include Adamantium::Flat, Concord::Public.new(:scope, :source_path)
5
+ extend AST::Sexp
5
6
 
6
- # Root ast node
7
- #
8
- # @param [Parser::AST::Node] node
7
+ NAMESPACE_DELIMITER = '::'.freeze
8
+
9
+ # Return root node for mutation
9
10
  #
10
11
  # @return [Parser::AST::Node]
11
- abstract_method :root
12
+ def root(node)
13
+ nesting.reverse.reduce(node) do |current, scope|
14
+ self.class.wrap(scope, current)
15
+ end
16
+ end
12
17
 
13
18
  # Identification string
14
19
  #
15
20
  # @return [String]
16
- abstract_method :identification
21
+ def identification
22
+ scope.name
23
+ end
24
+
25
+ # Wrap node into ast node
26
+ #
27
+ # @param [Class, Module] scope
28
+ # @param [Parser::AST::Node] node
29
+ #
30
+ # @return [Parser::AST::Class]
31
+ # if scope is of kind Class
32
+ #
33
+ # @return [Parser::AST::Module]
34
+ # if scope is of kind module
35
+ def self.wrap(scope, node)
36
+ name = s(:const, nil, scope.name.split(NAMESPACE_DELIMITER).last.to_sym)
37
+ case scope
38
+ when Class
39
+ s(:class, name, nil, node)
40
+ when Module
41
+ s(:module, name, node)
42
+ end
43
+ end
44
+
45
+ # Nesting of scope
46
+ #
47
+ # @return [Enumerable<Class,Module>]
48
+ def nesting
49
+ const = Object
50
+ name_nesting.map do |name|
51
+ const = const.const_get(name)
52
+ end
53
+ end
54
+ memoize :nesting
55
+
56
+ # Unqualified name of scope
57
+ #
58
+ # @return [String]
59
+ def unqualified_name
60
+ name_nesting.last
61
+ end
62
+
63
+ # Match expressions for scope
64
+ #
65
+ # @return [Enumerable<Expression>]
66
+ def match_expressions
67
+ name_nesting.each_index.reverse_each.map do |index|
68
+ Expression::Namespace::Recursive.new(
69
+ scope_name: name_nesting.take(index.succ).join(NAMESPACE_DELIMITER)
70
+ )
71
+ end
72
+ end
73
+ memoize :match_expressions
74
+
75
+ # Scope wrapped by context
76
+ #
77
+ # @return [Module|Class]
78
+ attr_reader :scope
79
+
80
+ private
81
+
82
+ # Nesting of names in scope
83
+ #
84
+ # @return [Array<String>]
85
+ def name_nesting
86
+ scope.name.split(NAMESPACE_DELIMITER)
87
+ end
88
+ memoize :name_nesting
17
89
 
18
90
  end # Context
19
91
  end # Mutant
@@ -17,9 +17,7 @@ module Mutant
17
17
  def diff
18
18
  return if diffs.empty?
19
19
 
20
- minimized_hunks.map do |hunk|
21
- hunk.diff(:unified) << NEWLINE
22
- end.join
20
+ minimized_hunk.diff(:unified) << NEWLINE
23
21
  end
24
22
  memoize :diff
25
23
 
@@ -74,19 +72,15 @@ module Mutant
74
72
  end
75
73
  end
76
74
 
77
- # Minimized hunks
75
+ # Minimized hunk
78
76
  #
79
- # @return [Array<Diff::LCS::Hunk>]
80
- def minimized_hunks
77
+ # @return Diff::LCS::Hunk
78
+ def minimized_hunk
81
79
  head, *tail = hunks
82
80
 
83
- tail.each_with_object([head]) do |right, aggregate|
84
- left = aggregate.last
85
- if right.overlaps?(left)
86
- right.merge(left)
87
- aggregate.pop
88
- end
89
- aggregate << right
81
+ tail.reduce(head) do |left, right|
82
+ right.merge(left)
83
+ right
90
84
  end
91
85
  end
92
86
 
@@ -69,9 +69,9 @@ module Mutant
69
69
 
70
70
  # Target context
71
71
  #
72
- # @return [Context::Scope]
72
+ # @return [Context]
73
73
  def context
74
- Context::Scope.new(scope, source_path)
74
+ Context.new(scope, source_path)
75
75
  end
76
76
 
77
77
  # Root source node
@@ -40,7 +40,7 @@ module Mutant
40
40
 
41
41
  # Test if mutation is killed by test reports
42
42
  #
43
- # @param [Array<Report::Test>] test_reports
43
+ # @param [Result::Test] test_result
44
44
  #
45
45
  # @return [Boolean]
46
46
  def self.success?(test_result)
@@ -8,7 +8,7 @@ module Mutant
8
8
  # These nodes still need a dedicated mutator,
9
9
  # your contribution is that close!
10
10
  handle(
11
- :ensure, :redo, :regopt, :retry, :arg_expr,
11
+ :csend, :ensure, :redo, :regopt, :retry, :arg_expr,
12
12
  :kwrestarg, :kwoptarg, :kwarg, :undef, :module, :empty,
13
13
  :alias, :for, :xstr, :back_ref, :class,
14
14
  :sclass, :match_with_lvasgn, :while_post,
@@ -3,10 +3,34 @@ module Mutant
3
3
  class CLI
4
4
  # CLI runner status printer base class
5
5
  class Printer
6
- include AbstractType, Delegator, Adamantium::Flat, Concord.new(:output, :object), Procto.call(:run)
6
+ include AbstractType,
7
+ Adamantium::Flat,
8
+ Concord.new(:output, :object),
9
+ Procto.call(:run)
7
10
 
8
11
  private_class_method :new
9
12
 
13
+ # Create delegators to object
14
+ #
15
+ # @return [undefined]
16
+ def self.delegate(*names)
17
+ names.each(&method(:define_delegator))
18
+ end
19
+ private_class_method :delegate
20
+
21
+ # Create delegator to object
22
+ #
23
+ # @param [Symbol] name
24
+ #
25
+ # @return [undefined]
26
+ def self.define_delegator(name)
27
+ define_method(name) do
28
+ object.public_send(name)
29
+ end
30
+ private(name)
31
+ end
32
+ private_class_method :define_delegator
33
+
10
34
  delegate :success?
11
35
 
12
36
  NL = "\n".freeze
@@ -0,0 +1,22 @@
1
+ module Mutant
2
+ class Reporter
3
+ class Sequence < self
4
+ include Concord.new(:reporters)
5
+
6
+ %i[warn progress report start].each do |name|
7
+ define_method(name) do |value|
8
+ reporters.each do |reporter|
9
+ reporter.public_send(name, value)
10
+ end
11
+
12
+ self
13
+ end
14
+ end
15
+
16
+ def delay
17
+ reporters.map(&:delay).min
18
+ end
19
+
20
+ end # Sequence
21
+ end # Reporter
22
+ end # Mutant
@@ -136,14 +136,7 @@ module Mutant
136
136
  alive_mutation_results.empty?
137
137
  end
138
138
 
139
- # Test if runner should continue on subject
140
- #
141
- # @return [Boolean]
142
- def continue?
143
- mutation_results.all?(&:success?)
144
- end
145
-
146
- # Killed mutations
139
+ # Alive mutations
147
140
  #
148
141
  # @return [Array<Result::Mutation>]
149
142
  def alive_mutation_results
@@ -179,7 +172,9 @@ module Mutant
179
172
  alive_mutation_results.length
180
173
  end
181
174
 
182
- # Alive mutations
175
+ private
176
+
177
+ # Killed mutation results
183
178
  #
184
179
  # @return [Array<Result::Mutation>]
185
180
  def killed_mutation_results
@@ -1,4 +1,4 @@
1
1
  module Mutant
2
2
  # Current mutant version
3
- VERSION = '0.8.9'.freeze
3
+ VERSION = '0.8.10'.freeze
4
4
  end # Mutant
@@ -2,11 +2,11 @@ Mutant::Meta::Example.add do
2
2
  source ':"foo#{bar}baz"'
3
3
 
4
4
  singleton_mutations
5
- # TODO: Unparser imperfection two asts with same source.
6
- mutation s(:dsym, s(:nil), s(:begin, s(:send, nil, :bar)), s(:str, 'baz'))
7
- mutation s(:dsym, s(:self), s(:begin, s(:send, nil, :bar)), s(:str, 'baz'))
8
- mutation s(:dsym, s(:str, 'foo'), s(:begin, s(:self)), s(:str, 'baz'))
9
- mutation s(:dsym, s(:str, 'foo'), s(:begin, s(:nil)), s(:str, 'baz'))
10
- mutation s(:dsym, s(:str, 'foo'), s(:begin, s(:send, nil, :bar)), s(:nil))
11
- mutation s(:dsym, s(:str, 'foo'), s(:begin, s(:send, nil, :bar)), s(:self))
5
+
6
+ mutation ':"#{nil}#{bar}#{"baz"}"'
7
+ mutation ':"#{self}#{bar}#{"baz"}"'
8
+ mutation ':"#{"foo"}#{nil}#{"baz"}"'
9
+ mutation ':"#{"foo"}#{self}#{"baz"}"'
10
+ mutation ':"#{"foo"}#{bar}#{nil}"'
11
+ mutation ':"#{"foo"}#{bar}#{self}"'
12
12
  end
@@ -21,14 +21,14 @@ Gem::Specification.new do |gem|
21
21
 
22
22
  gem.required_ruby_version = '>= 2.1'
23
23
 
24
- gem.add_runtime_dependency('parser', '~> 2.2.2')
25
- gem.add_runtime_dependency('ast', '~> 2.1')
24
+ gem.add_runtime_dependency('parser', '~> 2.3.0')
25
+ gem.add_runtime_dependency('ast', '~> 2.2')
26
26
  gem.add_runtime_dependency('diff-lcs', '~> 1.2')
27
27
  gem.add_runtime_dependency('parallel', '~> 1.3')
28
- gem.add_runtime_dependency('morpher', '~> 0.2.5')
28
+ gem.add_runtime_dependency('morpher', '~> 0.2.6')
29
29
  gem.add_runtime_dependency('procto', '~> 0.0.2')
30
30
  gem.add_runtime_dependency('abstract_type', '~> 0.0.7')
31
- gem.add_runtime_dependency('unparser', '~> 0.2.4')
31
+ gem.add_runtime_dependency('unparser', '~> 0.2.5')
32
32
  gem.add_runtime_dependency('ice_nine', '~> 0.11.1')
33
33
  gem.add_runtime_dependency('adamantium', '~> 0.2.0')
34
34
  gem.add_runtime_dependency('memoizable', '~> 0.4.2')
@@ -36,7 +36,7 @@ Gem::Specification.new do |gem|
36
36
  gem.add_runtime_dependency('anima', '~> 0.3.0')
37
37
  gem.add_runtime_dependency('concord', '~> 0.1.5')
38
38
 
39
- gem.add_development_dependency('devtools', '~> 0.1.1')
39
+ gem.add_development_dependency('devtools', '~> 0.1.3')
40
40
  gem.add_development_dependency('bundler', '~> 1.10')
41
41
  gem.add_development_dependency('ffi', '~> 1.9.6')
42
42
  end
@@ -26,7 +26,6 @@ require 'devtools/spec_helper'
26
26
  require 'unparser/cli'
27
27
  require 'mutant'
28
28
  require 'mutant/meta'
29
- Devtools.init_spec_helper
30
29
 
31
30
  $LOAD_PATH << File.join(TestApp.root, 'lib')
32
31
 
@@ -42,8 +42,18 @@ RSpec.describe Mutant::AST::NamedChildren do
42
42
  end
43
43
 
44
44
  describe '#bar' do
45
- it 'returns named child bar' do
46
- expect(instance.bar).to be(node_bar)
45
+ context 'when node is present' do
46
+ it 'returns named child bar' do
47
+ expect(instance.bar).to be(node_bar)
48
+ end
49
+ end
50
+
51
+ context 'when node is NOT present' do
52
+ let(:node) { s(:node, node_foo) }
53
+
54
+ it 'returns nil' do
55
+ expect(instance.bar).to be(nil)
56
+ end
47
57
  end
48
58
  end
49
59
  end
@@ -1,5 +1,5 @@
1
1
  # rubocop:disable ClosingParenthesisIndentation
2
- RSpec.describe Mutant::Context::Scope do
2
+ RSpec.describe Mutant::Context do
3
3
  describe '.wrap' do
4
4
  subject { described_class.wrap(scope, node) }
5
5
 
@@ -32,4 +32,88 @@ RSpec.describe Mutant::Context::Scope do
32
32
  it { should eql(expected) }
33
33
  end
34
34
  end
35
+
36
+ let(:object) { described_class.new(scope, source_path) }
37
+ let(:scope_name) { instance_double(String) }
38
+ let(:source_path) { instance_double(Pathname) }
39
+ let(:scope) { TestApp::Literal }
40
+
41
+ describe '#identification' do
42
+ subject { object.identification }
43
+
44
+ it { should eql(scope.name) }
45
+ end
46
+
47
+ describe '#root' do
48
+ subject { object.root(node) }
49
+
50
+ let(:node) { s(:sym, :node) }
51
+
52
+ let(:expected_source) do
53
+ generate(parse(<<-RUBY))
54
+ module TestApp
55
+ class Literal
56
+ :node
57
+ end
58
+ end
59
+ RUBY
60
+ end
61
+
62
+ let(:generated_source) do
63
+ Unparser.unparse(subject)
64
+ end
65
+
66
+ let(:round_tripped_source) do
67
+ Unparser.unparse(parse(expected_source))
68
+ end
69
+
70
+ it 'should create correct source' do
71
+ expect(generated_source).to eql(expected_source)
72
+ end
73
+ end
74
+
75
+ describe '#unqualified_name' do
76
+ subject { object.unqualified_name }
77
+
78
+ let(:path) { instance_double(Pathname) }
79
+
80
+ context 'with top level constant name' do
81
+ let(:scope) { TestApp }
82
+
83
+ it 'should return the unqualified name' do
84
+ should eql('TestApp')
85
+ end
86
+
87
+ it_should_behave_like 'an idempotent method'
88
+ end
89
+
90
+ context 'with scoped constant name' do
91
+ it 'should return the unqualified name' do
92
+ should eql('Literal')
93
+ end
94
+
95
+ it_should_behave_like 'an idempotent method'
96
+ end
97
+ end
98
+
99
+ describe '#match_expressions' do
100
+ subject { object.match_expressions }
101
+
102
+ context 'on toplevel scope' do
103
+ let(:scope) { TestApp }
104
+
105
+ it { should eql([parse_expression('TestApp*')]) }
106
+ end
107
+
108
+ context 'on nested scope' do
109
+ specify do
110
+ should eql(
111
+ [
112
+ parse_expression('TestApp::Literal*'),
113
+ parse_expression('TestApp*')
114
+ ]
115
+ )
116
+ end
117
+ end
118
+ end
35
119
  end