mutant 0.10.30 → 0.10.34

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: 92495691b68b4f556972ce8ec9bc8db2a30acb78980f509b27f2d12d4883b1f6
4
- data.tar.gz: b7e46f8a0e0544243aab53a9f873bc602ec52e83caa9de3e851e4415b8f15899
3
+ metadata.gz: ccd2a5c4c1c3ddf3b7eea82c613a144594609387a414cac729b75c4645295f47
4
+ data.tar.gz: e173ad25eb08658a040a7ca7cb6dca36a1d8bc9f01137e63fed081069b3ab9cb
5
5
  SHA512:
6
- metadata.gz: e7e49890adfcebdb4158d0e3d4afdc45589c49f1f66a4ecd700a7003dc3bf73acb967d6a51c1ea3c014edc1367ee1f5c79c4ebce8f5ca8c9dfac7f721649ad26
7
- data.tar.gz: 644ec9e3cb8a5d768b52d709aaf86222fdf588822d139f008aaed3aadd2a9606b0524760e4cbec230d3ba17012374006febd1cbad1eab9839e4166560e3add51
6
+ metadata.gz: bdebc9987d92b122b409715cc7933dc86952ae6dc58ed8f9a5525bcbfe8dc0aae93cf773622eced59643d27d4311df1f94f9c41a1abd1adc93e4a6b65771118d
7
+ data.tar.gz: 9e7d2dde25b8e532301475e8b7bc970626db00d36ce0abd5e4082aa2b39599a86372b4de93e5d61470f974f90539cf5445d51e242bcfbfec600ad8a5fca2b5aa
@@ -74,6 +74,7 @@ module Mutant
74
74
  [:regexp_katakana_property, [:property, :katakana, '\p{Katakana}'], ::Regexp::Expression::UnicodeProperty::Script],
75
75
  [:regexp_letter_property, [:property, :letter, '\p{L}'], ::Regexp::Expression::UnicodeProperty::Letter::Any],
76
76
  [:regexp_linebreak_type, [:type, :linebreak, '\R'], ::Regexp::Expression::CharacterType::Linebreak],
77
+ [:regexp_latin_property, [:property, :latin, '\p{Latin}'], ::Regexp::Expression::UnicodeProperty::Script],
77
78
  [:regexp_lower_posixclass, [:posixclass, :lower, '[:lower:]'], ::Regexp::Expression::PosixClass],
78
79
  [:regexp_mark_keep, [:keep, :mark, '\K'], ::Regexp::Expression::Keep::Mark],
79
80
  [:regexp_match_start_anchor, [:anchor, :match_start, '\\G'], ::Regexp::Expression::Anchor::MatchStart],
@@ -11,7 +11,9 @@ module Mutant
11
11
  class Transformer
12
12
  include AbstractType
13
13
 
14
- REGISTRY = Registry.new
14
+ REGISTRY = Registry.new(
15
+ ->(type) { fail "No regexp transformer registered for: #{type}" }
16
+ )
15
17
 
16
18
  # Lookup transformer class for regular expression node type
17
19
  #
@@ -107,6 +107,7 @@ module Mutant
107
107
  regexp_interval_close_escape
108
108
  regexp_interval_open_escape
109
109
  regexp_katakana_property
110
+ regexp_latin_property
110
111
  regexp_letter_property
111
112
  regexp_linebreak_type
112
113
  regexp_literal_escape
@@ -7,7 +7,7 @@ module Mutant
7
7
  class Run < self
8
8
  NAME = 'run'
9
9
  SHORT_DESCRIPTION = 'Run code analysis'
10
- SLEEP = 40
10
+ SLEEP = 60
11
11
  SUBCOMMANDS = EMPTY_ARRAY
12
12
 
13
13
  UNLICENSED = <<~MESSAGE.lines.freeze
@@ -16,6 +16,20 @@ module Mutant
16
16
  See https://github.com/mbj/mutant#licensing
17
17
  MESSAGE
18
18
 
19
+ NO_TESTS_MESSAGE = <<~'MESSAGE'
20
+ ===============
21
+ Mutant found no tests. Mutation testing cannot be started.
22
+
23
+ This can have various reasons:
24
+
25
+ * You did not setup an integration, see:
26
+ https://github.com/mbj/mutant/blob/main/docs/configuration.md#integration
27
+ * You set environment variables like RSPEC_OPTS that filter out all tests.
28
+ * You set configuration optiosn like `config.filter_run :focus` which do
29
+ make rspec to not report any test.
30
+ ===============
31
+ MESSAGE
32
+
19
33
  # Test if command needs to be executed in zombie environment
20
34
  #
21
35
  # @return [Bool]
@@ -28,10 +42,19 @@ module Mutant
28
42
  def action
29
43
  soft_fail(License.call(world))
30
44
  .bind { bootstrap }
45
+ .bind(&method(:validate_tests))
31
46
  .bind(&Runner.public_method(:call))
32
47
  .bind(&method(:from_result))
33
48
  end
34
49
 
50
+ def validate_tests(environment)
51
+ if environment.integration.all_tests.length.zero?
52
+ Either::Left.new(NO_TESTS_MESSAGE)
53
+ else
54
+ Either::Right.new(environment)
55
+ end
56
+ end
57
+
35
58
  def from_result(result)
36
59
  if result.success?
37
60
  Either::Right.new(nil)
@@ -10,7 +10,7 @@ module Mutant
10
10
  class Root < self
11
11
  NAME = 'mutant'
12
12
  SHORT_DESCRIPTION = 'mutation testing engine main command'
13
- SUBCOMMANDS = [Environment::Run, Environment, Subscription].freeze
13
+ SUBCOMMANDS = [Environment::Run, Environment, Subscription, Util].freeze
14
14
  end # Root
15
15
  end # Command
16
16
  end # CLI
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ module CLI
5
+ class Command
6
+ class Util < self
7
+ NAME = 'util'
8
+ SHORT_DESCRIPTION = 'Utility subcommands'
9
+
10
+ class Mutation < self
11
+ NAME = 'mutation'
12
+ SHORT_DESCRIPTION = 'Print mutations of a code snippet'
13
+ SUBCOMMANDS = [].freeze
14
+ OPTIONS = %i[add_target_options].freeze
15
+
16
+ def action
17
+ @targets.each(&method(:print_mutations))
18
+ Either::Right.new(nil)
19
+ end
20
+
21
+ private
22
+
23
+ class Target
24
+ include Adamantium
25
+
26
+ def node
27
+ Unparser.parse(source)
28
+ end
29
+ memoize :node
30
+
31
+ class File < self
32
+ include Concord.new(:pathname, :source)
33
+
34
+ public :source
35
+
36
+ def identification
37
+ "file:#{pathname}"
38
+ end
39
+ end # File
40
+
41
+ class Source < self
42
+ include Concord::Public.new(:source)
43
+
44
+ def identification
45
+ '<cli-source>'
46
+ end
47
+ end # source
48
+ end # Target
49
+
50
+ def initialize(_arguments)
51
+ super
52
+
53
+ @targets = []
54
+ end
55
+
56
+ def add_target_options(parser)
57
+ parser.on('-e', '--evaluate SOURCE') do |source|
58
+ @targets << Target::Source.new(source)
59
+ end
60
+ end
61
+
62
+ def print_mutations(target)
63
+ world.stdout.puts(target.identification)
64
+ Mutator.mutate(target.node).each do |mutation|
65
+ Reporter::CLI::Printer::Mutation.call(
66
+ world.stdout,
67
+ Mutant::Mutation::Evil.new(target, mutation)
68
+ )
69
+ end
70
+ end
71
+
72
+ def parse_remaining_arguments(arguments)
73
+ @targets.concat(
74
+ arguments.map do |argument|
75
+ parse_pathname(argument)
76
+ .bind(&method(:read_file))
77
+ .from_right { |error| return Either::Left.new(error) }
78
+ end
79
+ )
80
+
81
+ Either::Right.new(self)
82
+ end
83
+
84
+ def read_file(pathname)
85
+ Either::Right.new(Target::File.new(pathname, pathname.read))
86
+ rescue StandardError => exception
87
+ Either::Left.new("Cannot read file: #{exception}")
88
+ end
89
+
90
+ def parse_pathname(input)
91
+ Either.wrap_error(ArgumentError) { Pathname.new(input) }
92
+ .lmap(&:message)
93
+ end
94
+ end # Mutation
95
+
96
+ SUBCOMMANDS = [Mutation].freeze
97
+ end # Util
98
+ end # Command
99
+ end # CLI
100
+ end # Mutant
@@ -69,6 +69,8 @@ module Mutant
69
69
  false
70
70
  end
71
71
 
72
+ abstract_method :action
73
+
72
74
  private
73
75
 
74
76
  def subcommands
@@ -128,6 +130,8 @@ module Mutant
128
130
  end
129
131
 
130
132
  def add_global_options(parser)
133
+ parser.separator("mutant version: #{VERSION}")
134
+ parser.separator(nil)
131
135
  parser.separator('Global Options:')
132
136
  parser.separator(nil)
133
137
 
@@ -72,6 +72,15 @@ module Mutant
72
72
  )
73
73
  end
74
74
 
75
+ # Rendered mutation diff
76
+ #
77
+ # @return [String, nil]
78
+ # the diff, if present
79
+ def diff
80
+ Unparser::Diff.build(original_source, source)
81
+ end
82
+ memoize :diff
83
+
75
84
  private
76
85
 
77
86
  def sha1
@@ -80,7 +89,6 @@ module Mutant
80
89
 
81
90
  # Evil mutation that should case mutations to fail tests
82
91
  class Evil < self
83
-
84
92
  SYMBOL = 'evil'
85
93
  TEST_PASS_SUCCESS = false
86
94
 
@@ -21,8 +21,11 @@ module Mutant
21
21
  emit_singletons
22
22
  emit_promotions
23
23
  emit_operator_mutations
24
- emit_left_negation
25
- emit_left_mutations
24
+
25
+ emit_left_mutations do |mutation|
26
+ !(n_irange?(mutation) || n_erange?(mutation)) || !mutation.children.fetch(1).nil?
27
+ end
28
+
26
29
  emit_right_mutations
27
30
  end
28
31
 
@@ -35,10 +38,6 @@ module Mutant
35
38
  emit(right)
36
39
  end
37
40
 
38
- def emit_left_negation
39
- emit(s(node.type, n_not(left), right))
40
- end
41
-
42
41
  end # Binary
43
42
  end # Node
44
43
  end # Mutator
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ class Mutator
5
+ class Node
6
+ class Numblock < self
7
+
8
+ handle(:numblock)
9
+
10
+ children :receiver, :count, :block
11
+
12
+ private
13
+
14
+ def dispatch
15
+ emit_nil
16
+ emit_receiver_mutations(&method(:n_send?))
17
+ end
18
+ end # Numblock
19
+ end # Node
20
+ end # Mutator
21
+ end # Mutant
@@ -4,7 +4,7 @@ module Mutant
4
4
  # Generator for mutations
5
5
  class Mutator
6
6
 
7
- REGISTRY = Registry.new
7
+ REGISTRY = Registry.new(->(_) { Node::Generic })
8
8
 
9
9
  include(
10
10
  Adamantium,
@@ -29,9 +29,6 @@ module Mutant
29
29
  response = Pipe.from_io(io)
30
30
 
31
31
  pid = process.fork do
32
- request.reset_binmode
33
- response.reset_binmode
34
-
35
32
  world.thread.current.name = process_name
36
33
  world.process.setproctitle(process_name)
37
34
 
data/lib/mutant/pipe.rb CHANGED
@@ -35,18 +35,6 @@ module Mutant
35
35
  reader
36
36
  end
37
37
 
38
- # Set binmode (again)
39
- #
40
- # Ruby has a bug where the binmode setting may be lost duringa fork.
41
- # This API allows to set the binmode again.
42
- #
43
- # @return [self]
44
- def reset_binmode
45
- reader.binmode
46
- writer.binmode
47
- self
48
- end
49
-
50
38
  class Connection
51
39
  include Anima.new(:marshal, :reader, :writer)
52
40
 
@@ -69,6 +57,7 @@ module Mutant
69
57
 
70
58
  fail Error, 'message to big' if bytesize > MAX_BYTES
71
59
 
60
+ io.binmode
72
61
  io.write([bytesize].pack(HEADER_FORMAT))
73
62
  io.write(body)
74
63
  end
@@ -76,6 +65,7 @@ module Mutant
76
65
  private
77
66
 
78
67
  def read(bytes)
68
+ io.binmode
79
69
  io.read(bytes) or fail Error, 'Unexpected EOF'
80
70
  end
81
71
  end
@@ -3,13 +3,13 @@
3
3
  module Mutant
4
4
  # Registry for mapping AST types to classes
5
5
  class Registry
6
- include Concord.new(:contents)
6
+ include Concord.new(:contents, :default)
7
7
 
8
8
  # Initialize object
9
9
  #
10
10
  # @return [undefined]
11
- def initialize
12
- super({})
11
+ def initialize(default)
12
+ super({}, default)
13
13
  end
14
14
 
15
15
  # Raised when the type is an invalid type
@@ -34,7 +34,7 @@ module Mutant
34
34
  #
35
35
  # @return [Class<Mutator>]
36
36
  def lookup(type)
37
- contents.fetch(type, Mutator::Node::Generic)
37
+ contents.fetch(type, &default)
38
38
  end
39
39
 
40
40
  end # Registry
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ class Reporter
5
+ class CLI
6
+ class Printer
7
+ # Reporter for mutations
8
+ class Mutation < self
9
+ NO_DIFF_MESSAGE = <<~'MESSAGE'
10
+ --- Internal failure ---
11
+ BUG: A generated mutation did not result in exactly one diff hunk!
12
+ This is an invariant violation by the mutation generation engine.
13
+ Please report a reproduction to https://github.com/mbj/mutant
14
+ Original unparsed source:
15
+ %s
16
+ Original AST:
17
+ %s
18
+ Mutated unparsed source:
19
+ %s
20
+ Mutated AST:
21
+ %s
22
+ MESSAGE
23
+
24
+ SEPARATOR = '-----------------------'
25
+
26
+ # Run report printer
27
+ #
28
+ # @return [undefined]
29
+ def run
30
+ diff = object.diff
31
+ diff = color? ? diff.colorized_diff : diff.diff
32
+
33
+ if diff
34
+ output.write(diff)
35
+ else
36
+ print_no_diff_message
37
+ end
38
+ end
39
+
40
+ def print_no_diff_message
41
+ info(
42
+ NO_DIFF_MESSAGE,
43
+ object.original_source,
44
+ original_node.inspect,
45
+ object.source,
46
+ object.node.inspect
47
+ )
48
+ end
49
+
50
+ def original_node
51
+ object.subject.node
52
+ end
53
+
54
+ end # MutationResult
55
+ end # Printer
56
+ end # CLI
57
+ end # Reporter
58
+ end # Mutant
@@ -27,7 +27,7 @@ module Mutant
27
27
 
28
28
  NO_DIFF_MESSAGE = <<~'MESSAGE'
29
29
  --- Internal failure ---
30
- BUG: A generted mutation did not result in exactly one diff hunk!
30
+ BUG: A generated mutation did not result in exactly one diff hunk!
31
31
  This is an invariant violation by the mutation generation engine.
32
32
  Please report a reproduction to https://github.com/mbj/mutant
33
33
  Original unparsed source:
@@ -66,23 +66,7 @@ module Mutant
66
66
  end
67
67
 
68
68
  def evil_details
69
- diff = Unparser::Diff.build(mutation.original_source, mutation.source)
70
- diff = color? ? diff.colorized_diff : diff.diff
71
- if diff
72
- output.write(diff)
73
- else
74
- print_no_diff_message
75
- end
76
- end
77
-
78
- def print_no_diff_message
79
- info(
80
- NO_DIFF_MESSAGE,
81
- mutation.original_source,
82
- original_node.inspect,
83
- mutation.source,
84
- mutation.node.inspect
85
- )
69
+ visit(Mutation, mutation)
86
70
  end
87
71
 
88
72
  def noop_details
@@ -90,11 +74,7 @@ module Mutant
90
74
  end
91
75
 
92
76
  def neutral_details
93
- info(NEUTRAL_MESSAGE, original_node.inspect, mutation.source)
94
- end
95
-
96
- def original_node
97
- mutation.subject.node
77
+ info(NEUTRAL_MESSAGE, mutation.node.inspect, mutation.source)
98
78
  end
99
79
 
100
80
  end # MutationResult
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Mutant
4
4
  # Current mutant version
5
- VERSION = '0.10.30'
5
+ VERSION = '0.10.34'
6
6
  end # Mutant
data/lib/mutant.rb CHANGED
@@ -152,6 +152,7 @@ require 'mutant/mutator/node/match_current_line'
152
152
  require 'mutant/mutator/node/index'
153
153
  require 'mutant/mutator/node/procarg_zero'
154
154
  require 'mutant/mutator/node/kwargs'
155
+ require 'mutant/mutator/node/numblock'
155
156
  require 'mutant/loader'
156
157
  require 'mutant/context'
157
158
  require 'mutant/scope'
@@ -197,6 +198,7 @@ require 'mutant/cli/command/environment/run'
197
198
  require 'mutant/cli/command/environment/show'
198
199
  require 'mutant/cli/command/environment/subject'
199
200
  require 'mutant/cli/command/environment/test'
201
+ require 'mutant/cli/command/util'
200
202
  require 'mutant/cli/command/root'
201
203
  require 'mutant/runner'
202
204
  require 'mutant/runner/sink'
@@ -212,6 +214,7 @@ require 'mutant/reporter/cli/printer/env'
212
214
  require 'mutant/reporter/cli/printer/env_progress'
213
215
  require 'mutant/reporter/cli/printer/env_result'
214
216
  require 'mutant/reporter/cli/printer/isolation_result'
217
+ require 'mutant/reporter/cli/printer/mutation'
215
218
  require 'mutant/reporter/cli/printer/mutation_result'
216
219
  require 'mutant/reporter/cli/printer/status_progressive'
217
220
  require 'mutant/reporter/cli/printer/subject_result'
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.10.30
4
+ version: 0.10.34
5
5
  platform: ruby
6
6
  authors:
7
7
  - Markus Schirp
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-25 00:00:00.000000000 Z
11
+ date: 2021-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diff-lcs
@@ -186,6 +186,7 @@ files:
186
186
  - lib/mutant/cli/command/environment/test.rb
187
187
  - lib/mutant/cli/command/root.rb
188
188
  - lib/mutant/cli/command/subscription.rb
189
+ - lib/mutant/cli/command/util.rb
189
190
  - lib/mutant/config.rb
190
191
  - lib/mutant/config/coverage_criteria.rb
191
192
  - lib/mutant/context.rb
@@ -268,6 +269,7 @@ files:
268
269
  - lib/mutant/mutator/node/next.rb
269
270
  - lib/mutant/mutator/node/noop.rb
270
271
  - lib/mutant/mutator/node/nthref.rb
272
+ - lib/mutant/mutator/node/numblock.rb
271
273
  - lib/mutant/mutator/node/op_asgn.rb
272
274
  - lib/mutant/mutator/node/or_asgn.rb
273
275
  - lib/mutant/mutator/node/procarg_zero.rb
@@ -316,6 +318,7 @@ files:
316
318
  - lib/mutant/reporter/cli/printer/env_progress.rb
317
319
  - lib/mutant/reporter/cli/printer/env_result.rb
318
320
  - lib/mutant/reporter/cli/printer/isolation_result.rb
321
+ - lib/mutant/reporter/cli/printer/mutation.rb
319
322
  - lib/mutant/reporter/cli/printer/mutation_result.rb
320
323
  - lib/mutant/reporter/cli/printer/status_progressive.rb
321
324
  - lib/mutant/reporter/cli/printer/subject_result.rb
@@ -364,7 +367,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
364
367
  - !ruby/object:Gem::Version
365
368
  version: '0'
366
369
  requirements: []
367
- rubygems_version: 3.0.3
370
+ rubygems_version: 3.1.6
368
371
  signing_key:
369
372
  specification_version: 4
370
373
  summary: ''