mutant 0.5.26 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/.travis.yml +1 -0
  4. data/Changelog.md +16 -3
  5. data/Gemfile +0 -2
  6. data/Gemfile.devtools +2 -2
  7. data/README.md +9 -15
  8. data/bin/mutant +0 -1
  9. data/config/flay.yml +1 -1
  10. data/config/flog.yml +1 -1
  11. data/config/mutant.yml +1 -1
  12. data/config/reek.yml +14 -11
  13. data/config/rubocop.yml +1 -1
  14. data/lib/mutant.rb +22 -21
  15. data/lib/mutant/ast.rb +47 -0
  16. data/lib/mutant/cli.rb +7 -4
  17. data/lib/mutant/config.rb +1 -0
  18. data/lib/mutant/context.rb +1 -1
  19. data/lib/mutant/diff.rb +38 -7
  20. data/lib/mutant/env.rb +22 -3
  21. data/lib/mutant/expression.rb +15 -4
  22. data/lib/mutant/integration.rb +1 -1
  23. data/lib/mutant/isolation.rb +2 -4
  24. data/lib/mutant/matcher.rb +1 -1
  25. data/lib/mutant/matcher/method.rb +1 -1
  26. data/lib/mutant/matcher/method/singleton.rb +1 -1
  27. data/lib/mutant/matcher/methods.rb +0 -2
  28. data/lib/mutant/meta/example.rb +0 -2
  29. data/lib/mutant/meta/example/dsl.rb +1 -1
  30. data/lib/mutant/mutator.rb +1 -1
  31. data/lib/mutant/mutator/node.rb +3 -3
  32. data/lib/mutant/mutator/node/begin.rb +1 -1
  33. data/lib/mutant/mutator/node/block.rb +16 -3
  34. data/lib/mutant/mutator/node/if.rb +1 -1
  35. data/lib/mutant/mutator/node/literal/fixnum.rb +1 -1
  36. data/lib/mutant/mutator/node/resbody.rb +0 -2
  37. data/lib/mutant/mutator/node/send.rb +17 -7
  38. data/lib/mutant/mutator/node/send/index.rb +0 -2
  39. data/lib/mutant/mutator/registry.rb +1 -1
  40. data/lib/mutant/mutator/util.rb +1 -1
  41. data/lib/mutant/mutator/util/array.rb +1 -1
  42. data/lib/mutant/reporter.rb +13 -3
  43. data/lib/mutant/reporter/cli.rb +54 -8
  44. data/lib/mutant/reporter/cli/format.rb +197 -0
  45. data/lib/mutant/reporter/cli/printer.rb +402 -22
  46. data/lib/mutant/reporter/cli/tput.rb +27 -0
  47. data/lib/mutant/reporter/null.rb +4 -34
  48. data/lib/mutant/reporter/trace.rb +6 -38
  49. data/lib/mutant/result.rb +44 -56
  50. data/lib/mutant/runner.rb +99 -52
  51. data/lib/mutant/runner/collector.rb +134 -0
  52. data/lib/mutant/subject/method/instance.rb +12 -4
  53. data/lib/mutant/version.rb +1 -1
  54. data/lib/mutant/warning_filter.rb +0 -2
  55. data/lib/mutant/zombifier/file.rb +1 -1
  56. data/meta/block.rb +17 -1
  57. data/meta/send.rb +123 -1
  58. data/mutant-rspec.gemspec +3 -3
  59. data/mutant.gemspec +1 -1
  60. data/spec/integration/mutant/corpus_spec.rb +4 -195
  61. data/spec/integration/mutant/null_spec.rb +1 -3
  62. data/spec/integration/mutant/rspec_spec.rb +1 -3
  63. data/spec/integration/mutant/test_mutator_handles_types_spec.rb +1 -3
  64. data/spec/integration/mutant/zombie_spec.rb +1 -3
  65. data/spec/integrations.yml +7 -0
  66. data/spec/shared/method_matcher_behavior.rb +1 -1
  67. data/spec/spec_helper.rb +1 -0
  68. data/spec/support/compress_helper.rb +1 -0
  69. data/spec/support/corpus.rb +239 -0
  70. data/spec/support/mutation_verifier.rb +2 -4
  71. data/spec/unit/mutant/cli_spec.rb +20 -13
  72. data/spec/unit/mutant/context/root_spec.rb +1 -3
  73. data/spec/unit/mutant/context/scope/root_spec.rb +1 -3
  74. data/spec/unit/mutant/context/scope/unqualified_name_spec.rb +1 -3
  75. data/spec/unit/mutant/diff_spec.rb +37 -19
  76. data/spec/unit/mutant/expression/method_spec.rb +5 -7
  77. data/spec/unit/mutant/expression/methods_spec.rb +5 -7
  78. data/spec/unit/mutant/expression/namespace/flat_spec.rb +6 -8
  79. data/spec/unit/mutant/expression/namespace/recursive_spec.rb +6 -7
  80. data/spec/unit/mutant/expression_spec.rb +14 -5
  81. data/spec/unit/mutant/integration_spec.rb +14 -3
  82. data/spec/unit/mutant/isolation_spec.rb +2 -4
  83. data/spec/unit/mutant/loader/eval_spec.rb +1 -3
  84. data/spec/unit/mutant/matcher/chain_spec.rb +1 -3
  85. data/spec/unit/mutant/matcher/compiler/subject_prefix_spec.rb +21 -0
  86. data/spec/unit/mutant/matcher/compiler_spec.rb +28 -3
  87. data/spec/unit/mutant/matcher/filter_spec.rb +1 -3
  88. data/spec/unit/mutant/matcher/method/instance_spec.rb +3 -5
  89. data/spec/unit/mutant/matcher/method/singleton_spec.rb +22 -4
  90. data/spec/unit/mutant/matcher/methods/instance_spec.rb +7 -6
  91. data/spec/unit/mutant/matcher/methods/singleton_spec.rb +4 -6
  92. data/spec/unit/mutant/matcher/namespace_spec.rb +1 -3
  93. data/spec/unit/mutant/matcher/null_spec.rb +1 -3
  94. data/spec/unit/mutant/mutation_spec.rb +1 -3
  95. data/spec/unit/mutant/mutator/node_spec.rb +1 -3
  96. data/spec/unit/mutant/reporter/cli_spec.rb +444 -206
  97. data/spec/unit/mutant/reporter/null_spec.rb +1 -3
  98. data/spec/unit/mutant/require_highjack_spec.rb +1 -3
  99. data/spec/unit/mutant/runner_spec.rb +42 -28
  100. data/spec/unit/mutant/subject/context_spec.rb +1 -3
  101. data/spec/unit/mutant/subject/method/instance_spec.rb +27 -19
  102. data/spec/unit/mutant/subject/method/singleton_spec.rb +49 -17
  103. data/spec/unit/mutant/subject_spec.rb +1 -3
  104. data/spec/unit/mutant/test_spec.rb +1 -3
  105. data/spec/unit/mutant/warning_expectation.rb +1 -3
  106. data/spec/unit/mutant/warning_filter_spec.rb +1 -3
  107. data/spec/unit/mutant_spec.rb +13 -3
  108. data/test_app/Gemfile.devtools +2 -2
  109. data/test_app/spec/unit/test_app/literal/string_spec.rb +1 -1
  110. metadata +10 -21
  111. data/lib/mutant/matcher/method/finder.rb +0 -72
  112. data/lib/mutant/reporter/cli/progress.rb +0 -10
  113. data/lib/mutant/reporter/cli/progress/config.rb +0 -30
  114. data/lib/mutant/reporter/cli/progress/env.rb +0 -30
  115. data/lib/mutant/reporter/cli/progress/noop.rb +0 -27
  116. data/lib/mutant/reporter/cli/progress/result.rb +0 -12
  117. data/lib/mutant/reporter/cli/progress/result/mutation.rb +0 -45
  118. data/lib/mutant/reporter/cli/progress/result/subject.rb +0 -54
  119. data/lib/mutant/reporter/cli/progress/subject.rb +0 -27
  120. data/lib/mutant/reporter/cli/registry.rb +0 -81
  121. data/lib/mutant/reporter/cli/report.rb +0 -10
  122. data/lib/mutant/reporter/cli/report/env.rb +0 -92
  123. data/lib/mutant/reporter/cli/report/mutation.rb +0 -103
  124. data/lib/mutant/reporter/cli/report/subject.rb +0 -32
  125. data/lib/mutant/reporter/cli/report/test.rb +0 -28
  126. data/lib/mutant/walker.rb +0 -53
  127. data/spec/shared/mutator_behavior.rb +0 -55
@@ -10,6 +10,7 @@ module Mutant
10
10
  :reporter,
11
11
  :isolation,
12
12
  :fail_fast,
13
+ :processes,
13
14
  :zombie,
14
15
  :expected_coverage
15
16
  )
@@ -1,5 +1,5 @@
1
1
  module Mutant
2
- # An abstract context where mutations can be appied to.
2
+ # An abstract context where mutations can be applied to.
3
3
  class Context
4
4
  include Adamantium::Flat, AbstractType, Concord::Public.new(:source_path)
5
5
 
@@ -18,9 +18,11 @@ module Mutant
18
18
  # @api private
19
19
  #
20
20
  def diff
21
- return unless diffs.length.equal?(1)
22
- ::Diff::LCS::Hunk.new(old, new, diffs.first, max_length, 0)
23
- .diff(:unified) << NEWLINE
21
+ return if diffs.empty?
22
+
23
+ minimized_hunks.map do |hunk|
24
+ hunk.diff(:unified) << NEWLINE
25
+ end.join
24
26
  end
25
27
  memoize :diff
26
28
 
@@ -36,9 +38,7 @@ module Mutant
36
38
  #
37
39
  def colorized_diff
38
40
  return unless diff
39
- diff.lines.map do |line|
40
- self.class.colorize_line(line)
41
- end.join
41
+ diff.lines.map(&self.class.method(:colorize_line)).join
42
42
  end
43
43
  memoize :colorized_diff
44
44
 
@@ -79,7 +79,37 @@ module Mutant
79
79
  def diffs
80
80
  ::Diff::LCS.diff(old, new)
81
81
  end
82
- memoize :diffs
82
+
83
+ # Return hunks
84
+ #
85
+ # @return [Array<Diff::LCS::Hunk>]
86
+ #
87
+ # @api private
88
+ #
89
+ def hunks
90
+ diffs.map do |diff|
91
+ ::Diff::LCS::Hunk.new(old, new, diff, max_length, 0)
92
+ end
93
+ end
94
+
95
+ # Return minimized hunks
96
+ #
97
+ # @return [Array<Diff::LCS::Hunk>]
98
+ #
99
+ # @api private
100
+ #
101
+ def minimized_hunks
102
+ head, *tail = hunks()
103
+
104
+ tail.each_with_object([head]) do |right, aggregate|
105
+ left = aggregate.last
106
+ if right.overlaps?(left)
107
+ right.merge(left)
108
+ aggregate.pop
109
+ end
110
+ aggregate << right
111
+ end
112
+ end
83
113
 
84
114
  # Return max length
85
115
  #
@@ -109,6 +139,7 @@ module Mutant
109
139
  Color::NONE
110
140
  end.format(line)
111
141
  end
142
+ private_class_method :colorize_line
112
143
 
113
144
  end # Diff
114
145
  end # Mutant
@@ -1,7 +1,7 @@
1
1
  module Mutant
2
2
  # Abstract base class for mutant environments
3
3
  class Env
4
- include Adamantium, Concord::Public.new(:config, :cache), Procto.call(:run)
4
+ include Adamantium::Flat, Concord::Public.new(:config, :cache), Procto.call(:run)
5
5
 
6
6
  # Return new env
7
7
  #
@@ -27,6 +27,7 @@ module Mutant
27
27
  infect
28
28
  initialize_matchable_scopes
29
29
  initialize_subjects
30
+ initialize_mutations
30
31
  end
31
32
 
32
33
  # Run mutant producing a report on configured env
@@ -60,6 +61,14 @@ module Mutant
60
61
  #
61
62
  attr_reader :subjects
62
63
 
64
+ # Return mutations
65
+ #
66
+ # @return [Array<Mutation>]
67
+ #
68
+ # @api private
69
+ #
70
+ attr_reader :mutations
71
+
63
72
  # Return all usable match scopes
64
73
  #
65
74
  # @return [Array<Matcher::Scope>]
@@ -75,7 +84,7 @@ module Mutant
75
84
  # @param [Class, Module] scope
76
85
  #
77
86
  # @return [String]
78
- # if scope has a name and does not raise exceptions optaining it
87
+ # if scope has a name and does not raise exceptions obtaining it
79
88
  #
80
89
  # @return [nil]
81
90
  # otherwise
@@ -87,7 +96,7 @@ module Mutant
87
96
  def scope_name(scope)
88
97
  scope.name
89
98
  rescue => exception
90
- warn("While optaining #{scope.class}#name from: #{scope.inspect} It raised an error: #{exception.inspect} fix your lib!")
99
+ warn("While obtaining #{scope.class}#name from: #{scope.inspect} It raised an error: #{exception.inspect} fix your lib!")
91
100
  nil
92
101
  end
93
102
 
@@ -124,6 +133,16 @@ module Mutant
124
133
  @subjects = Matcher::Compiler.call(self, config.matcher_config).to_a
125
134
  end
126
135
 
136
+ # Initialize mutations
137
+ #
138
+ # @return [undefined]
139
+ #
140
+ # @api private
141
+ #
142
+ def initialize_mutations
143
+ @mutations = subjects.flat_map(&:mutations)
144
+ end
145
+
127
146
  # Infect environment
128
147
  #
129
148
  # @return [undefined]
@@ -2,7 +2,7 @@ module Mutant
2
2
 
3
3
  # Abstract base class for match expression
4
4
  class Expression
5
- include AbstractType, Adamantium, Concord::Public.new(:match)
5
+ include AbstractType, Adamantium::Flat, Concord::Public.new(:match)
6
6
 
7
7
  include Equalizer.new(:syntax)
8
8
 
@@ -13,6 +13,8 @@ module Mutant
13
13
  *AST::Types::OPERATOR_METHODS.map(&:to_s)
14
14
  ).freeze
15
15
 
16
+ INSPECT_FORMAT = '<Mutant::Expression: %s>'.freeze
17
+
16
18
  SCOPE_PATTERN = /#{SCOPE_NAME_PATTERN}(?:#{SCOPE_OPERATOR}#{SCOPE_NAME_PATTERN})*/.freeze
17
19
 
18
20
  REGISTRY = {}
@@ -20,8 +22,8 @@ module Mutant
20
22
  # Error raised on invalid expressions
21
23
  class InvalidExpressionError < RuntimeError; end
22
24
 
23
- # Error raised on ambigous expressions
24
- class AmbigousExpressionError < RuntimeError; end
25
+ # Error raised on ambiguous expressions
26
+ class AmbiguousExpressionError < RuntimeError; end
25
27
 
26
28
  # Initialize expression
27
29
  #
@@ -32,8 +34,17 @@ module Mutant
32
34
  def initialize(*)
33
35
  super
34
36
  @syntax = match.to_s
37
+ @inspect = format(INSPECT_FORMAT, syntax)
35
38
  end
36
39
 
40
+ # Return inspection
41
+ #
42
+ # @return [String]
43
+ #
44
+ # @api private
45
+ #
46
+ attr_reader :inspect
47
+
37
48
  # Return syntax
38
49
  #
39
50
  # @return [String]
@@ -116,7 +127,7 @@ module Mutant
116
127
  when 1
117
128
  expressions.first
118
129
  else
119
- fail AmbigousExpressionError, "Ambigous expression: #{input.inspect}"
130
+ fail AmbiguousExpressionError, "Ambiguous expression: #{input.inspect}"
120
131
  end
121
132
  end
122
133
 
@@ -16,7 +16,7 @@ module Mutant
16
16
  # @api private
17
17
  #
18
18
  def self.lookup(name)
19
- REGISTRY.fetch(name)
19
+ REGISTRY.fetch(name).build
20
20
  end
21
21
 
22
22
  # Register integration
@@ -1,5 +1,5 @@
1
1
  module Mutant
2
- # Module providing isolationg
2
+ # Module providing isolation
3
3
  module Isolation
4
4
  Error = Class.new(RuntimeError)
5
5
 
@@ -38,9 +38,7 @@ module Mutant
38
38
  # @api private
39
39
  #
40
40
  def self.call(&block)
41
- Parallel.map([block], in_processes: 1) do
42
- block.call
43
- end.first
41
+ Parallel.map([block], in_processes: 1, &block.method(:call)).first
44
42
  rescue Parallel::DeadWorker => exception
45
43
  fail Error, exception
46
44
  end
@@ -23,7 +23,7 @@ module Mutant
23
23
  # @return [self]
24
24
  # if block given
25
25
  #
26
- # @return [Enumerabe<Subject>]
26
+ # @return [Enumerable<Subject>]
27
27
  # otherwise
28
28
  #
29
29
  abstract_method :each
@@ -135,7 +135,7 @@ module Mutant
135
135
  # @api private
136
136
  #
137
137
  def matched_node
138
- Finder.run(ast) do |node|
138
+ AST.find_last(ast) do |node|
139
139
  match?(node)
140
140
  end
141
141
  end
@@ -80,7 +80,7 @@ module Mutant
80
80
  end
81
81
  end
82
82
 
83
- # Test if reciver name matches context
83
+ # Test if receiver name matches context
84
84
  #
85
85
  # @param [Parser::AST::Node] node
86
86
  #
@@ -63,8 +63,6 @@ module Mutant
63
63
 
64
64
  # Return candidate names
65
65
  #
66
- # @param [Object] object
67
- #
68
66
  # @return [Enumerable<Symbol>]
69
67
  #
70
68
  # @api private
@@ -1,5 +1,3 @@
1
- # encoding: UTF-8
2
-
3
1
  module Mutant
4
2
  module Meta
5
3
  class Example
@@ -78,7 +78,7 @@ module Mutant
78
78
  self
79
79
  end
80
80
 
81
- # Add singleotn mutations
81
+ # Add singleton mutations
82
82
  #
83
83
  # @return [undefined]
84
84
  #
@@ -69,7 +69,7 @@ module Mutant
69
69
  dispatch
70
70
  end
71
71
 
72
- # Test if generated object is not guarded from emmitting
72
+ # Test if generated object is not guarded from emitting
73
73
  #
74
74
  # @param [Object] object
75
75
  #
@@ -29,7 +29,7 @@ module Mutant
29
29
  emit_child_update(index, node)
30
30
  end
31
31
  end
32
- private_class_method :children
32
+ private_class_method :define_named_child
33
33
 
34
34
  private
35
35
 
@@ -176,7 +176,7 @@ module Mutant
176
176
  # Return parent node
177
177
  #
178
178
  # @return [Parser::AST::Node] node
179
- # if parent with node is presnet
179
+ # if parent with node is present
180
180
  #
181
181
  # @return [nil]
182
182
  # otherwise
@@ -190,7 +190,7 @@ module Mutant
190
190
  # Return parent type
191
191
  #
192
192
  # @return [Symbol] type
193
- # if parent with type is presnet
193
+ # if parent with type is present
194
194
  #
195
195
  # @return [nil]
196
196
  # otherwise
@@ -27,7 +27,7 @@ module Mutant
27
27
 
28
28
  # Emit child subset
29
29
  #
30
- # @param [Array<Parser::AST::Node>] nodes
30
+ # @param [Array<Parser::AST::Node>] children
31
31
  #
32
32
  # @return [undefined]
33
33
  #
@@ -19,12 +19,25 @@ module Mutant
19
19
  def dispatch
20
20
  emit_singletons
21
21
  emit(send)
22
+ emit_send_mutations(&method(:n_send?))
22
23
  emit_arguments_mutations
23
- if body
24
- emit_body_mutations
25
- end
24
+
25
+ mutate_body
26
+ end
27
+
28
+ # Emit body mutations
29
+ #
30
+ # @return [undefined]
31
+ #
32
+ # @api private
33
+ #
34
+ def mutate_body
26
35
  emit_body(nil)
27
36
  emit_body(N_RAISE)
37
+
38
+ return unless body
39
+ emit(body)
40
+ emit_body_mutations
28
41
  end
29
42
 
30
43
  end # Block
@@ -23,7 +23,7 @@ module Mutant
23
23
  mutate_else_branch
24
24
  end
25
25
 
26
- # Emit conditon mutations
26
+ # Emit condition mutations
27
27
  #
28
28
  # @return [undefined]
29
29
  #
@@ -40,7 +40,7 @@ module Mutant
40
40
  children.first
41
41
  end
42
42
 
43
- end # Fixnuma
43
+ end # Fixnum
44
44
  end # Literal
45
45
  end # Node
46
46
  end # Mutator
@@ -1,5 +1,3 @@
1
- # encoding: UTF-8
2
-
3
1
  module Mutant
4
2
  class Mutator
5
3
  class Node
@@ -11,13 +11,23 @@ module Mutant
11
11
  children :receiver, :selector
12
12
 
13
13
  SELECTOR_REPLACEMENTS = IceNine.deep_freeze(
14
- reverse_map: [:map, :each],
15
- reverse_each: [:each],
16
- map: [:each],
17
- send: [:public_send],
18
- gsub: [:sub],
19
- eql?: [:equal?],
20
- :== => [:eql?, :equal?]
14
+ reverse_map: [:map, :each],
15
+ kind_of?: [:instance_of?],
16
+ is_a?: [:instance_of?],
17
+ reverse_each: [:each],
18
+ reverse_merge: [:merge],
19
+ map: [:each],
20
+ send: [:public_send],
21
+ gsub: [:sub],
22
+ eql?: [:equal?],
23
+ to_s: [:to_str],
24
+ to_i: [:to_int],
25
+ to_a: [:to_ary],
26
+ :== => [:eql?, :equal?],
27
+ :>= => [:>, :==, :eql?, :equal?],
28
+ :<= => [:<, :==, :eql?, :equal?],
29
+ :> => [:==, :eql?, :equal?],
30
+ :< => [:==, :eql?, :equal?]
21
31
  )
22
32
 
23
33
  private
@@ -1,5 +1,3 @@
1
- # encoding: UTF-8
2
-
3
1
  module Mutant
4
2
  class Mutator
5
3
  class Node
@@ -74,7 +74,7 @@ module Mutant
74
74
  #
75
75
  # @return [undefined]
76
76
  #
77
- # @raise [DuplcateTypeError]
77
+ # @raise [DuplicateTypeError]
78
78
  # raised when the node type is a duplicate
79
79
  #
80
80
  # @api private
@@ -3,7 +3,7 @@ module Mutant
3
3
  # Namespace for utility mutators
4
4
  class Util < self
5
5
 
6
- # Run ulitity mutator
6
+ # Run utility mutator
7
7
  #
8
8
  # @param [Object] object
9
9
  # @param [Object] parent