mutant 0.11.21 → 0.11.22

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 47d87c7b5cf12da4906d170d892be1d5121b4f65b86b39365db2d6424f1487c6
4
- data.tar.gz: e8d9007fc9633eea5343b83282d331fab5e59aaf6f880aa9b52cb525b2f6db81
3
+ metadata.gz: bf5fb7800ce81d8fa90585c0dbd00ef9e2f2d21fa1ffbb788cbb59bb9323f677
4
+ data.tar.gz: 206c2db89f0cfca28b088059a68b0715974dffee6062a8825dd27c703ecbb245
5
5
  SHA512:
6
- metadata.gz: 04e36404f3a210cfa4a7045e244a72509e69332dbf736e81f5069a7d632680931747b761fd5f61e2ba915e94a0f9545506c3db2369a44f62a21b7a0254264db6
7
- data.tar.gz: 2e0db7e6d2ada9525ec054a197f8004e223a31c093ab7b603704044f92f8e1f4305048851b8a583172f3904a028a25a304a605e3e84d5a5e708eccabb6842e9c
6
+ metadata.gz: 3923e0d8c6927da2c7f6ca9915fd84c98e032708bb41b9c10c91cca92503226a1d04d09b20970ffad44b06e0ca7ff20215609d385f927ea0880a8b60bafcad93
7
+ data.tar.gz: 43834f9ec8af1dd4a65b307cf43af4acaffcb839c3c76fe52069dedb2201d80c7a81e313c6e4742fd9d28f81229f36f72bbfb5f4a7c119d13a001705648993b8
@@ -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
@@ -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
@@ -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.22'
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.22
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-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diff-lcs
@@ -235,6 +235,7 @@ files:
235
235
  - lib/mutant/meta/example/verification.rb
236
236
  - lib/mutant/mutation.rb
237
237
  - lib/mutant/mutation/config.rb
238
+ - lib/mutant/mutation/operators.rb
238
239
  - lib/mutant/mutation/runner.rb
239
240
  - lib/mutant/mutation/runner/sink.rb
240
241
  - lib/mutant/mutator.rb