mutant 0.10.31 → 0.10.35

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: 4ef901ceb41d6239377dac9b9c7c8cc908775e76f12dee66998087f3f6ef70a0
4
- data.tar.gz: fc6b949b0bb8c8353cdab6186307daf67e5274faded923aaa8941dd0f5219de3
3
+ metadata.gz: b90188339d2ae50f1bc48723f56dc2740b6932a99cd8aae263543734aa66c494
4
+ data.tar.gz: dcd1e40f9dc9b37467ec067142da4f2c1c6351191de235b2a88237c16b6b936c
5
5
  SHA512:
6
- metadata.gz: c05c611a7cc443e82d278fcaac02f1b43413b30dff81c602d468aea5256cb3389850c414f5415fbd6d7fa67aac035669c325edf3ffccaa915fa70cb03b8dbdc0
7
- data.tar.gz: e171127271fc59b0d66a9050e9cb9398e5f0caecf13df14289e9acf1f7940cefa9b9760c051655c41090a5cb2235e18465b7dde72b4ed27ac21b80a745850cc0
6
+ metadata.gz: 323cc89a39fb5163c7778d5a9ae7e989e207c974436207803d91118c502e17219b47514573d24d2e9003043bb170744110e68c9fa8dc2201236566d1a2f701c7
7
+ data.tar.gz: 4f27f289f727c157237f65a15241b8adce8f91027894690994908eb698895a4c81f57344138e7c1a010454aee06776a41460f1ed37a4f164871b58e71291c1ca
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ module CLI
5
+ class Command
6
+ class Environment
7
+ class IRB < self
8
+ NAME = 'irb'
9
+ SHORT_DESCRIPTION = 'Run irb with mutant environment loaded'
10
+ SUBCOMMANDS = EMPTY_ARRAY
11
+
12
+ private
13
+
14
+ def action
15
+ bootstrap.fmap { TOPLEVEL_BINDING.irb }
16
+ end
17
+ end # IRB
18
+ end # Environment
19
+ end # Command
20
+ end # CLI
21
+ end # Mutant
@@ -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)
@@ -4,13 +4,13 @@ module Mutant
4
4
  module CLI
5
5
  class Command
6
6
  class Environment < self
7
- SUBCOMMANDS = [Environment::Subject, Environment::Show, Environment::Test].freeze
7
+ SUBCOMMANDS = [Environment::Subject, Environment::Show, Environment::IRB, Environment::Test].freeze
8
8
  end # Environment
9
9
 
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
@@ -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
@@ -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.31'
5
+ VERSION = '0.10.35'
6
6
  end # Mutant
data/lib/mutant.rb CHANGED
@@ -4,6 +4,7 @@ require 'diff/lcs'
4
4
  require 'diff/lcs/hunk'
5
5
  require 'digest/sha1'
6
6
  require 'etc'
7
+ require 'irb'
7
8
  require 'json'
8
9
  require 'open3'
9
10
  require 'optparse'
@@ -152,6 +153,7 @@ require 'mutant/mutator/node/match_current_line'
152
153
  require 'mutant/mutator/node/index'
153
154
  require 'mutant/mutator/node/procarg_zero'
154
155
  require 'mutant/mutator/node/kwargs'
156
+ require 'mutant/mutator/node/numblock'
155
157
  require 'mutant/loader'
156
158
  require 'mutant/context'
157
159
  require 'mutant/scope'
@@ -193,10 +195,12 @@ require 'mutant/cli'
193
195
  require 'mutant/cli/command'
194
196
  require 'mutant/cli/command/subscription'
195
197
  require 'mutant/cli/command/environment'
198
+ require 'mutant/cli/command/environment/irb'
196
199
  require 'mutant/cli/command/environment/run'
197
200
  require 'mutant/cli/command/environment/show'
198
201
  require 'mutant/cli/command/environment/subject'
199
202
  require 'mutant/cli/command/environment/test'
203
+ require 'mutant/cli/command/util'
200
204
  require 'mutant/cli/command/root'
201
205
  require 'mutant/runner'
202
206
  require 'mutant/runner/sink'
@@ -212,6 +216,7 @@ require 'mutant/reporter/cli/printer/env'
212
216
  require 'mutant/reporter/cli/printer/env_progress'
213
217
  require 'mutant/reporter/cli/printer/env_result'
214
218
  require 'mutant/reporter/cli/printer/isolation_result'
219
+ require 'mutant/reporter/cli/printer/mutation'
215
220
  require 'mutant/reporter/cli/printer/mutation_result'
216
221
  require 'mutant/reporter/cli/printer/status_progressive'
217
222
  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.31
4
+ version: 0.10.35
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-05-03 00:00:00.000000000 Z
11
+ date: 2021-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diff-lcs
@@ -180,12 +180,14 @@ files:
180
180
  - lib/mutant/cli.rb
181
181
  - lib/mutant/cli/command.rb
182
182
  - lib/mutant/cli/command/environment.rb
183
+ - lib/mutant/cli/command/environment/irb.rb
183
184
  - lib/mutant/cli/command/environment/run.rb
184
185
  - lib/mutant/cli/command/environment/show.rb
185
186
  - lib/mutant/cli/command/environment/subject.rb
186
187
  - lib/mutant/cli/command/environment/test.rb
187
188
  - lib/mutant/cli/command/root.rb
188
189
  - lib/mutant/cli/command/subscription.rb
190
+ - lib/mutant/cli/command/util.rb
189
191
  - lib/mutant/config.rb
190
192
  - lib/mutant/config/coverage_criteria.rb
191
193
  - lib/mutant/context.rb
@@ -268,6 +270,7 @@ files:
268
270
  - lib/mutant/mutator/node/next.rb
269
271
  - lib/mutant/mutator/node/noop.rb
270
272
  - lib/mutant/mutator/node/nthref.rb
273
+ - lib/mutant/mutator/node/numblock.rb
271
274
  - lib/mutant/mutator/node/op_asgn.rb
272
275
  - lib/mutant/mutator/node/or_asgn.rb
273
276
  - lib/mutant/mutator/node/procarg_zero.rb
@@ -316,6 +319,7 @@ files:
316
319
  - lib/mutant/reporter/cli/printer/env_progress.rb
317
320
  - lib/mutant/reporter/cli/printer/env_result.rb
318
321
  - lib/mutant/reporter/cli/printer/isolation_result.rb
322
+ - lib/mutant/reporter/cli/printer/mutation.rb
319
323
  - lib/mutant/reporter/cli/printer/mutation_result.rb
320
324
  - lib/mutant/reporter/cli/printer/status_progressive.rb
321
325
  - lib/mutant/reporter/cli/printer/subject_result.rb
@@ -357,14 +361,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
357
361
  requirements:
358
362
  - - ">="
359
363
  - !ruby/object:Gem::Version
360
- version: '2.5'
364
+ version: '2.6'
361
365
  required_rubygems_version: !ruby/object:Gem::Requirement
362
366
  requirements:
363
367
  - - ">="
364
368
  - !ruby/object:Gem::Version
365
369
  version: '0'
366
370
  requirements: []
367
- rubygems_version: 3.2.15
371
+ rubygems_version: 3.1.6
368
372
  signing_key:
369
373
  specification_version: 4
370
374
  summary: ''