mutant 0.11.21 → 0.11.23

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: 47d87c7b5cf12da4906d170d892be1d5121b4f65b86b39365db2d6424f1487c6
4
- data.tar.gz: e8d9007fc9633eea5343b83282d331fab5e59aaf6f880aa9b52cb525b2f6db81
3
+ metadata.gz: fa07ddf4b4373385631a41ea909149b416a97eaeaa51c0f259d846fbf5488c24
4
+ data.tar.gz: ef900eed75c28e9ad08e21e2ed026dd4184808125ed5338c36e421659c388ff1
5
5
  SHA512:
6
- metadata.gz: 04e36404f3a210cfa4a7045e244a72509e69332dbf736e81f5069a7d632680931747b761fd5f61e2ba915e94a0f9545506c3db2369a44f62a21b7a0254264db6
7
- data.tar.gz: 2e0db7e6d2ada9525ec054a197f8004e223a31c093ab7b603704044f92f8e1f4305048851b8a583172f3904a028a25a304a605e3e84d5a5e708eccabb6842e9c
6
+ metadata.gz: e5a6f9a778791413e93f0b9d427d3636e4cbfba89581f1b196a03fa2bd528935550405da4a02e281a72476fac6dbd8a5a11ad101e484989051de36305fea3a63
7
+ data.tar.gz: 256ba65540c021c9855248dfe4aeff4a230e2cd4119119b4e46ff5f82c1c1eac540d8a4e2f9b1345be9f2b81d171ecfbae5a11ae03d1139643522dd190c0e77c
@@ -3,7 +3,6 @@
3
3
  module Mutant
4
4
  module CLI
5
5
  class Command
6
- # rubocop:disable Metrics/ClassLength
7
6
  class Environment < self
8
7
  NAME = 'environment'
9
8
  SHORT_DESCRIPTION = 'Environment subcommands'
@@ -29,18 +28,8 @@ module Mutant
29
28
  env = Env.empty(world, @config)
30
29
 
31
30
  env
32
- .record(:config) { Config.load_config_file(env).fmap(&method(:expand)) }
33
- .bind { Bootstrap.call(env.with(config: @config)) }
34
- end
35
-
36
- def expand(file_config)
37
- if @config.matcher.subjects.any?
38
- file_config = file_config.with(
39
- matcher: file_config.matcher.with(subjects: [])
40
- )
41
- end
42
-
43
- @config = Config.env.merge(file_config).merge(@config)
31
+ .record(:config) { Config.load(cli_config: @config, world: world) }
32
+ .bind { |config| Bootstrap.call(env.with(config: config)) }
44
33
  end
45
34
 
46
35
  def parse_remaining_arguments(arguments)
data/lib/mutant/config.rb CHANGED
@@ -93,30 +93,41 @@ module Mutant
93
93
  # rubocop:enable Metrics/AbcSize
94
94
  # rubocop:enable Metrics/MethodLength
95
95
 
96
+ # Load the configuration
97
+ def self.load(cli_config:, world:)
98
+ load_config_file(reporter: cli_config.reporter, world: world).fmap do |file_config|
99
+ DEFAULT.with(
100
+ jobs: Etc.nprocessors,
101
+ mutation: Mutation::Config::DEFAULT
102
+ ).merge(file_config.merge(cli_config))
103
+ end
104
+ end
105
+
96
106
  # Load config file
97
107
  #
98
108
  # @param [Env] env
99
109
  #
100
110
  # @return [Either<String,Config>]
101
- def self.load_config_file(env)
111
+ def self.load_config_file(reporter:, world:)
102
112
  files = CANDIDATES
103
- .map(&env.world.pathname.public_method(:new))
113
+ .map(&world.pathname.public_method(:new))
104
114
  .select(&:readable?)
105
115
 
106
116
  if files.one?
107
- load_contents(env, files.first).fmap(&DEFAULT.public_method(:with))
117
+ load_contents(reporter: reporter, path: files.first).fmap(&DEFAULT.public_method(:with))
108
118
  elsif files.empty?
109
119
  Either::Right.new(DEFAULT)
110
120
  else
111
121
  Either::Left.new(MORE_THAN_ONE_CONFIG_FILE % files.join(', '))
112
122
  end
113
123
  end
124
+ private_class_method :load_config_file
114
125
 
115
- def self.load_contents(env, path)
126
+ def self.load_contents(reporter:, path:)
116
127
  Transform::Named
117
128
  .new(
118
129
  name: path.to_s,
119
- transform: sequence(env.config.reporter)
130
+ transform: sequence(reporter)
120
131
  )
121
132
  .call(path)
122
133
  .lmap(&:compact_message)
@@ -136,13 +147,6 @@ module Mutant
136
147
  end
137
148
  private_class_method :sequence
138
149
 
139
- # The configuration from the environment
140
- #
141
- # @return [Config]
142
- def self.env
143
- DEFAULT.with(jobs: Etc.nprocessors)
144
- end
145
-
146
150
  PATHNAME_ARRAY = Transform::Array.new(
147
151
  transform: Transform::Sequence.new(
148
152
  steps: [
@@ -78,9 +78,11 @@ module Mutant
78
78
  #
79
79
  # @return [Config]
80
80
  def merge(other)
81
- self.class.new(
82
- to_h
83
- .to_h { |name, value| [name, value + other.public_send(name)] }
81
+ with(
82
+ ignore: ignore + other.ignore,
83
+ start_expressions: start_expressions + other.start_expressions,
84
+ subjects: other.subjects.any? ? other.subjects : subjects,
85
+ diffs: diffs + other.diffs
84
86
  )
85
87
  end
86
88
 
@@ -11,25 +11,26 @@ module Mutant
11
11
  #
12
12
  # @param [Thread::Backtrace::Location] location
13
13
  # @param [Set<Symbol>] types
14
+ # @param [Mutation::Operators] operators
14
15
  #
15
16
  # @return [Example]
16
- def self.call(location, types, block)
17
- instance = new(location, types)
17
+ #
18
+ def self.call(location:, types:, operators:, block:) # rubocop:disable Metrics/ParameterLists
19
+ instance = new(location, types, operators)
18
20
  instance.instance_eval(&block)
19
21
  instance.example
20
22
  end
21
-
22
23
  private_class_method :new
23
24
 
24
25
  # Initialize object
25
26
  #
26
27
  # @return [undefined]
27
- def initialize(location, types)
28
- @expected = []
29
- @location = location
30
- @lvars = []
31
- @source = nil
32
- @types = types
28
+ def initialize(location, types, operators)
29
+ @expected = []
30
+ @location = location
31
+ @lvars = []
32
+ @operators = operators
33
+ @types = types
33
34
  end
34
35
 
35
36
  # Example captured by DSL
@@ -46,6 +47,7 @@ module Mutant
46
47
  location: @location,
47
48
  lvars: @lvars,
48
49
  node: @node,
50
+ operators: @operators,
49
51
  original_source: @source,
50
52
  types: @types
51
53
  )
@@ -54,7 +54,7 @@ module Mutant
54
54
 
55
55
  def original
56
56
  [
57
- 'Original:',
57
+ "Original: (operators: #{example.operators.class.operators_name})",
58
58
  example.node,
59
59
  example.original_source
60
60
  ]
@@ -10,6 +10,7 @@ module Mutant
10
10
  :location,
11
11
  :lvars,
12
12
  :node,
13
+ :operators,
13
14
  :original_source,
14
15
  :types
15
16
  )
@@ -56,7 +57,7 @@ module Mutant
56
57
  # @return [Enumerable<Mutant::Mutation>]
57
58
  def generated
58
59
  Mutator::Node.mutate(
59
- config: Mutation::Config::DEFAULT,
60
+ config: Mutation::Config::DEFAULT.with(operators: operators),
60
61
  node: node
61
62
  ).map do |node|
62
63
  Mutation::Evil.new(subject: self, node: node)
data/lib/mutant/meta.rb CHANGED
@@ -16,8 +16,13 @@ module Mutant
16
16
  # Add example
17
17
  #
18
18
  # @return [undefined]
19
- def self.add(*types, &block)
20
- ALL << DSL.call(caller_locations(1).first, Set.new(types), block)
19
+ def self.add(*types, operators: :full, &block)
20
+ ALL << DSL.call(
21
+ block: block,
22
+ location: caller_locations(1).first,
23
+ operators: Mutation::Operators.parse(operators.to_s).from_right,
24
+ types: Set.new(types)
25
+ )
21
26
  end
22
27
 
23
28
  Pathname.glob(Pathname.new(__dir__).parent.parent.join('meta', '*.rb'))
@@ -3,11 +3,22 @@
3
3
  module Mutant
4
4
  class Mutation
5
5
  class Config
6
- include Anima.new(:ignore_patterns, :timeout)
6
+ include Anima.new(
7
+ :ignore_patterns,
8
+ :operators,
9
+ :timeout
10
+ )
11
+
12
+ EMPTY = new(
13
+ ignore_patterns: [],
14
+ operators: nil,
15
+ timeout: nil
16
+ )
7
17
 
8
18
  DEFAULT = new(
9
- timeout: nil,
10
- ignore_patterns: []
19
+ ignore_patterns: [],
20
+ operators: Mutation::Operators::Light.new,
21
+ timeout: nil
11
22
  )
12
23
 
13
24
  ignore_pattern = Transform::Block.capture('ignore pattern', &AST::Pattern.method(:parse))
@@ -20,6 +31,10 @@ module Mutant
20
31
  transform: Transform::Array.new(transform: ignore_pattern),
21
32
  value: 'ignore_patterns'
22
33
  ),
34
+ Transform::Hash::Key.new(
35
+ transform: Operators::TRANSFORM,
36
+ value: 'operators'
37
+ ),
23
38
  Transform::Hash::Key.new(
24
39
  transform: Transform::FLOAT,
25
40
  value: 'timeout'
@@ -28,13 +43,14 @@ module Mutant
28
43
  required: []
29
44
  ),
30
45
  Transform::Hash::Symbolize.new,
31
- Transform::Success.new(block: DEFAULT.method(:with))
46
+ Transform::Success.new(block: EMPTY.method(:with))
32
47
  ]
33
48
  )
34
49
 
35
50
  def merge(other)
36
51
  with(
37
52
  ignore_patterns: other.ignore_patterns,
53
+ operators: other.operators || operators,
38
54
  timeout: other.timeout || timeout
39
55
  )
40
56
  end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ # Represent a mutated node with its subject
5
+ class Mutation
6
+ class Operators
7
+ include Equalizer.new
8
+
9
+ class Full < self
10
+ NAME = :full
11
+
12
+ SELECTOR_REPLACEMENTS = {
13
+ :< => %i[== eql? equal?],
14
+ :<= => %i[< == eql? equal?],
15
+ :== => %i[eql? equal?],
16
+ :=== => %i[is_a?],
17
+ :=~ => %i[match?],
18
+ :> => %i[== eql? equal?],
19
+ :>= => %i[> == eql? equal?],
20
+ __send__: %i[public_send],
21
+ all?: %i[any?],
22
+ any?: %i[all?],
23
+ at: %i[fetch key?],
24
+ fetch: %i[key?],
25
+ flat_map: %i[map],
26
+ gsub: %i[sub],
27
+ is_a?: %i[instance_of?],
28
+ kind_of?: %i[instance_of?],
29
+ map: %i[each],
30
+ match: %i[match?],
31
+ method: %i[public_method],
32
+ reverse_each: %i[each],
33
+ reverse_map: %i[map each],
34
+ reverse_merge: %i[merge],
35
+ send: %i[public_send __send__],
36
+ to_a: %i[to_ary],
37
+ to_h: %i[to_hash],
38
+ to_i: %i[to_int],
39
+ to_s: %i[to_str],
40
+ values_at: %i[fetch_values]
41
+ }.freeze.tap { |hash| hash.values(&:freeze) }
42
+ end
43
+
44
+ class Light < self
45
+ NAME = :light
46
+
47
+ SELECTOR_REPLACEMENTS = Full::SELECTOR_REPLACEMENTS
48
+ .dup
49
+ .tap do |replacements|
50
+ replacements.delete(:==)
51
+ replacements.delete(:eql?)
52
+ end
53
+ .freeze
54
+ end
55
+
56
+ def self.operators_name
57
+ self::NAME
58
+ end
59
+
60
+ def selector_replacements
61
+ self.class::SELECTOR_REPLACEMENTS
62
+ end
63
+
64
+ def self.parse(value)
65
+ klass = [Light, Full].detect { |candidate| candidate.operators_name.to_s.eql?(value) }
66
+
67
+ if klass
68
+ Either::Right.new(klass.new)
69
+ else
70
+ Either::Left.new("Unknown operators: #{value}")
71
+ end
72
+ end
73
+
74
+ TRANSFORM =
75
+ Transform::Sequence.new(
76
+ steps: [
77
+ Transform::STRING,
78
+ Transform::Block.capture('parse operator', &method(:parse))
79
+ ]
80
+ )
81
+ end # Operators
82
+ end # Mutation
83
+ end # Mutant
@@ -25,18 +25,22 @@ module Mutant
25
25
  children.each_with_index do |removed, index|
26
26
  new_arguments = children.dup
27
27
  new_arguments.delete_at(index)
28
- unless n_forward_arg?(removed) || removed_block_arg?(new_arguments) || only_mlhs?(new_arguments)
28
+ unless forward_type?(removed) || removed_block_arg?(new_arguments) || only_mlhs?(new_arguments)
29
29
  emit_type(*new_arguments)
30
30
  end
31
31
  end
32
32
  end
33
33
 
34
+ def forward_type?(removed)
35
+ n_forward_arg?(removed) || n_restarg?(removed) || n_kwrestarg?(removed)
36
+ end
37
+
34
38
  def only_mlhs?(new_arguments)
35
39
  new_arguments.one? && n_mlhs?(new_arguments.first)
36
40
  end
37
41
 
38
42
  def forward_arg?
39
- children.last && n_forward_arg?(children.last)
43
+ children.any?(&method(:forward_type?))
40
44
  end
41
45
 
42
46
  def removed_block_arg?(new_arguments)
@@ -54,7 +54,7 @@ module Mutant
54
54
  end
55
55
 
56
56
  def valid_send_mutation?(node)
57
- return unless n_send?(node)
57
+ return false unless n_send?(node)
58
58
 
59
59
  last = AST::Meta::Send.new(node: node).arguments.last
60
60
 
@@ -20,9 +20,12 @@ module Mutant
20
20
  def emit_symbol_to_proc_mutations
21
21
  return unless n_sym?(argument)
22
22
 
23
- Send::SELECTOR_REPLACEMENTS.fetch(*argument, EMPTY_ARRAY).each do |method|
24
- emit_argument(s(:sym, method))
25
- end
23
+ config
24
+ .operators
25
+ .selector_replacements
26
+ .fetch(*argument, EMPTY_ARRAY).each do |method|
27
+ emit_argument(s(:sym, method))
28
+ end
26
29
  end
27
30
  end # Block
28
31
  end # Node
@@ -13,37 +13,6 @@ module Mutant
13
13
 
14
14
  children :receiver, :selector
15
15
 
16
- SELECTOR_REPLACEMENTS = {
17
- :< => %i[== eql? equal?],
18
- :<= => %i[< == eql? equal?],
19
- :== => %i[eql? equal?],
20
- :=== => %i[is_a?],
21
- :=~ => %i[match?],
22
- :> => %i[== eql? equal?],
23
- :>= => %i[> == eql? equal?],
24
- __send__: %i[public_send],
25
- all?: %i[any?],
26
- any?: %i[all?],
27
- at: %i[fetch key?],
28
- fetch: %i[key?],
29
- flat_map: %i[map],
30
- gsub: %i[sub],
31
- is_a?: %i[instance_of?],
32
- kind_of?: %i[instance_of?],
33
- map: %i[each],
34
- match: %i[match?],
35
- method: %i[public_method],
36
- reverse_each: %i[each],
37
- reverse_map: %i[map each],
38
- reverse_merge: %i[merge],
39
- send: %i[public_send __send__],
40
- to_a: %i[to_ary],
41
- to_h: %i[to_hash],
42
- to_i: %i[to_int],
43
- to_s: %i[to_str],
44
- values_at: %i[fetch_values]
45
- }.freeze.tap { |hash| hash.values(&:freeze) }
46
-
47
16
  RECEIVER_SELECTOR_REPLACEMENTS = {
48
17
  Date: {
49
18
  parse: %i[jd civil strptime iso8601 rfc3339 xmlschema rfc2822 rfc822 httpdate jisx0301]
@@ -227,7 +196,10 @@ module Mutant
227
196
  end
228
197
 
229
198
  def emit_selector_replacement
230
- SELECTOR_REPLACEMENTS.fetch(selector, EMPTY_ARRAY).each(&method(:emit_selector))
199
+ config
200
+ .operators
201
+ .selector_replacements
202
+ .fetch(selector, EMPTY_ARRAY).each(&public_method(:emit_selector))
231
203
  end
232
204
 
233
205
  def emit_naked_receiver
@@ -251,7 +223,7 @@ module Mutant
251
223
 
252
224
  argument = Mutant::Util.one(arguments)
253
225
 
254
- return if n_kwargs?(argument) || n_forwarded_args?(argument)
226
+ return if n_kwargs?(argument) || n_forwarded_args?(argument) || n_forwarded_restarg?(argument)
255
227
 
256
228
  emit_propagation(argument)
257
229
  end
@@ -20,6 +20,7 @@ module Mutant
20
20
  info 'Jobs: %s', object.jobs || 'auto'
21
21
  info 'Includes: %s', object.includes
22
22
  info 'Requires: %s', object.requires
23
+ info 'Operators: %s', object.mutation.operators.class.operators_name
23
24
  info 'MutationTimeout: %0.9g', object.mutation.timeout if object.mutation.timeout
24
25
  end
25
26
  # rubocop:enable Metrics/AbcSize
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Mutant
4
4
  # Current mutant version
5
- VERSION = '0.11.21'
5
+ VERSION = '0.11.23'
6
6
  end # Mutant
data/lib/mutant.rb CHANGED
@@ -107,6 +107,7 @@ module Mutant
107
107
  require 'mutant/parallel/worker'
108
108
  require 'mutant/require_highjack'
109
109
  require 'mutant/mutation'
110
+ require 'mutant/mutation/operators'
110
111
  require 'mutant/mutation/config'
111
112
  require 'mutant/mutator'
112
113
  require 'mutant/mutator/util'
@@ -350,7 +351,7 @@ module Mutant
350
351
  isolation: Mutant::Isolation::Fork.new(world: WORLD),
351
352
  jobs: nil,
352
353
  matcher: Matcher::Config::DEFAULT,
353
- mutation: Mutation::Config::DEFAULT,
354
+ mutation: Mutation::Config::EMPTY,
354
355
  reporter: Reporter::CLI.build(WORLD.stdout),
355
356
  requires: EMPTY_ARRAY
356
357
  )
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.21
4
+ version: 0.11.23
5
5
  platform: ruby
6
6
  authors:
7
7
  - Markus Schirp
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-15 00:00:00.000000000 Z
11
+ date: 2023-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diff-lcs
@@ -31,6 +31,9 @@ dependencies:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: 3.2.2
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 3.2.2.4
34
37
  type: :runtime
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
@@ -38,6 +41,9 @@ dependencies:
38
41
  - - "~>"
39
42
  - !ruby/object:Gem::Version
40
43
  version: 3.2.2
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 3.2.2.4
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: regexp_parser
43
49
  requirement: !ruby/object:Gem::Requirement
@@ -72,14 +78,14 @@ dependencies:
72
78
  requirements:
73
79
  - - "~>"
74
80
  - !ruby/object:Gem::Version
75
- version: 0.6.8
81
+ version: 0.6.9
76
82
  type: :runtime
77
83
  prerelease: false
78
84
  version_requirements: !ruby/object:Gem::Requirement
79
85
  requirements:
80
86
  - - "~>"
81
87
  - !ruby/object:Gem::Version
82
- version: 0.6.8
88
+ version: 0.6.9
83
89
  - !ruby/object:Gem::Dependency
84
90
  name: parallel
85
91
  requirement: !ruby/object:Gem::Requirement
@@ -235,6 +241,7 @@ files:
235
241
  - lib/mutant/meta/example/verification.rb
236
242
  - lib/mutant/mutation.rb
237
243
  - lib/mutant/mutation/config.rb
244
+ - lib/mutant/mutation/operators.rb
238
245
  - lib/mutant/mutation/runner.rb
239
246
  - lib/mutant/mutation/runner/sink.rb
240
247
  - lib/mutant/mutator.rb