mutant 0.11.11 → 0.11.12

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: 6aa7e42cfe0d8ed12281745fc607d4d933fbb03af10b2bb41c41e6df3765ef89
4
- data.tar.gz: 2a4a940e72bf2381816cfc0522f236166df7c2f649bd4da6ee8c2f41a6851c33
3
+ metadata.gz: 3c75928a9c55d93ebfad196c028271b8c82d7d9eb94e48873ac75c9ee4bb212f
4
+ data.tar.gz: 27149ec498db15629bb18c55a5840e3ea8c369542a85bf9d3658cadc78fc8f4a
5
5
  SHA512:
6
- metadata.gz: 9ffa1cf3efbe20ba6ac20d7aee9765cbb8833a2934f2d1b874ec800673582c709e76bcbdb6a026191bd751a9f30726720c5f0f81b09e0999805608e22d775dc7
7
- data.tar.gz: b4a2cf6f0fc16056e8f92b306828c4e5578f0b1408770b0395b4874ff887a3f1cbe5023613694493736bcdede6c906dfaed06c1b73e78f2b8b5821b2fb61457b
6
+ metadata.gz: 44867690e5c1c367df8b22ec3fa5b35941328e12fd10a26a252baa3815c955fbd37f1adaaaadfafbcdc1f72280ce013d37996d001c163930c084b95b07905362
7
+ data.tar.gz: b22e71a0ae7f1ecb96345e7996e61c4ee843c7b82442f21dea3516be132503fa9c0193443f1d7da9196fbb27f23f3ef858ee58f81c03b7cda8d4f4dcced1a9a7
@@ -24,16 +24,15 @@ module Mutant
24
24
 
25
25
  # Run Bootstrap
26
26
  #
27
- # @param [World] world
28
- # @param [Config] config
27
+ # @param [Env] env
29
28
  #
30
29
  # @return [Either<String, Env>]
31
30
  #
32
31
  # rubocop:disable Metrics/MethodLength
33
- def self.call(world, config)
34
- env = load_hooks(Env.empty(world, config))
32
+ def self.call(env)
33
+ env = load_hooks(env)
35
34
  .tap(&method(:infect))
36
- .with(matchable_scopes: matchable_scopes(world, config))
35
+ .with(matchable_scopes: matchable_scopes(env))
37
36
 
38
37
  subjects = start_subject(env, Matcher.from_config(env.config.matcher).call(env))
39
38
 
@@ -82,8 +81,10 @@ module Mutant
82
81
  end
83
82
  private_class_method :infect
84
83
 
85
- def self.matchable_scopes(world, config)
86
- scopes = world.object_space.each_object(Module).each_with_object([]) do |scope, aggregate|
84
+ def self.matchable_scopes(env)
85
+ config = env.config
86
+
87
+ scopes = env.world.object_space.each_object(Module).each_with_object([]) do |scope, aggregate|
87
88
  expression = expression(config.reporter, config.expression_parser, scope) || next
88
89
  aggregate << Scope.new(scope, expression)
89
90
  end
@@ -23,9 +23,11 @@ module Mutant
23
23
  end
24
24
 
25
25
  def bootstrap
26
- Config.load_config_file(world)
26
+ env = Env.empty(world, @config)
27
+
28
+ Config.load_config_file(env)
27
29
  .fmap(&method(:expand))
28
- .bind { Bootstrap.call(world, @config) }
30
+ .bind { Bootstrap.call(env.with(config: @config)) }
29
31
  end
30
32
 
31
33
  def expand(file_config)
@@ -121,7 +123,7 @@ module Mutant
121
123
  set(jobs: Integer(number))
122
124
  end
123
125
  parser.on('-t', '--mutation-timeout NUMBER', 'Per mutation analysis timeout') do |number|
124
- set(mutation_timeout: Float(number))
126
+ set(mutation: @config.mutation.with(timeout: Float(number)))
125
127
  end
126
128
  end
127
129
  end # Run
@@ -14,6 +14,12 @@ module Mutant
14
14
  OPTIONS = %i[add_target_options].freeze
15
15
 
16
16
  def action
17
+ @ignore_patterns.map! do |syntax|
18
+ AST::Pattern.parse(syntax).from_right do |message|
19
+ return Either::Left.new(message)
20
+ end
21
+ end
22
+
17
23
  @targets.each(&method(:print_mutations))
18
24
  Either::Right.new(nil)
19
25
  end
@@ -50,18 +56,27 @@ module Mutant
50
56
  def initialize(_arguments)
51
57
  super
52
58
 
53
- @targets = []
59
+ @targets = []
60
+ @ignore_patterns = []
54
61
  end
55
62
 
56
63
  def add_target_options(parser)
57
64
  parser.on('-e', '--evaluate SOURCE') do |source|
58
65
  @targets << Target::Source.new(source)
59
66
  end
67
+
68
+ parser.on('-i', '--ignore-pattern AST_PATTERN') do |pattern|
69
+ @ignore_patterns << pattern
70
+ end
60
71
  end
61
72
 
62
73
  def print_mutations(target)
63
74
  world.stdout.puts(target.identification)
64
- Mutator.mutate(target.node).each do |mutation|
75
+
76
+ Mutator::Node.mutate(
77
+ config: Mutant::Mutation::Config::DEFAULT.with(ignore_patterns: @ignore_patterns),
78
+ node: target.node
79
+ ).each do |mutation|
65
80
  Reporter::CLI::Printer::Mutation.call(
66
81
  world.stdout,
67
82
  Mutant::Mutation::Evil.new(target, mutation)
data/lib/mutant/config.rb CHANGED
@@ -19,7 +19,7 @@ module Mutant
19
19
  :isolation,
20
20
  :jobs,
21
21
  :matcher,
22
- :mutation_timeout,
22
+ :mutation,
23
23
  :reporter,
24
24
  :requires,
25
25
  :zombie
@@ -39,6 +39,20 @@ module Mutant
39
39
  mutant.yml
40
40
  ].freeze
41
41
 
42
+ MUTATION_TIMEOUT_DEPRECATION = <<~'MESSAGE'
43
+ Deprecated configuration toplevel key `mutation_timeout` found.
44
+
45
+ This key will be removed in the next mayor version.
46
+ Instead place your mutation timeout configuration under the `mutation` key
47
+ like this:
48
+
49
+ ```
50
+ # mutant.yml
51
+ mutation:
52
+ timeout: 10.0 # float here.
53
+ ```
54
+ MESSAGE
55
+
42
56
  private_constant(*constants(false))
43
57
 
44
58
  # Merge with other config
@@ -59,7 +73,7 @@ module Mutant
59
73
  integration: other.integration || integration,
60
74
  jobs: other.jobs || jobs,
61
75
  matcher: matcher.merge(other.matcher),
62
- mutation_timeout: other.mutation_timeout || mutation_timeout,
76
+ mutation: mutation.merge(other.mutation),
63
77
  requires: requires + other.requires,
64
78
  zombie: zombie || other.zombie
65
79
  )
@@ -69,17 +83,16 @@ module Mutant
69
83
 
70
84
  # Load config file
71
85
  #
72
- # @param [World] world
73
- # @param [Config] config
86
+ # @param [Env] env
74
87
  #
75
88
  # @return [Either<String,Config>]
76
- def self.load_config_file(world)
89
+ def self.load_config_file(env)
77
90
  files = CANDIDATES
78
- .map(&world.pathname.public_method(:new))
91
+ .map(&env.world.pathname.public_method(:new))
79
92
  .select(&:readable?)
80
93
 
81
94
  if files.one?
82
- load_contents(files.first).fmap(&DEFAULT.public_method(:with))
95
+ load_contents(env, files.first).fmap(&DEFAULT.public_method(:with))
83
96
  elsif files.empty?
84
97
  Either::Right.new(DEFAULT)
85
98
  else
@@ -97,14 +110,30 @@ module Mutant
97
110
  )
98
111
  end
99
112
 
100
- def self.load_contents(path)
113
+ def self.load_contents(env, path)
101
114
  Transform::Named
102
- .new(path.to_s, TRANSFORM)
115
+ .new(
116
+ path.to_s,
117
+ sequence(env.config.reporter)
118
+ )
103
119
  .call(path)
104
120
  .lmap(&:compact_message)
105
121
  end
106
122
  private_class_method :load_contents
107
123
 
124
+ def self.sequence(reporter)
125
+ Transform::Sequence.new(
126
+ [
127
+ Transform::Exception.new(SystemCallError, :read.to_proc),
128
+ Transform::Exception.new(YAML::SyntaxError, YAML.public_method(:safe_load)),
129
+ Transform::Primitive.new(Hash),
130
+ Transform::Success.new(->(hash) { deprecations(reporter, hash) }),
131
+ *TRANSFORMS
132
+ ]
133
+ )
134
+ end
135
+ private_class_method :sequence
136
+
108
137
  # The configuration from the environment
109
138
  #
110
139
  # @return [Config]
@@ -139,38 +168,45 @@ module Mutant
139
168
 
140
169
  Either::Right.new(hash)
141
170
  end
142
- TRANSFORM = Transform::Sequence.new(
143
- [
144
- Transform::Exception.new(SystemCallError, :read.to_proc),
145
- Transform::Exception.new(YAML::SyntaxError, YAML.public_method(:safe_load)),
146
- Transform::Hash.new(
147
- optional: [
148
- Transform::Hash::Key.new('coverage_criteria', ->(value) { CoverageCriteria::TRANSFORM.call(value) }),
149
- Transform::Hash::Key.new(
150
- 'environment_variables',
151
- Transform::Sequence.new(
152
- [
153
- Transform::Primitive.new(Hash),
154
- Transform::Block.capture(:environment_variables, &method(:parse_environment_variables))
155
- ]
156
- )
157
- ),
158
- Transform::Hash::Key.new('fail_fast', Transform::BOOLEAN),
159
- Transform::Hash::Key.new('hooks', PATHNAME_ARRAY),
160
- Transform::Hash::Key.new('includes', Transform::STRING_ARRAY),
161
- Transform::Hash::Key.new('integration', Transform::STRING),
162
- Transform::Hash::Key.new('jobs', Transform::INTEGER),
163
- Transform::Hash::Key.new('matcher', Matcher::Config::LOADER),
164
- Transform::Hash::Key.new('mutation_timeout', Transform::FLOAT),
165
- Transform::Hash::Key.new('requires', Transform::STRING_ARRAY)
166
- ],
167
- required: []
168
- ),
169
- Transform::Hash::Symbolize.new
170
- ]
171
- )
172
171
 
173
- private_constant(:TRANSFORM)
172
+ def self.deprecations(reporter, hash)
173
+ if hash.key?('mutation_timeout')
174
+ reporter.warn(MUTATION_TIMEOUT_DEPRECATION)
175
+
176
+ (hash['mutation'] ||= {})['timeout'] ||= hash.delete('mutation_timeout')
177
+ end
178
+
179
+ hash
180
+ end
181
+
182
+ TRANSFORMS = [
183
+ Transform::Hash.new(
184
+ optional: [
185
+ Transform::Hash::Key.new('coverage_criteria', ->(value) { CoverageCriteria::TRANSFORM.call(value) }),
186
+ Transform::Hash::Key.new(
187
+ 'environment_variables',
188
+ Transform::Sequence.new(
189
+ [
190
+ Transform::Primitive.new(Hash),
191
+ Transform::Block.capture(:environment_variables, &method(:parse_environment_variables))
192
+ ]
193
+ )
194
+ ),
195
+ Transform::Hash::Key.new('fail_fast', Transform::BOOLEAN),
196
+ Transform::Hash::Key.new('hooks', PATHNAME_ARRAY),
197
+ Transform::Hash::Key.new('includes', Transform::STRING_ARRAY),
198
+ Transform::Hash::Key.new('integration', Transform::STRING),
199
+ Transform::Hash::Key.new('jobs', Transform::INTEGER),
200
+ Transform::Hash::Key.new('matcher', Matcher::Config::LOADER),
201
+ Transform::Hash::Key.new('mutation', Mutation::Config::TRANSFORM),
202
+ Transform::Hash::Key.new('requires', Transform::STRING_ARRAY)
203
+ ],
204
+ required: []
205
+ ),
206
+ Transform::Hash::Symbolize.new
207
+ ].freeze
208
+
209
+ private_constant(:TRANSFORMS)
174
210
  end # Config
175
211
  # rubocop:enable Metrics/ClassLength
176
212
  end # Mutant
data/lib/mutant/env.rb CHANGED
@@ -137,7 +137,7 @@ module Mutant
137
137
  private
138
138
 
139
139
  def run_mutation_tests(mutation, tests)
140
- config.isolation.call(config.mutation_timeout) do
140
+ config.isolation.call(config.mutation.timeout) do
141
141
  hooks.run(:mutation_insert_pre, mutation)
142
142
  result = mutation.insert(world.kernel)
143
143
  hooks.run(:mutation_insert_post, mutation)
@@ -18,7 +18,7 @@ module Mutant
18
18
  end
19
19
 
20
20
  def self.parse(input)
21
- new(*input.split('/', 2))
21
+ new(*input.split('/', 2).map(&:downcase))
22
22
  end
23
23
 
24
24
  def self.parse_remote(input)
@@ -99,13 +99,20 @@ module Mutant
99
99
  node = matched_node_path.last || return
100
100
 
101
101
  self.class::SUBJECT_CLASS.new(
102
- config: Subject::Config.parse(ast.comment_associations.fetch(node, [])),
102
+ config: subject_config(node),
103
103
  context: context,
104
104
  node: node,
105
105
  visibility: visibility
106
106
  )
107
107
  end
108
108
 
109
+ def subject_config(node)
110
+ Subject::Config.parse(
111
+ comments: ast.comment_associations.fetch(node, []),
112
+ mutation: env.config.mutation
113
+ )
114
+ end
115
+
109
116
  def matched_node_path
110
117
  AST.find_last_path(ast.node, &method(:match?))
111
118
  end
@@ -55,7 +55,10 @@ module Mutant
55
55
  #
56
56
  # @return [Enumerable<Mutant::Mutation>]
57
57
  def generated
58
- Mutator.mutate(node).map do |node|
58
+ Mutator::Node.mutate(
59
+ config: Mutation::Config::DEFAULT,
60
+ node: node
61
+ ).map do |node|
59
62
  Mutation::Evil.new(self, node)
60
63
  end
61
64
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ class Mutation
5
+ class Config
6
+ include Anima.new(:ignore_patterns, :timeout)
7
+
8
+ DEFAULT = new(
9
+ timeout: nil,
10
+ ignore_patterns: []
11
+ )
12
+
13
+ ignore_pattern = Transform::Block.capture('ignore pattern', &AST::Pattern.method(:parse))
14
+
15
+ TRANSFORM = Transform::Sequence.new(
16
+ [
17
+ Transform::Hash.new(
18
+ optional: [
19
+ Transform::Hash::Key.new('ignore_patterns', Transform::Array.new(ignore_pattern)),
20
+ Transform::Hash::Key.new('timeout', Transform::FLOAT)
21
+ ],
22
+ required: []
23
+ ),
24
+ Transform::Hash::Symbolize.new,
25
+ Transform::Success.new(DEFAULT.method(:with))
26
+ ]
27
+ )
28
+
29
+ def merge(other)
30
+ with(
31
+ timeout: other.timeout || timeout
32
+ )
33
+ end
34
+ end # Config
35
+ end # Mutation
36
+ end # Mutant
@@ -50,7 +50,7 @@ module Mutant
50
50
 
51
51
  def emit_argument_mutations
52
52
  children.each_with_index do |child, index|
53
- Mutator.mutate(child).each do |mutant|
53
+ mutate(node: child).each do |mutant|
54
54
  next if invalid_argument_replacement?(mutant, index)
55
55
  emit_child_update(index, mutant)
56
56
  end
@@ -12,8 +12,9 @@ module Mutant
12
12
  private
13
13
 
14
14
  def dispatch
15
+ ignore_single = children.any?(&method(:ignore?))
15
16
  mutate_single_child do |child|
16
- emit(child)
17
+ emit(child) unless ignore_single
17
18
  end
18
19
  end
19
20
  end # Begin
@@ -13,8 +13,11 @@ module Mutant
13
13
  emit_optarg_body_assignments
14
14
  emit_body(N_RAISE)
15
15
  emit_body(N_ZSUPER)
16
- emit_body(nil)
17
- emit_body_mutations if body
16
+
17
+ return unless body && !ignore?(body)
18
+
19
+ emit_body(nil) unless body.children.any?(&method(:ignore?))
20
+ emit_body_mutations
18
21
  end
19
22
 
20
23
  def emit_optarg_body_assignments
@@ -20,14 +20,14 @@ module Mutant
20
20
  end
21
21
 
22
22
  def emit_argument_presence
23
- Util::Array::Presence.call(children).each do |children|
23
+ Util::Array::Presence.call(input: children, parent: nil).each do |children|
24
24
  emit_type(*children) unless children.empty?
25
25
  end
26
26
  end
27
27
 
28
28
  def emit_argument_mutations
29
29
  children.each_with_index do |child, index|
30
- Mutator.mutate(child).each do |mutant|
30
+ mutate(node: child).each do |mutant|
31
31
  unless forbid_argument?(mutant)
32
32
  emit_child_update(index, mutant)
33
33
  end
@@ -33,7 +33,7 @@ module Mutant
33
33
  # Regular expressions with interpolation are skipped.
34
34
  return unless (body_ast = AST::Regexp.expand_regexp_ast(input))
35
35
 
36
- Mutator.mutate(body_ast).each do |mutation|
36
+ mutate(node: body_ast).each do |mutation|
37
37
  source = AST::Regexp.to_expression(mutation).to_s
38
38
  emit_type(s(:str, source), options)
39
39
  end
@@ -15,9 +15,9 @@ module Mutant
15
15
 
16
16
  def dispatch
17
17
  emit_singletons
18
- Util::Symbol.call(value).each(&method(:emit_type))
19
- end
20
18
 
19
+ Util::Symbol.call(input: value, parent: nil).each(&method(:emit_type))
20
+ end
21
21
  end # Symbol
22
22
  end # Literal
23
23
  end # Node
@@ -26,7 +26,7 @@ module Mutant
26
26
  end
27
27
 
28
28
  def mutate_name
29
- Util::Symbol.call(name).each do |name|
29
+ Util::Symbol.call(input: name, parent: nil).each do |name|
30
30
  emit_name(name.upcase)
31
31
  end
32
32
  end
@@ -34,11 +34,11 @@ module Mutant
34
34
  def mutate_name
35
35
  prefix, regexp = MAP.fetch(node.type)
36
36
  stripped = name.to_s.sub(regexp, EMPTY_STRING)
37
- Util::Symbol.call(stripped).each do |name|
37
+
38
+ Util::Symbol.call(input: stripped, parent: nil).each do |name|
38
39
  emit_name(:"#{prefix}#{name}")
39
40
  end
40
41
  end
41
-
42
42
  end # VariableAssignment
43
43
  end # NamedValue
44
44
  end # Node
@@ -15,17 +15,7 @@ module Mutant
15
15
  def dispatch
16
16
  emit_assignment(nil)
17
17
  emit_body_mutations if body
18
- mutate_captures
19
18
  end
20
-
21
- def mutate_captures
22
- return unless captures
23
- Util::Array::Element.call(captures.children).each do |matchers|
24
- next if matchers.any?(&method(:n_nil?))
25
- emit_captures(s(:array, *matchers))
26
- end
27
- end
28
-
29
19
  end # Resbody
30
20
  end # Node
31
21
  end # Mutator
@@ -10,8 +10,37 @@ module Mutant
10
10
  include AbstractType, Unparser::Constants
11
11
  include AST::NamedChildren, AST::NodePredicates, AST::Sexp, AST::Nodes
12
12
 
13
+ include anima.add(:config)
14
+
13
15
  TAUTOLOGY = ->(_input) { true }
14
16
 
17
+ REGISTRY = Registry.new(->(_) { Node::Generic })
18
+
19
+ # Lookup and invoke dedicated AST mutator
20
+ #
21
+ # @param input [Parser::AST::Node]
22
+ # @param parent [nil,Mutant::Mutator::Node]
23
+ #
24
+ # @return [Set<Parser::AST::Node>]
25
+ def self.mutate(config:, node:, parent: nil)
26
+ config.ignore_patterns.each do |pattern|
27
+ return Set.new if pattern.match?(node)
28
+ end
29
+
30
+ self::REGISTRY.lookup(node.type).call(
31
+ config: config,
32
+ input: node,
33
+ parent: parent
34
+ )
35
+ end
36
+
37
+ def self.handle(*types)
38
+ types.each do |type|
39
+ self::REGISTRY.register(type, self)
40
+ end
41
+ end
42
+ private_class_method :handle
43
+
15
44
  # Helper to define a named child
16
45
  #
17
46
  # @param [Parser::AST::Node] node
@@ -37,9 +66,13 @@ module Mutant
37
66
  alias_method :node, :input
38
67
  alias_method :dup_node, :dup_input
39
68
 
69
+ def mutate(node:, parent: nil)
70
+ self.class.mutate(config: config, node: node, parent: parent)
71
+ end
72
+
40
73
  def mutate_child(index, &block)
41
74
  block ||= TAUTOLOGY
42
- Mutator.mutate(children.fetch(index), self).each do |mutation|
75
+ mutate(node: children.fetch(index), parent: self).each do |mutation|
43
76
  next unless block.call(mutation)
44
77
  emit_child_update(index, mutation)
45
78
  end
@@ -96,6 +129,19 @@ module Mutant
96
129
  end
97
130
  end
98
131
 
132
+ def run(mutator)
133
+ mutator.call(
134
+ config: config,
135
+ input: input,
136
+ parent: nil
137
+ ).each(&method(:emit))
138
+ end
139
+
140
+ def ignore?(node)
141
+ config.ignore_patterns.any? do |pattern|
142
+ pattern.match?(node)
143
+ end
144
+ end
99
145
  end # Node
100
146
  end # Mutator
101
147
  end # Mutant
@@ -21,23 +21,6 @@ module Mutant
21
21
  end
22
22
 
23
23
  end # Presence
24
-
25
- # Array element mutator
26
- class Element < Util
27
-
28
- private
29
-
30
- def dispatch
31
- input.each_with_index do |element, index|
32
- Mutator.mutate(element).each do |mutation|
33
- dup = dup_input
34
- dup[index] = mutation
35
- emit(dup)
36
- end
37
- end
38
- end
39
-
40
- end # Element
41
24
  end # Array
42
25
  end # Util
43
26
  end # Mutator
@@ -3,33 +3,13 @@
3
3
  module Mutant
4
4
  # Generator for mutations
5
5
  class Mutator
6
-
7
- REGISTRY = Registry.new(->(_) { Node::Generic })
8
-
9
6
  include(
10
7
  Adamantium,
11
- Concord.new(:input, :parent),
12
8
  AbstractType,
9
+ Anima.new(:input, :parent),
13
10
  Procto
14
11
  )
15
12
 
16
- # Lookup and invoke dedicated AST mutator
17
- #
18
- # @param node [Parser::AST::Node]
19
- # @param parent [nil,Mutant::Mutator::Node]
20
- #
21
- # @return [Set<Parser::AST::Node>]
22
- def self.mutate(node, parent = nil)
23
- self::REGISTRY.lookup(node.type).call(node, parent)
24
- end
25
-
26
- def self.handle(*types)
27
- types.each do |type|
28
- self::REGISTRY.register(type, self)
29
- end
30
- end
31
- private_class_method :handle
32
-
33
13
  # Return output
34
14
  #
35
15
  # @return [Set<Parser::AST::Node>]
@@ -39,7 +19,7 @@ module Mutant
39
19
 
40
20
  private
41
21
 
42
- def initialize(_input, _parent = nil)
22
+ def initialize(_attributes)
43
23
  super
44
24
 
45
25
  @output = Set.new
@@ -60,10 +40,6 @@ module Mutant
60
40
  output << object
61
41
  end
62
42
 
63
- def run(mutator)
64
- mutator.call(input).each(&method(:emit))
65
- end
66
-
67
43
  def dup_input
68
44
  input.dup
69
45
  end
@@ -20,7 +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 'MutationTimeout: %0.9g', object.mutation_timeout if object.mutation_timeout
23
+ info 'MutationTimeout: %0.9g', object.mutation.timeout if object.mutation.timeout
24
24
  end
25
25
  # rubocop:enable Metrics/AbcSize
26
26
 
@@ -3,16 +3,17 @@
3
3
  module Mutant
4
4
  class Subject
5
5
  class Config
6
- include Adamantium, Anima.new(:inline_disable)
6
+ include Adamantium, Anima.new(:inline_disable, :mutation)
7
7
 
8
- DEFAULT = new(inline_disable: false)
8
+ DEFAULT = new(inline_disable: false, mutation: Mutation::Config::DEFAULT)
9
9
 
10
10
  DISABLE_REGEXP = /(\s|^)mutant:disable(?:\s|$)/.freeze
11
11
  SYNTAX_REGEXP = /\A(?:#|=begin\n)/.freeze
12
12
 
13
- def self.parse(comments)
13
+ def self.parse(comments:, mutation:)
14
14
  new(
15
- inline_disable: comments.any? { |comment| DISABLE_REGEXP.match?(comment_body(comment)) }
15
+ inline_disable: comments.any? { |comment| DISABLE_REGEXP.match?(comment_body(comment)) },
16
+ mutation: mutation
16
17
  )
17
18
  end
18
19
 
@@ -12,7 +12,10 @@ module Mutant
12
12
  # @return [undefined]
13
13
  def mutations
14
14
  [neutral_mutation].concat(
15
- Mutator.mutate(node).map do |mutant|
15
+ Mutator::Node.mutate(
16
+ config: config.mutation,
17
+ node: node
18
+ ).map do |mutant|
16
19
  Mutation::Evil.new(self, wrap_node(mutant))
17
20
  end
18
21
  )
@@ -421,6 +421,20 @@ module Mutant
421
421
  end
422
422
  end # Sequence
423
423
 
424
+ # Always successful transformation
425
+ class Success < self
426
+ include Concord.new(:block)
427
+
428
+ # Apply transformation to input
429
+ #
430
+ # @param [Object]
431
+ #
432
+ # @return [Either<Error, Object>]
433
+ def call(input)
434
+ success(block.call(input))
435
+ end
436
+ end # Sequence
437
+
424
438
  # Generic exception transformer
425
439
  class Exception < self
426
440
  include Concord.new(:error_class, :block)
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Mutant
4
4
  # Current mutant version
5
- VERSION = '0.11.11'
5
+ VERSION = '0.11.12'
6
6
  end # Mutant
data/lib/mutant.rb CHANGED
@@ -93,6 +93,7 @@ require 'mutant/parallel/source'
93
93
  require 'mutant/parallel/worker'
94
94
  require 'mutant/require_highjack'
95
95
  require 'mutant/mutation'
96
+ require 'mutant/mutation/config'
96
97
  require 'mutant/mutator'
97
98
  require 'mutant/mutator/util'
98
99
  require 'mutant/mutator/util/array'
@@ -288,7 +289,7 @@ module Mutant
288
289
  isolation: Mutant::Isolation::Fork.new(WORLD),
289
290
  jobs: nil,
290
291
  matcher: Matcher::Config::DEFAULT,
291
- mutation_timeout: nil,
292
+ mutation: Mutation::Config::DEFAULT,
292
293
  reporter: Reporter::CLI.build(WORLD.stdout),
293
294
  requires: EMPTY_ARRAY,
294
295
  zombie: false
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.11
4
+ version: 0.11.12
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-06-20 00:00:00.000000000 Z
11
+ date: 2022-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diff-lcs
@@ -249,6 +249,7 @@ files:
249
249
  - lib/mutant/meta/example/dsl.rb
250
250
  - lib/mutant/meta/example/verification.rb
251
251
  - lib/mutant/mutation.rb
252
+ - lib/mutant/mutation/config.rb
252
253
  - lib/mutant/mutator.rb
253
254
  - lib/mutant/mutator/node.rb
254
255
  - lib/mutant/mutator/node/and_asgn.rb