mutant 0.9.8 → 0.9.9

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 (161) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -2
  3. data/Changelog.md +6 -0
  4. data/README.md +63 -23
  5. data/config/reek.yml +1 -0
  6. data/lib/mutant.rb +4 -0
  7. data/lib/mutant/ast.rb +0 -9
  8. data/lib/mutant/ast/find_metaclass_containing.rb +48 -0
  9. data/lib/mutant/ast/meta/send.rb +0 -6
  10. data/lib/mutant/bootstrap.rb +0 -36
  11. data/lib/mutant/cli.rb +5 -49
  12. data/lib/mutant/color.rb +0 -3
  13. data/lib/mutant/config.rb +0 -8
  14. data/lib/mutant/context.rb +0 -3
  15. data/lib/mutant/diff.rb +0 -17
  16. data/lib/mutant/env.rb +0 -6
  17. data/lib/mutant/expression/method.rb +6 -6
  18. data/lib/mutant/expression/methods.rb +6 -6
  19. data/lib/mutant/expression/parser.rb +0 -6
  20. data/lib/mutant/integration.rb +0 -18
  21. data/lib/mutant/isolation/fork.rb +0 -22
  22. data/lib/mutant/license.rb +11 -0
  23. data/lib/mutant/matcher.rb +0 -14
  24. data/lib/mutant/matcher/config.rb +0 -11
  25. data/lib/mutant/matcher/method.rb +0 -31
  26. data/lib/mutant/matcher/method/instance.rb +0 -8
  27. data/lib/mutant/matcher/method/metaclass.rb +86 -0
  28. data/lib/mutant/matcher/method/singleton.rb +0 -25
  29. data/lib/mutant/matcher/methods.rb +17 -28
  30. data/lib/mutant/matcher/namespace.rb +0 -10
  31. data/lib/mutant/matcher/scope.rb +2 -4
  32. data/lib/mutant/meta/example/dsl.rb +0 -21
  33. data/lib/mutant/meta/example/verification.rb +0 -20
  34. data/lib/mutant/mutation.rb +0 -3
  35. data/lib/mutant/mutator.rb +1 -29
  36. data/lib/mutant/mutator/node.rb +1 -66
  37. data/lib/mutant/mutator/node/and_asgn.rb +0 -3
  38. data/lib/mutant/mutator/node/argument.rb +0 -15
  39. data/lib/mutant/mutator/node/arguments.rb +0 -20
  40. data/lib/mutant/mutator/node/begin.rb +0 -3
  41. data/lib/mutant/mutator/node/binary.rb +0 -23
  42. data/lib/mutant/mutator/node/block.rb +0 -15
  43. data/lib/mutant/mutator/node/break.rb +0 -3
  44. data/lib/mutant/mutator/node/case.rb +0 -9
  45. data/lib/mutant/mutator/node/class.rb +0 -3
  46. data/lib/mutant/mutator/node/conditional_loop.rb +0 -3
  47. data/lib/mutant/mutator/node/const.rb +0 -3
  48. data/lib/mutant/mutator/node/define.rb +0 -11
  49. data/lib/mutant/mutator/node/defined.rb +0 -3
  50. data/lib/mutant/mutator/node/dstr.rb +0 -3
  51. data/lib/mutant/mutator/node/dsym.rb +0 -3
  52. data/lib/mutant/mutator/node/generic.rb +0 -3
  53. data/lib/mutant/mutator/node/if.rb +0 -12
  54. data/lib/mutant/mutator/node/index.rb +0 -27
  55. data/lib/mutant/mutator/node/kwbegin.rb +0 -3
  56. data/lib/mutant/mutator/node/literal.rb +0 -3
  57. data/lib/mutant/mutator/node/literal/array.rb +0 -6
  58. data/lib/mutant/mutator/node/literal/boolean.rb +0 -4
  59. data/lib/mutant/mutator/node/literal/float.rb +0 -9
  60. data/lib/mutant/mutator/node/literal/hash.rb +0 -9
  61. data/lib/mutant/mutator/node/literal/integer.rb +0 -9
  62. data/lib/mutant/mutator/node/literal/nil.rb +0 -3
  63. data/lib/mutant/mutator/node/literal/range.rb +0 -6
  64. data/lib/mutant/mutator/node/literal/regex.rb +0 -6
  65. data/lib/mutant/mutator/node/literal/string.rb +0 -3
  66. data/lib/mutant/mutator/node/literal/symbol.rb +0 -3
  67. data/lib/mutant/mutator/node/masgn.rb +0 -3
  68. data/lib/mutant/mutator/node/match_current_line.rb +0 -3
  69. data/lib/mutant/mutator/node/mlhs.rb +0 -3
  70. data/lib/mutant/mutator/node/named_value/access.rb +2 -14
  71. data/lib/mutant/mutator/node/named_value/constant_assignment.rb +0 -9
  72. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +0 -6
  73. data/lib/mutant/mutator/node/next.rb +0 -3
  74. data/lib/mutant/mutator/node/noop.rb +0 -3
  75. data/lib/mutant/mutator/node/nthref.rb +0 -3
  76. data/lib/mutant/mutator/node/op_asgn.rb +0 -3
  77. data/lib/mutant/mutator/node/or_asgn.rb +0 -3
  78. data/lib/mutant/mutator/node/procarg_zero.rb +0 -3
  79. data/lib/mutant/mutator/node/regopt.rb +0 -6
  80. data/lib/mutant/mutator/node/resbody.rb +0 -6
  81. data/lib/mutant/mutator/node/rescue.rb +2 -19
  82. data/lib/mutant/mutator/node/return.rb +0 -3
  83. data/lib/mutant/mutator/node/sclass.rb +20 -0
  84. data/lib/mutant/mutator/node/send.rb +2 -61
  85. data/lib/mutant/mutator/node/send/attribute_assignment.rb +0 -9
  86. data/lib/mutant/mutator/node/send/binary.rb +0 -11
  87. data/lib/mutant/mutator/node/send/conditional.rb +0 -3
  88. data/lib/mutant/mutator/node/splat.rb +0 -3
  89. data/lib/mutant/mutator/node/super.rb +0 -3
  90. data/lib/mutant/mutator/node/when.rb +0 -19
  91. data/lib/mutant/mutator/node/yield.rb +0 -3
  92. data/lib/mutant/mutator/node/zsuper.rb +0 -3
  93. data/lib/mutant/mutator/util/array.rb +0 -6
  94. data/lib/mutant/mutator/util/symbol.rb +0 -3
  95. data/lib/mutant/parallel.rb +0 -13
  96. data/lib/mutant/parallel/driver.rb +0 -10
  97. data/lib/mutant/parallel/worker.rb +0 -22
  98. data/lib/mutant/reporter/cli.rb +0 -5
  99. data/lib/mutant/reporter/cli/format.rb +0 -9
  100. data/lib/mutant/reporter/cli/printer.rb +0 -40
  101. data/lib/mutant/reporter/cli/printer/env_progress.rb +0 -15
  102. data/lib/mutant/reporter/cli/printer/isolation_result.rb +0 -18
  103. data/lib/mutant/reporter/cli/printer/mutation_progress_result.rb +0 -5
  104. data/lib/mutant/reporter/cli/printer/mutation_result.rb +0 -21
  105. data/lib/mutant/reporter/cli/printer/status_progressive.rb +0 -8
  106. data/lib/mutant/reporter/cli/printer/subject_progress.rb +0 -9
  107. data/lib/mutant/repository/diff.rb +1 -13
  108. data/lib/mutant/repository/diff/ranges.rb +0 -11
  109. data/lib/mutant/result.rb +0 -3
  110. data/lib/mutant/runner.rb +0 -18
  111. data/lib/mutant/runner/sink.rb +0 -5
  112. data/lib/mutant/subject.rb +0 -8
  113. data/lib/mutant/subject/method.rb +0 -3
  114. data/lib/mutant/subject/method/instance.rb +0 -5
  115. data/lib/mutant/subject/method/metaclass.rb +30 -0
  116. data/lib/mutant/transform.rb +0 -92
  117. data/lib/mutant/version.rb +1 -1
  118. data/lib/mutant/warnings.rb +0 -6
  119. data/lib/mutant/zombifier.rb +2 -34
  120. data/meta/and.rb +0 -2
  121. data/meta/array.rb +0 -3
  122. data/meta/begin.rb +0 -3
  123. data/meta/block.rb +0 -3
  124. data/meta/break.rb +0 -1
  125. data/meta/case.rb +0 -6
  126. data/meta/casgn.rb +0 -3
  127. data/meta/cvasgn.rb +0 -1
  128. data/meta/def.rb +0 -7
  129. data/meta/ensure.rb +0 -1
  130. data/meta/false.rb +0 -1
  131. data/meta/gvasgn.rb +0 -1
  132. data/meta/hash.rb +0 -4
  133. data/meta/if.rb +0 -5
  134. data/meta/ivasgn.rb +0 -1
  135. data/meta/kwbegin.rb +0 -1
  136. data/meta/lvasgn.rb +0 -1
  137. data/meta/match_current_line.rb +0 -1
  138. data/meta/next.rb +0 -1
  139. data/meta/or.rb +0 -2
  140. data/meta/regexp.rb +0 -1
  141. data/meta/rescue.rb +0 -6
  142. data/meta/sclass.rb +12 -0
  143. data/meta/send.rb +0 -4
  144. data/meta/true.rb +0 -1
  145. data/meta/until.rb +0 -1
  146. data/meta/while.rb +0 -2
  147. data/meta/yield.rb +0 -1
  148. data/mutant.sh +12 -0
  149. data/spec/unit/mutant/ast/find_metaclass_containing_spec.rb +64 -0
  150. data/spec/unit/mutant/expression/methods_spec.rb +7 -2
  151. data/spec/unit/mutant/license_spec.rb +15 -3
  152. data/spec/unit/mutant/matcher/method/metaclass_spec.rb +108 -0
  153. data/spec/unit/mutant/matcher/methods/metaclass_spec.rb +62 -0
  154. data/spec/unit/mutant/matcher/namespace_spec.rb +3 -1
  155. data/spec/unit/mutant/matcher/scope_spec.rb +11 -1
  156. data/spec/unit/mutant/meta/example_spec.rb +3 -3
  157. data/spec/unit/mutant/mutator/node_spec.rb +1 -6
  158. data/spec/unit/mutant/subject/method/metaclass_spec.rb +63 -0
  159. data/test_app/lib/test_app.rb +1 -0
  160. data/test_app/lib/test_app/metaclasses.rb +108 -0
  161. metadata +17 -2
@@ -28,9 +28,6 @@ module Mutant
28
28
 
29
29
  private
30
30
 
31
- # Initialize null color
32
- #
33
- # @return [undefined]
34
31
  def initialize; end
35
32
 
36
33
  end.new
@@ -126,14 +126,6 @@ module Mutant
126
126
  end
127
127
  end
128
128
 
129
- # Load contents of file
130
- #
131
- # @param [Pathname] path
132
- #
133
- # @return [Config]
134
- #
135
- # @raise [Either<String, Hash{Symbol => Object}>]
136
- # in case of config file error
137
129
  def self.load_contents(path)
138
130
  Transform::Named
139
131
  .new(path.to_s, TRANSFORM)
@@ -81,9 +81,6 @@ module Mutant
81
81
 
82
82
  private
83
83
 
84
- # Nesting of names in scope
85
- #
86
- # @return [Array<String>]
87
84
  def name_nesting
88
85
  scope.name.split(NAMESPACE_DELIMITER)
89
86
  end
@@ -58,25 +58,16 @@ module Mutant
58
58
 
59
59
  private
60
60
 
61
- # Diffs between old and new
62
- #
63
- # @return [Array<Array>]
64
61
  def diffs
65
62
  ::Diff::LCS.diff(old, new)
66
63
  end
67
64
 
68
- # Raw diff-lcs hunks
69
- #
70
- # @return [Array<Diff::LCS::Hunk>]
71
65
  def hunks
72
66
  diffs.map do |diff|
73
67
  ::Diff::LCS::Hunk.new(old.map(&:dup), new, diff, max_length, 0)
74
68
  end
75
69
  end
76
70
 
77
- # Minimized hunk
78
- #
79
- # @return Diff::LCS::Hunk
80
71
  def minimized_hunk
81
72
  head, *tail = hunks
82
73
 
@@ -86,18 +77,10 @@ module Mutant
86
77
  end
87
78
  end
88
79
 
89
- # Max length of source line in new and old
90
- #
91
- # @return [Integer]
92
80
  def max_length
93
81
  [old, new].map(&:length).max
94
82
  end
95
83
 
96
- # Colorized a unified diff line
97
- #
98
- # @param [String] line
99
- #
100
- # @return [String]
101
84
  def self.colorize_line(line)
102
85
  case line[0]
103
86
  when ADDITION
@@ -126,12 +126,6 @@ module Mutant
126
126
 
127
127
  private
128
128
 
129
- # Kill mutation under isolation with integration
130
- #
131
- # @param [Mutation] mutation
132
- # @param [Array<Test>] test
133
- #
134
- # @return [Result::Isolation]
135
129
  def run_mutation_tests(mutation, tests)
136
130
  config.isolation.call do
137
131
  result = mutation.insert(world.kernel)
@@ -14,8 +14,8 @@ module Mutant
14
14
  private(*anima.attribute_names)
15
15
 
16
16
  MATCHERS = IceNine.deep_freeze(
17
- '.' => Matcher::Methods::Singleton,
18
- '#' => Matcher::Methods::Instance
17
+ '.' => [Matcher::Methods::Singleton, Matcher::Methods::Metaclass],
18
+ '#' => [Matcher::Methods::Instance]
19
19
  )
20
20
 
21
21
  METHOD_NAME_PATTERN = Regexp.union(
@@ -39,16 +39,16 @@ module Mutant
39
39
  #
40
40
  # @return [Matcher]
41
41
  def matcher
42
- methods_matcher = MATCHERS.fetch(scope_symbol).new(scope)
42
+ matcher_candidates = MATCHERS.fetch(scope_symbol)
43
+ .map { |submatcher| submatcher.new(scope) }
44
+
45
+ methods_matcher = Matcher::Chain.new(matcher_candidates)
43
46
 
44
47
  Matcher::Filter.new(methods_matcher, ->(subject) { subject.expression.eql?(self) })
45
48
  end
46
49
 
47
50
  private
48
51
 
49
- # Scope object
50
- #
51
- # @return [Class, Method]
52
52
  def scope
53
53
  Object.const_get(scope_name)
54
54
  end
@@ -13,8 +13,8 @@ module Mutant
13
13
  private(*anima.attribute_names)
14
14
 
15
15
  MATCHERS = IceNine.deep_freeze(
16
- '.' => Matcher::Methods::Singleton,
17
- '#' => Matcher::Methods::Instance
16
+ '.' => [Matcher::Methods::Singleton, Matcher::Methods::Metaclass],
17
+ '#' => [Matcher::Methods::Instance]
18
18
  )
19
19
  private_constant(*constants(false))
20
20
 
@@ -32,7 +32,10 @@ module Mutant
32
32
  #
33
33
  # @return [Matcher::Method]
34
34
  def matcher
35
- MATCHERS.fetch(scope_symbol).new(scope)
35
+ matcher_candidates = MATCHERS.fetch(scope_symbol)
36
+ .map { |submatcher| submatcher.new(scope) }
37
+
38
+ Matcher::Chain.new(matcher_candidates)
36
39
  end
37
40
 
38
41
  # Length of match with other expression
@@ -50,9 +53,6 @@ module Mutant
50
53
 
51
54
  private
52
55
 
53
- # Scope object
54
- #
55
- # @return [Class, Method]
56
56
  def scope
57
57
  Object.const_get(scope_name)
58
58
  end
@@ -28,12 +28,6 @@ module Mutant
28
28
 
29
29
  private
30
30
 
31
- # Expressions parsed from input
32
- #
33
- # @param [String] input
34
- #
35
- # @return [Array<Expression>]
36
- # if expressions can be parsed from input
37
31
  def expressions(input)
38
32
  types.each_with_object([]) do |type, aggregate|
39
33
  expression = type.try_parse(input) and aggregate << expression
@@ -32,14 +32,6 @@ module Mutant
32
32
  .fmap { |klass| klass.new(env.config).setup }
33
33
  end
34
34
 
35
- # Attempt to require integration
36
- #
37
- # @param env [Bootstrap]
38
- #
39
- # @return [Either<String, undefined>]
40
- #
41
- # @api private
42
- #
43
35
  # rubocop:disable Style/MultilineBlockChain
44
36
  def self.attempt_require(env)
45
37
  integration_name = env.config.integration
@@ -56,13 +48,6 @@ module Mutant
56
48
  private_class_method :attempt_require
57
49
  # rubocop:enable Style/MultilineBlockChain
58
50
 
59
- # Attempt const get
60
- #
61
- # @param env [Boostrap]
62
- #
63
- # @return [Either<String, Class<Integration>>]
64
- #
65
- # @api private
66
51
  def self.attempt_const_get(env)
67
52
  integration_name = env.config.integration
68
53
  constant_name = integration_name.capitalize
@@ -98,9 +83,6 @@ module Mutant
98
83
 
99
84
  private
100
85
 
101
- # Expression parser
102
- #
103
- # @return [Expression::Parser]
104
86
  def expression_parser
105
87
  config.expression_parser
106
88
  end
@@ -73,9 +73,6 @@ module Mutant
73
73
 
74
74
  private
75
75
 
76
- # Start child process
77
- #
78
- # @return [Integer]
79
76
  def start_child
80
77
  world.process.fork do
81
78
  Child.call(
@@ -87,12 +84,6 @@ module Mutant
87
84
  end
88
85
  end
89
86
 
90
- # Read child result
91
- #
92
- # @param [Integer] pid
93
- #
94
- # @return [undefined]
95
- #
96
87
  # rubocop:disable Metrics/MethodLength
97
88
  def read_child_result(pid)
98
89
  result_fragments = []
@@ -115,11 +106,6 @@ module Mutant
115
106
  end
116
107
  # rubocop:enable Metrics/MethodLength
117
108
 
118
- # Read fragments
119
- #
120
- # @param [Hash{FD => Array<String}] targets
121
- #
122
- # @return [undefined]
123
109
  def read_fragments(targets)
124
110
  until targets.empty?
125
111
  ready, = world.io.select(targets.keys)
@@ -134,11 +120,6 @@ module Mutant
134
120
  end
135
121
  end
136
122
 
137
- # Wait for child process
138
- #
139
- # @param [Integer] pid
140
- #
141
- # @return [undefined]
142
123
  def wait_child(pid, log_fragments)
143
124
  _pid, status = world.process.wait2(pid)
144
125
 
@@ -147,9 +128,6 @@ module Mutant
147
128
  end
148
129
  end
149
130
 
150
- # Add a result
151
- #
152
- # @param [Result]
153
131
  def add_result(result)
154
132
  @result = defined?(@result) ? @result.add_error(result) : result
155
133
  end
@@ -43,6 +43,8 @@ module Mutant
43
43
  def self.load_mutant_license(world)
44
44
  Either
45
45
  .wrap_error(LoadError) { world.gem_method.call(NAME, VERSION) }
46
+ .lmap(&:message)
47
+ .lmap(&method(:check_for_rubygems_mutant_license))
46
48
  .lmap(&method(:unlicensed))
47
49
  end
48
50
 
@@ -50,6 +52,15 @@ module Mutant
50
52
  "[Mutant-License-Error]: #{message}"
51
53
  end
52
54
 
55
+ def self.check_for_rubygems_mutant_license(message)
56
+ if message.include?('already activated mutant-license-0.0.0')
57
+ 'mutant-license gem from rubygems.org is a dummy'
58
+ else
59
+ message
60
+ end
61
+ end
62
+ private_class_method :check_for_rubygems_mutant_license
63
+
53
64
  def self.license_path(world)
54
65
  world
55
66
  .pathname
@@ -25,25 +25,11 @@ module Mutant
25
25
  )
26
26
  end
27
27
 
28
- # Test if subject is allowed do
29
- #
30
- # @param [Config] config
31
- # @param [Subject] subject
32
- #
33
- # @return [Boolean]
34
28
  def self.allowed_subject?(config, subject)
35
29
  select_subject?(config, subject) && !ignore_subject?(config, subject)
36
30
  end
37
31
  private_class_method :allowed_subject?
38
32
 
39
- # Predicate that tests for selected subject
40
- #
41
- # @param [Config] config
42
- # @param [Subject] subject
43
- #
44
- # @return [Boolean]
45
- #
46
- # @api private
47
33
  def self.select_subject?(config, subject)
48
34
  config.subject_filters.all? { |filter| filter.call(subject) }
49
35
  end
@@ -44,16 +44,10 @@ module Mutant
44
44
 
45
45
  private
46
46
 
47
- # Present attributes
48
- #
49
- # @return [Array<Symbol>]
50
47
  def present_attributes
51
48
  to_h.reject { |_key, value| value.empty? }.keys
52
49
  end
53
50
 
54
- # Formatted attributes
55
- #
56
- # @return [String]
57
51
  def inspect_attributes
58
52
  attributes = present_attributes
59
53
  .map(&method(:format_attribute))
@@ -62,11 +56,6 @@ module Mutant
62
56
  attributes.empty? ? EMPTY_ATTRIBUTES : attributes
63
57
  end
64
58
 
65
- # Format attribute
66
- #
67
- # @param [Symbol] attribute_name
68
- #
69
- # @return [String]
70
59
  def format_attribute(attribute_name)
71
60
  ATTRIBUTE_FORMAT %
72
61
  [
@@ -49,9 +49,6 @@ module Mutant
49
49
 
50
50
  private
51
51
 
52
- # Test if method should be skipped
53
- #
54
- # @return [Truthy]
55
52
  def skip?
56
53
  location = source_location
57
54
  if location.nil? || BLACKLIST.include?(location.first)
@@ -61,56 +58,31 @@ module Mutant
61
58
  end
62
59
  end
63
60
 
64
- # Target method name
65
- #
66
- # @return [String]
67
61
  def method_name
68
62
  target_method.name
69
63
  end
70
64
 
71
- # Target context
72
- #
73
- # @return [Context]
74
65
  def context
75
66
  Context.new(scope, source_path)
76
67
  end
77
68
 
78
- # Root source node
79
- #
80
- # @return [Parser::AST::Node]
81
69
  def ast
82
70
  env.parser.call(source_path)
83
71
  end
84
72
 
85
- # Path to source
86
- #
87
- # @return [Pathname]
88
73
  def source_path
89
74
  env.world.pathname.new(source_location.first)
90
75
  end
91
76
  memoize :source_path
92
77
 
93
- # Source file line
94
- #
95
- # @return [Integer]
96
78
  def source_line
97
79
  source_location.last
98
80
  end
99
81
 
100
- # Full source location
101
- #
102
- # @return [Array{String,Integer}]
103
82
  def source_location
104
83
  target_method.source_location
105
84
  end
106
85
 
107
- # Matched subject
108
- #
109
- # @return [Subject]
110
- # if there is a matched node
111
- #
112
- # @return [nil]
113
- # otherwise
114
86
  def subject
115
87
  node = matched_node_path.last || return
116
88
 
@@ -122,9 +94,6 @@ module Mutant
122
94
  end
123
95
  memoize :subject
124
96
 
125
- # Matched node path
126
- #
127
- # @return [Array<Parser::AST::Node>]
128
97
  def matched_node_path
129
98
  AST.find_last_path(ast, &method(:match?))
130
99
  end
@@ -33,11 +33,6 @@ module Mutant
33
33
 
34
34
  private
35
35
 
36
- # Check if node is matched
37
- #
38
- # @param [Parser::AST::Node] node
39
- #
40
- # @return [Boolean]
41
36
  def match?(node)
42
37
  n_def?(node) &&
43
38
  node.location.line.equal?(source_line) &&
@@ -50,9 +45,6 @@ module Mutant
50
45
 
51
46
  private
52
47
 
53
- # Source location
54
- #
55
- # @return [Array{String,Integer}]
56
48
  def source_location
57
49
  scope
58
50
  .unmemoized_instance_method(method_name)