mutant 0.5.26 → 0.6.0

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