mutant 0.11.3 → 0.11.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4fd616c16c6eb981572f300d63760de29c418e0c8c72b29b0462565aeec59271
4
- data.tar.gz: cff1c3128b75347267692919f598642dc6906faff02253b8bb5cf0a738b7488e
3
+ metadata.gz: 945d6584e161b704f52d2f1efc3b6a78df03cc0f6b48d52f157bf47720623725
4
+ data.tar.gz: 7a507de1188d5d8a89740730d827c89f818dee72b844f3bccfa97264d1c1e266
5
5
  SHA512:
6
- metadata.gz: 0a1fe601d15da1235e4384c15346f3080021b8f08dfdd2313f60882f722d45bc824d8099fd13cebe77ea97edd06ecba0bbd2ff64ede9b7f5765e2115652f4202
7
- data.tar.gz: b61a27b8b5119e489908bb43aa4aec0a348e552b443fa3b937ed20718f436e77740b0b891fc731fd1126eb5170525ffa7388d4ab555f9a7ef7e33812141b4ec7
6
+ metadata.gz: f37bb36a48e29385936390ebd92790051871b6ee9b5fe053c1249815ab4432db1dbe7633e236d5bf40c04a488c1f0288bca9e29b8282f7c8a4bd2bfe0da5ee15
7
+ data.tar.gz: 281fc2061663198b9676c1000793104c7bb52fed3f4892b02419bd7a7aaf2a7168f23fb1c7ba0653710900cd7bf2637de99998da37b87cac1ca021a2dd10b0d8
@@ -110,9 +110,9 @@ module Mutant
110
110
  #
111
111
  # @return [Table]
112
112
  def self.create(*rows)
113
- table = rows.map do |ast_type, token, klass|
113
+ table = rows.to_h do |ast_type, token, klass|
114
114
  [ast_type, Mapping.new(::Regexp::Token.new(*token), klass)]
115
- end.to_h
115
+ end
116
116
 
117
117
  new(table)
118
118
  end
@@ -7,13 +7,13 @@ module Mutant
7
7
  class Run < self
8
8
  NAME = 'run'
9
9
  SHORT_DESCRIPTION = 'Run code analysis'
10
- SLEEP = 60
11
10
  SUBCOMMANDS = EMPTY_ARRAY
12
11
 
13
12
  UNLICENSED = <<~MESSAGE.lines.freeze
14
- Soft fail, continuing in #{SLEEP} seconds
15
- Next major version will enforce the license
16
- See https://github.com/mbj/mutant#licensing
13
+ You are using mutant unlicensed.
14
+
15
+ See https://github.com/mbj/mutant#licensing to aquire a license.
16
+ Note: Its free for opensource use, which is recommended for trials.
17
17
  MESSAGE
18
18
 
19
19
  NO_TESTS_MESSAGE = <<~'MESSAGE'
@@ -40,7 +40,7 @@ module Mutant
40
40
  private
41
41
 
42
42
  def action
43
- soft_fail(License.call(world))
43
+ License.call(world)
44
44
  .bind { bootstrap }
45
45
  .bind(&method(:validate_tests))
46
46
  .bind(&Runner.public_method(:call))
@@ -62,23 +62,6 @@ module Mutant
62
62
  Either::Left.new('Uncovered mutations detected, exiting nonzero!')
63
63
  end
64
64
  end
65
-
66
- def soft_fail(result)
67
- result.either(
68
- lambda do |message|
69
- stderr = world.stderr
70
- stderr.puts(message)
71
- UNLICENSED.each { |line| stderr.puts(unlicensed(line)) }
72
- world.kernel.sleep(SLEEP)
73
- Either::Right.new(nil)
74
- end,
75
- ->(_subscription) { Either::Right.new(nil) }
76
- )
77
- end
78
-
79
- def unlicensed(message)
80
- "[Mutant-License-Error]: #{message}"
81
- end
82
65
  end # Run
83
66
  end # Environment
84
67
  end # Command
@@ -184,9 +184,9 @@ module Mutant
184
184
  end
185
185
 
186
186
  def format_subcommands
187
- commands = subcommands.map do |subcommand|
187
+ commands = subcommands.to_h do |subcommand|
188
188
  [subcommand.command_name, subcommand.short_description]
189
- end.to_h
189
+ end
190
190
 
191
191
  width = commands.each_key.map(&:length).max
192
192
 
data/lib/mutant/env.rb CHANGED
@@ -68,9 +68,9 @@ module Mutant
68
68
  #
69
69
  # @return Hash{Mutation => Enumerable<Test>}
70
70
  def selections
71
- subjects.map do |subject|
71
+ subjects.to_h do |subject|
72
72
  [subject, selector.call(subject)]
73
- end.to_h
73
+ end
74
74
  end
75
75
  memoize :selections
76
76
 
@@ -142,11 +142,10 @@ module Mutant
142
142
  result = mutation.insert(world.kernel)
143
143
  hooks.run(:mutation_insert_post, mutation)
144
144
 
145
- if result.equal?(Loader::Result::VoidValue.instance)
146
- Result::Test::VoidValue.instance
147
- else
148
- integration.call(tests)
149
- end
145
+ result.either(
146
+ ->(_) { Result::Test::VoidValue.instance },
147
+ ->(_) { integration.call(tests) }
148
+ )
150
149
  end
151
150
  end
152
151
 
@@ -36,7 +36,7 @@ module Mutant
36
36
  fail "Unmatched git remote URL: #{input.inspect}"
37
37
  end
38
38
 
39
- new(match[:host], match[:path])
39
+ new(match[:host], match[:path].downcase)
40
40
  end
41
41
  private_class_method :parse_url
42
42
  end
data/lib/mutant/loader.rb CHANGED
@@ -9,17 +9,8 @@ module Mutant
9
9
 
10
10
  private_constant(*constants(false))
11
11
 
12
- class Result
13
- include Singleton
14
-
15
- # Vale returned on successful load
16
- class Success < self
17
- end # Success
18
-
19
- # Vale returned on MRI detecting void value expressions
20
- class VoidValue < self
21
- end # VoidValue
22
- end # Result
12
+ VOID_VALUE = Either::Left.new(nil)
13
+ SUCCESS = Either::Right.new(nil)
23
14
 
24
15
  # Call loader
25
16
  #
@@ -45,12 +36,12 @@ module Mutant
45
36
  rescue SyntaxError => exception
46
37
  # rubocop:disable Style/GuardClause
47
38
  if VOID_VALUE_REGEXP.match?(exception.message)
48
- Result::VoidValue.instance
39
+ VOID_VALUE
49
40
  else
50
41
  raise
51
42
  end
52
43
  else
53
- Result::Success.instance
44
+ SUCCESS
54
45
  end
55
46
  end # Loader
56
47
  end # Mutant
@@ -25,7 +25,7 @@ module Mutant
25
25
 
26
26
  private_constant(*constants(false))
27
27
 
28
- DEFAULT = new(anima.attribute_names.map { |name| [name, []] }.to_h)
28
+ DEFAULT = new(anima.attribute_names.to_h { |name| [name, []] })
29
29
 
30
30
  expression = Transform::Block.capture(:expression) do |input|
31
31
  Mutant::Config::DEFAULT.expression_parser.call(input)
@@ -74,8 +74,7 @@ module Mutant
74
74
  def merge(other)
75
75
  self.class.new(
76
76
  to_h
77
- .map { |name, value| [name, value + other.public_send(name)] }
78
- .to_h
77
+ .to_h { |name, value| [name, value + other.public_send(name)] }
79
78
  )
80
79
  end
81
80
 
@@ -41,6 +41,16 @@ module Mutant
41
41
  node.children.fetch(NAME_INDEX).equal?(method_name)
42
42
  end
43
43
 
44
+ def visibility
45
+ if scope.private_instance_methods.include?(method_name)
46
+ :private
47
+ elsif scope.protected_instance_methods.include?(method_name)
48
+ :protected
49
+ else
50
+ :public
51
+ end
52
+ end
53
+
44
54
  # Evaluator specialized for memoized instance methods
45
55
  class Memoized < self
46
56
  SUBJECT_CLASS = Subject::Method::Instance::Memoized
@@ -99,16 +99,40 @@ module Mutant
99
99
  node = matched_node_path.last || return
100
100
 
101
101
  self.class::SUBJECT_CLASS.new(
102
- context: context,
103
- node: node
102
+ context: context,
103
+ node: node,
104
+ visibility: visibility
104
105
  )
105
106
  end
106
- memoize :subject
107
107
 
108
108
  def matched_node_path
109
109
  AST.find_last_path(ast, &method(:match?))
110
110
  end
111
111
  memoize :matched_node_path
112
+
113
+ def visibility
114
+ # This can be cleaned up once we are on >ruby-3.0
115
+ # Method#{public,private,protected}? exists there.
116
+ #
117
+ # On Ruby 3.1 this can just be:
118
+ #
119
+ # if target_method.private?
120
+ # :private
121
+ # elsif target_method.protected?
122
+ # :protected
123
+ # else
124
+ # :public
125
+ # end
126
+ #
127
+ # Change to this once 3.0 is EOL.
128
+ if scope.private_methods.include?(method_name)
129
+ :private
130
+ elsif scope.protected_methods.include?(method_name)
131
+ :protected
132
+ else
133
+ :public
134
+ end
135
+ end
112
136
  end # Evaluator
113
137
 
114
138
  private_constant(*constants(false))
@@ -93,7 +93,9 @@ module Mutant
93
93
 
94
94
  Object: %<scope>s
95
95
  Method: %<method_name>s
96
- Exception: %<exception>s
96
+ Exception:
97
+
98
+ %<exception>s
97
99
 
98
100
  See: https://github.com/mbj/mutant/issues/1273
99
101
  MESSAGE
@@ -7,7 +7,7 @@ module Mutant
7
7
  include Concord::Public.new(:subject, :node)
8
8
 
9
9
  CODE_DELIMITER = "\0"
10
- CODE_RANGE = (0..4).freeze
10
+ CODE_RANGE = (..4).freeze
11
11
 
12
12
  # Mutation identification code
13
13
  #
@@ -69,7 +69,10 @@ module Mutant
69
69
  kernel: kernel,
70
70
  source: monkeypatch,
71
71
  subject: subject
72
- )
72
+ ).fmap do
73
+ subject.post_insert
74
+ nil
75
+ end
73
76
  end
74
77
 
75
78
  # Rendered mutation diff
@@ -6,6 +6,8 @@ module Mutant
6
6
  # Mutator for arguments node
7
7
  class Arguments < self
8
8
 
9
+ ANONYMOUS_BLOCKARG_PRED = ::Parser::AST::Node.new(:blockarg, [nil]).method(:eql?)
10
+
9
11
  handle(:args)
10
12
 
11
13
  private
@@ -17,15 +19,34 @@ module Mutant
17
19
  end
18
20
 
19
21
  def emit_argument_presence
20
- emit_type
22
+ emit_type unless removed_block_arg?(EMPTY_ARRAY) || forward_arg?
21
23
 
22
- Util::Array::Presence.call(children).each do |children|
23
- unless children.one? && n_mlhs?(children.first)
24
- emit_type(*children)
24
+ children.each_with_index do |removed, index|
25
+ new_arguments = children.dup
26
+ new_arguments.delete_at(index)
27
+ unless n_forward_arg?(removed) || removed_block_arg?(new_arguments) || only_mlhs?(new_arguments)
28
+ emit_type(*new_arguments)
25
29
  end
26
30
  end
27
31
  end
28
32
 
33
+ def only_mlhs?(new_arguments)
34
+ new_arguments.one? && n_mlhs?(new_arguments.first)
35
+ end
36
+
37
+ def forward_arg?
38
+ children.last && n_forward_arg?(children.last)
39
+ end
40
+
41
+ def removed_block_arg?(new_arguments)
42
+ anonymous_block_arg? && new_arguments.none?(&ANONYMOUS_BLOCKARG_PRED)
43
+ end
44
+
45
+ def anonymous_block_arg?
46
+ children.any?(&ANONYMOUS_BLOCKARG_PRED)
47
+ end
48
+ memoize :anonymous_block_arg?
49
+
29
50
  def emit_argument_mutations
30
51
  children.each_with_index do |child, index|
31
52
  Mutator.mutate(child).each do |mutant|
@@ -36,7 +57,7 @@ module Mutant
36
57
  end
37
58
 
38
59
  def invalid_argument_replacement?(mutant, index)
39
- n_arg?(mutant) && children[0...index].any?(&method(:n_optarg?))
60
+ n_arg?(mutant) && children[...index].any?(&method(:n_optarg?))
40
61
  end
41
62
 
42
63
  def emit_mlhs_expansion
@@ -12,6 +12,7 @@ module Mutant
12
12
  private
13
13
 
14
14
  def dispatch
15
+ return unless argument
15
16
  emit_argument_mutations
16
17
  emit_symbol_to_proc_mutations
17
18
  end
@@ -7,7 +7,7 @@ module Mutant
7
7
  # Mutator for attribute assignments
8
8
  class AttributeAssignment < self
9
9
 
10
- ATTRIBUTE_RANGE = (0..-2).freeze
10
+ ATTRIBUTE_RANGE = (..-2).freeze
11
11
 
12
12
  private_constant(*constants(false))
13
13
 
@@ -241,7 +241,9 @@ module Mutant
241
241
 
242
242
  argument = Mutant::Util.one(arguments)
243
243
 
244
- emit_propagation(argument) unless n_kwargs?(argument)
244
+ return if n_kwargs?(argument) || n_forwarded_args?(argument)
245
+
246
+ emit_propagation(argument)
245
247
  end
246
248
 
247
249
  def mutate_receiver
@@ -22,7 +22,7 @@ module Mutant
22
22
 
23
23
  def mutate_conditions
24
24
  conditions = children.length - 1
25
- children[0..-2].each_index do |index|
25
+ children[..-2].each_index do |index|
26
26
  delete_child(index) if conditions > 1
27
27
  mutate_child(index)
28
28
  end
@@ -50,7 +50,7 @@ module Mutant
50
50
  Mutant
51
51
  .traverse(->(line) { parse_line(root, line) }, lines)
52
52
  .fmap do |paths|
53
- paths.map { |path| [path.path, path] }.to_h
53
+ paths.to_h { |path| [path.path, path] }
54
54
  end
55
55
  end
56
56
  end
@@ -17,6 +17,11 @@ module Mutant
17
17
  self
18
18
  end
19
19
 
20
+ def post_insert
21
+ scope.__send__(visibility, name)
22
+ self
23
+ end
24
+
20
25
  # Mutator for memoizable memoized instance methods
21
26
  class Memoized < self
22
27
  include AST::Sexp
@@ -17,6 +17,11 @@ module Mutant
17
17
  self
18
18
  end
19
19
 
20
+ def post_insert
21
+ scope.singleton_class.__send__(visibility, name)
22
+ self
23
+ end
24
+
20
25
  end # Singleton
21
26
  end # Method
22
27
  end # Subject
@@ -4,6 +4,7 @@ module Mutant
4
4
  class Subject
5
5
  # Abstract base class for method subjects
6
6
  class Method < self
7
+ include anima.add(:visibility)
7
8
 
8
9
  # Method name
9
10
  #
@@ -33,6 +33,13 @@ module Mutant
33
33
  self
34
34
  end
35
35
 
36
+ # Perform post insert cleanup
37
+ #
38
+ # @return [self]
39
+ def post_insert
40
+ self
41
+ end
42
+
36
43
  # Source line range
37
44
  #
38
45
  # @return [Range<Integer>]
@@ -351,7 +351,7 @@ module Mutant
351
351
  def transform_keys(keys, input)
352
352
  success(
353
353
  keys
354
- .map do |key|
354
+ .to_h do |key|
355
355
  [
356
356
  key.value,
357
357
  coerce_key(key, input).from_right do |error|
@@ -359,7 +359,6 @@ module Mutant
359
359
  end
360
360
  ]
361
361
  end
362
- .to_h
363
362
  )
364
363
  end
365
364
  # rubocop:enable Metrics/MethodLength
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Mutant
4
4
  # Current mutant version
5
- VERSION = '0.11.3'
5
+ VERSION = '0.11.6'
6
6
  end # Mutant
@@ -17,10 +17,7 @@ module Mutant
17
17
 
18
18
  include AST::Sexp
19
19
 
20
- # rubocop:disable Lint/InheritException
21
20
  LoadError = Class.new(::LoadError)
22
- # rubocop:enable Lint/InheritException
23
-
24
21
  # Initialize object
25
22
  #
26
23
  # @param [Symbol] namespace
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mutant
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.3
4
+ version: 0.11.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Markus Schirp
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-13 00:00:00.000000000 Z
11
+ date: 2022-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diff-lcs
@@ -368,7 +368,8 @@ files:
368
368
  homepage: https://github.com/mbj/mutant
369
369
  licenses:
370
370
  - Nonstandard
371
- metadata: {}
371
+ metadata:
372
+ rubygems_mfa_required: 'true'
372
373
  post_install_message:
373
374
  rdoc_options: []
374
375
  require_paths:
@@ -377,7 +378,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
377
378
  requirements:
378
379
  - - ">="
379
380
  - !ruby/object:Gem::Version
380
- version: '2.6'
381
+ version: '2.7'
381
382
  required_rubygems_version: !ruby/object:Gem::Requirement
382
383
  requirements:
383
384
  - - ">="