mutant 0.8.23 → 0.8.24

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +6 -0
  3. data/Gemfile.lock +3 -3
  4. data/README.md +2 -0
  5. data/config/rubocop.yml +0 -4
  6. data/lib/mutant.rb +2 -2
  7. data/lib/mutant/actor.rb +1 -1
  8. data/lib/mutant/ast/meta/optarg.rb +1 -1
  9. data/lib/mutant/ast/meta/send.rb +1 -1
  10. data/lib/mutant/ast/regexp.rb +6 -1
  11. data/lib/mutant/context.rb +1 -1
  12. data/lib/mutant/diff.rb +3 -3
  13. data/lib/mutant/env.rb +1 -1
  14. data/lib/mutant/env/bootstrap.rb +3 -3
  15. data/lib/mutant/expression.rb +1 -1
  16. data/lib/mutant/loader.rb +5 -1
  17. data/lib/mutant/matcher/config.rb +5 -5
  18. data/lib/mutant/matcher/method.rb +2 -2
  19. data/lib/mutant/matcher/method/singleton.rb +1 -1
  20. data/lib/mutant/mutation.rb +4 -4
  21. data/lib/mutant/mutator/node/argument.rb +1 -1
  22. data/lib/mutant/mutator/node/block.rb +23 -4
  23. data/lib/mutant/mutator/node/literal/array.rb +3 -1
  24. data/lib/mutant/mutator/node/literal/regex.rb +5 -5
  25. data/lib/mutant/mutator/node/literal/symbol.rb +1 -1
  26. data/lib/mutant/mutator/util/symbol.rb +1 -1
  27. data/lib/mutant/reporter/cli/format.rb +1 -1
  28. data/lib/mutant/reporter/cli/printer.rb +1 -1
  29. data/lib/mutant/reporter/cli/printer/mutation_progress_result.rb +2 -2
  30. data/lib/mutant/reporter/cli/printer/status.rb +2 -2
  31. data/lib/mutant/reporter/cli/printer/status_progressive.rb +1 -1
  32. data/lib/mutant/reporter/cli/printer/subject_progress.rb +1 -1
  33. data/lib/mutant/reporter/cli/printer/test_result.rb +3 -3
  34. data/lib/mutant/repository.rb +2 -1
  35. data/lib/mutant/subject/method/instance.rb +1 -1
  36. data/lib/mutant/subject/method/singleton.rb +1 -1
  37. data/lib/mutant/version.rb +1 -1
  38. data/meta/block.rb +81 -0
  39. data/meta/file.rb +5 -0
  40. data/meta/lambda.rb +14 -0
  41. data/meta/line.rb +5 -0
  42. data/meta/lvasgn.rb +15 -0
  43. data/meta/regexp.rb +9 -0
  44. data/spec/integrations.yml +3 -8
  45. data/spec/support/corpus.rb +5 -5
  46. data/spec/support/warning.rb +1 -1
  47. data/spec/unit/mutant/ast/regexp/parse_spec.rb +14 -2
  48. data/spec/unit/mutant/loader_spec.rb +1 -1
  49. data/spec/unit/mutant/mutation_spec.rb +1 -1
  50. data/spec/unit/mutant/repository/diff_spec.rb +1 -1
  51. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: da1d34448bf320507db377b41c46f591022e69e02597f4097a9a8ff52cf7b7a8
4
- data.tar.gz: fd4a4c3d03d89ec53e5e7a040c0f3e4854d58d2bd1347404f06cae2541db742c
3
+ metadata.gz: dd93463f0b9eb83512dc15c039e8fd58d7bba5715bbdffc2a39a8a8ad380d317
4
+ data.tar.gz: b0ed77d675e680fffdf7d90a7c5b8656aa4ba139226d45d3a728d468e5c3c92a
5
5
  SHA512:
6
- metadata.gz: e64e8ed9f28ee391b704aa37d18c4ec204ad3c73af56e3fed7d4430399bea663a599a0561d8e567b7d4fa9416b53ba810a5df95529c47345dfe18781a2367be0
7
- data.tar.gz: be1ec3a98dfbf5b55dbc8543cf61a0dcf6a187232d0fd3d640ddc31533aaa1ed658a433f36bdc1158eb81df4774da13df737a8c59dd4e331369045957da7d226
6
+ metadata.gz: aaa7a56cc2dbdcab8748d7eb07bf7b64c8ad616bff18d51b89018812a88bcb43fb99487e8241ed52fb96ae3a3ed61c0e67b39ec559863f265b66cbf933189dbf
7
+ data.tar.gz: 0c6884921c5cf564d7155e0e3a8edf6163f637200540ab51fe044d4b5a14e783f083ddcdcd8aafbc71278f737e78a95ea0728389b6162d35e3db24a499d5ca46
@@ -1,3 +1,9 @@
1
+ # v0.8.24 2018-12-29
2
+
3
+ * Change to always insert mutations with frozen string literals
4
+ * Fix various invalid AST or source mutations
5
+ * Handle regexp `regexp_parser` cannot parse but MRI accepts gracefully
6
+
1
7
  # v0.8.23 2018-12-23
2
8
 
3
9
  * Improved isolation error reporting
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mutant (0.8.22)
4
+ mutant (0.8.24)
5
5
  abstract_type (~> 0.0.7)
6
6
  adamantium (~> 0.2.0)
7
7
  anima (~> 0.3.0)
@@ -16,8 +16,8 @@ PATH
16
16
  procto (~> 0.0.2)
17
17
  regexp_parser (~> 1.2)
18
18
  unparser (~> 0.4.2)
19
- mutant-rspec (0.8.22)
20
- mutant (~> 0.8.22)
19
+ mutant-rspec (0.8.24)
20
+ mutant (~> 0.8.24)
21
21
  rspec-core (>= 3.4.0, < 4.0.0)
22
22
 
23
23
  GEM
data/README.md CHANGED
@@ -93,6 +93,8 @@ Running mutant for the first time on an existing codebase can be a rather dishea
93
93
  bundle exec mutant --include lib --require virtus --since master --use rspec Virtus::Attribute#type
94
94
  ```
95
95
 
96
+ Note that this feature requires at least git `2.13.0`.
97
+
96
98
  Presentations
97
99
  -------------
98
100
 
@@ -150,10 +150,6 @@ SignalException:
150
150
  Alias:
151
151
  EnforcedStyle: prefer_alias_method
152
152
 
153
- # Teaching people to ignore singleton class is pointless
154
- RedundantFreeze:
155
- Enabled: false
156
-
157
153
  # Do not waste my horizontal or vertical space
158
154
  IndentArray:
159
155
  Enabled: false
@@ -29,10 +29,10 @@ Thread.abort_on_exception = true
29
29
  #
30
30
  # @api private
31
31
  module Mutant
32
- EMPTY_STRING = ''.freeze
32
+ EMPTY_STRING = ''
33
33
  EMPTY_ARRAY = [].freeze
34
34
  EMPTY_HASH = {}.freeze
35
- SCOPE_OPERATOR = '::'.freeze
35
+ SCOPE_OPERATOR = '::'
36
36
 
37
37
  # Test if CI is detected via environment
38
38
  #
@@ -10,7 +10,7 @@ module Mutant
10
10
 
11
11
  # Undefined message payload
12
12
  Undefined = Class.new do
13
- INSPECT = 'Mutant::Actor::Undefined'.freeze
13
+ INSPECT = 'Mutant::Actor::Undefined'
14
14
 
15
15
  # Object inspection
16
16
  #
@@ -9,7 +9,7 @@ module Mutant
9
9
  class Optarg
10
10
  include NamedChildren, Concord.new(:node)
11
11
 
12
- UNDERSCORE = '_'.freeze
12
+ UNDERSCORE = '_'
13
13
 
14
14
  children :name, :default_value
15
15
 
@@ -14,7 +14,7 @@ module Mutant
14
14
  public :receiver, :selector
15
15
 
16
16
  INDEX_ASSIGNMENT_SELECTOR = :[]=
17
- ATTRIBUTE_ASSIGNMENT_SELECTOR_SUFFIX = '='.freeze
17
+ ATTRIBUTE_ASSIGNMENT_SELECTOR_SUFFIX = '='
18
18
 
19
19
  # Arguments of mutated node
20
20
  #
@@ -8,10 +8,15 @@ module Mutant
8
8
  #
9
9
  # @param regexp [String]
10
10
  #
11
- # @return [Regexp::Expression]
11
+ # @return [Regexp::Expression, nil]
12
+ #
13
+ # rubocop:disable Lint/HandleExceptions
12
14
  def self.parse(regexp)
13
15
  ::Regexp::Parser.parse(regexp)
16
+ # regexp_parser is more strict than MRI
17
+ rescue ::Regexp::Scanner::PrematureEndError
14
18
  end
19
+ # rubocop:enable Lint/HandleExceptions
15
20
 
16
21
  # Convert expression into ast node
17
22
  #
@@ -6,7 +6,7 @@ module Mutant
6
6
  include Adamantium::Flat, Concord::Public.new(:scope, :source_path)
7
7
  extend AST::Sexp
8
8
 
9
- NAMESPACE_DELIMITER = '::'.freeze
9
+ NAMESPACE_DELIMITER = '::'
10
10
 
11
11
  # Return root node for mutation
12
12
  #
@@ -5,9 +5,9 @@ module Mutant
5
5
  class Diff
6
6
  include Adamantium::Flat, Concord.new(:old, :new)
7
7
 
8
- ADDITION = '+'.freeze
9
- DELETION = '-'.freeze
10
- NEWLINE = "\n".freeze
8
+ ADDITION = '+'
9
+ DELETION = '-'
10
+ NEWLINE = "\n"
11
11
 
12
12
  # Unified source diff between old and new
13
13
  #
@@ -16,7 +16,7 @@ module Mutant
16
16
 
17
17
  SEMANTICS_MESSAGE =
18
18
  "Fix your lib to follow normal ruby semantics!\n" \
19
- '{Module,Class}#name should return resolvable constant name as String or nil'.freeze
19
+ '{Module,Class}#name should return resolvable constant name as String or nil'
20
20
 
21
21
  # Kill mutation
22
22
  #
@@ -8,13 +8,13 @@ module Mutant
8
8
 
9
9
  SEMANTICS_MESSAGE_FORMAT =
10
10
  "%<message>s. Fix your lib to follow normal ruby semantics!\n" \
11
- '{Module,Class}#name should return resolvable constant name as String or nil'.freeze
11
+ '{Module,Class}#name should return resolvable constant name as String or nil'
12
12
 
13
13
  CLASS_NAME_RAISED_EXCEPTION =
14
- '%<scope_class>s#name from: %<scope>s raised an error: %<exception>s'.freeze
14
+ '%<scope_class>s#name from: %<scope>s raised an error: %<exception>s'
15
15
 
16
16
  CLASS_NAME_TYPE_MISMATCH_FORMAT =
17
- '%<scope_class>s#name from: %<scope>s returned %<name>s'.freeze
17
+ '%<scope_class>s#name from: %<scope>s returned %<name>s'
18
18
 
19
19
  private_constant(*constants(false))
20
20
 
@@ -8,7 +8,7 @@ module Mutant
8
8
 
9
9
  fragment = /[A-Za-z][A-Za-z\d_]*/.freeze
10
10
  SCOPE_NAME_PATTERN = /(?<scope_name>#{fragment}(?:#{SCOPE_OPERATOR}#{fragment})*)/.freeze
11
- SCOPE_SYMBOL_PATTERN = '(?<scope_symbol>[.#])'.freeze
11
+ SCOPE_SYMBOL_PATTERN = '(?<scope_symbol>[.#])'
12
12
 
13
13
  private_constant(*constants(false))
14
14
 
@@ -5,6 +5,10 @@ module Mutant
5
5
  class Loader
6
6
  include Anima.new(:binding, :kernel, :source, :subject)
7
7
 
8
+ FROZEN_STRING_FORMAT = "# frozen_string_literal: true\n%s"
9
+
10
+ private_constant(*constants(false))
11
+
8
12
  # Call loader
9
13
  #
10
14
  # @return [self]
@@ -19,7 +23,7 @@ module Mutant
19
23
  # @return [undefined]
20
24
  def call
21
25
  kernel.eval(
22
- source,
26
+ FROZEN_STRING_FORMAT % source,
23
27
  binding,
24
28
  subject.source_path.to_s,
25
29
  subject.source_line
@@ -12,11 +12,11 @@ module Mutant
12
12
  :subject_filters
13
13
  )
14
14
 
15
- INSPECT_FORMAT = "#<#{self} %s>".freeze
16
- ATTRIBUTE_DELIMITER = ' '.freeze
17
- ATTRIBUTE_FORMAT = '%s: [%s]'.freeze
18
- ENUM_DELIMITER = ','.freeze
19
- EMPTY_ATTRIBUTES = 'empty'.freeze
15
+ INSPECT_FORMAT = "#<#{self} %s>"
16
+ ATTRIBUTE_DELIMITER = ' '
17
+ ATTRIBUTE_FORMAT = '%s: [%s]'
18
+ ENUM_DELIMITER = ','
19
+ EMPTY_ATTRIBUTES = 'empty'
20
20
  PRESENTATIONS = IceNine.deep_freeze(
21
21
  ignore_expressions: :syntax,
22
22
  match_expressions: :syntax,
@@ -13,10 +13,10 @@ module Mutant
13
13
  BLACKLIST = %r{\A(kernel/|\(eval\)\z)}.freeze
14
14
 
15
15
  SOURCE_LOCATION_WARNING_FORMAT =
16
- '%s does not have a valid source location, unable to emit subject'.freeze
16
+ '%s does not have a valid source location, unable to emit subject'
17
17
 
18
18
  CLOSURE_WARNING_FORMAT =
19
- '%s is dynamically defined in a closure, unable to emit subject'.freeze
19
+ '%s is dynamically defined in a closure, unable to emit subject'
20
20
 
21
21
  # Matched subjects
22
22
  #
@@ -21,7 +21,7 @@ module Mutant
21
21
  SUBJECT_CLASS = Subject::Method::Singleton
22
22
  RECEIVER_INDEX = 0
23
23
  NAME_INDEX = 1
24
- RECEIVER_WARNING = 'Can only match :defs on :self or :const got %p unable to match'.freeze
24
+ RECEIVER_WARNING = 'Can only match :defs on :self or :const got %p unable to match'
25
25
 
26
26
  private
27
27
 
@@ -6,7 +6,7 @@ module Mutant
6
6
  include AbstractType, Adamantium::Flat
7
7
  include Concord::Public.new(:subject, :node)
8
8
 
9
- CODE_DELIMITER = "\0".freeze
9
+ CODE_DELIMITER = "\0"
10
10
  CODE_RANGE = (0..4).freeze
11
11
 
12
12
  # Identification string
@@ -86,7 +86,7 @@ module Mutant
86
86
  # Evil mutation that should case mutations to fail tests
87
87
  class Evil < self
88
88
 
89
- SYMBOL = 'evil'.freeze
89
+ SYMBOL = 'evil'
90
90
  TEST_PASS_SUCCESS = false
91
91
 
92
92
  end # Evil
@@ -94,7 +94,7 @@ module Mutant
94
94
  # Neutral mutation that should not cause mutations to fail tests
95
95
  class Neutral < self
96
96
 
97
- SYMBOL = 'neutral'.freeze
97
+ SYMBOL = 'neutral'
98
98
  TEST_PASS_SUCCESS = true
99
99
 
100
100
  end # Neutral
@@ -102,7 +102,7 @@ module Mutant
102
102
  # Noop mutation, special case of neutral
103
103
  class Noop < Neutral
104
104
 
105
- SYMBOL = 'noop'.freeze
105
+ SYMBOL = 'noop'
106
106
  TEST_PASS_SUCCESS = true
107
107
 
108
108
  end # Noop
@@ -8,7 +8,7 @@ module Mutant
8
8
  class Argument < self
9
9
  handle(:arg, :kwarg)
10
10
 
11
- UNDERSCORE = '_'.freeze
11
+ UNDERSCORE = '_'
12
12
 
13
13
  children :name
14
14
 
@@ -3,7 +3,6 @@
3
3
  module Mutant
4
4
  class Mutator
5
5
  class Node
6
- # Emitter for mutations on 19 blocks
7
6
  class Block < self
8
7
 
9
8
  handle(:block)
@@ -18,7 +17,7 @@ module Mutant
18
17
  def dispatch
19
18
  emit_singletons
20
19
  emit(send) unless n_lambda?(send)
21
- emit_send_mutations(&method(:n_send?))
20
+ emit_send_mutations(&method(:valid_send_mutation?))
22
21
  emit_arguments_mutations
23
22
 
24
23
  mutate_body
@@ -32,23 +31,43 @@ module Mutant
32
31
  emit_body(N_RAISE)
33
32
 
34
33
  return unless body
35
- emit(body)
34
+ emit(body) unless body_has_control?
36
35
  emit_body_mutations
37
36
 
38
37
  mutate_body_receiver
39
38
  end
40
39
 
40
+ # Test if body has control structures
41
+ #
42
+ # @return [Boolean]
43
+ def body_has_control?
44
+ AST.find_last_path(body) do |node|
45
+ n_break?(node) || n_next?(node)
46
+ end.any?
47
+ end
48
+
41
49
  # Mutate method send in body scope of `send`
42
50
  #
43
51
  # @return [undefined]
44
52
  def mutate_body_receiver
45
- return unless n_send?(body)
53
+ return if n_lambda?(send) || !n_send?(body)
46
54
 
47
55
  body_meta = AST::Meta::Send.new(body)
48
56
 
49
57
  emit(s(:send, send, body_meta.selector, *body_meta.arguments))
50
58
  end
51
59
 
60
+ # Test for valid send mutations
61
+ #
62
+ # @return [true, false, nil]
63
+ def valid_send_mutation?(node)
64
+ return unless n_send?(node)
65
+
66
+ last = AST::Meta::Send.new(node).arguments.last
67
+
68
+ !last&.type.equal?(:block_pass)
69
+ end
70
+
52
71
  end # Block
53
72
  end # Node
54
73
  end # Mutator
@@ -9,6 +9,8 @@ module Mutant
9
9
 
10
10
  handle(:array)
11
11
 
12
+ children :first
13
+
12
14
  private
13
15
 
14
16
  # Emit mutations
@@ -19,7 +21,7 @@ module Mutant
19
21
  emit_type
20
22
  mutate_body
21
23
  return unless children.one?
22
- emit(children.first)
24
+ emit(first) unless n_splat?(first)
23
25
  end
24
26
 
25
27
  # Mutate body
@@ -10,7 +10,7 @@ module Mutant
10
10
  handle(:regexp)
11
11
 
12
12
  # No input can ever be matched with this
13
- NULL_REGEXP_SOURCE = 'nomatch\A'.freeze
13
+ NULL_REGEXP_SOURCE = 'nomatch\A'
14
14
 
15
15
  private
16
16
 
@@ -42,7 +42,7 @@ module Mutant
42
42
  #
43
43
  # @return [undefined]
44
44
  def mutate_body
45
- return unless body.all?(&method(:n_str?))
45
+ return unless body.all?(&method(:n_str?)) && body_ast
46
46
 
47
47
  Mutator.mutate(body_ast).each do |mutation|
48
48
  source = AST::Regexp.to_expression(mutation).to_s
@@ -52,14 +52,14 @@ module Mutant
52
52
 
53
53
  # AST representation of regexp body
54
54
  #
55
- # @return [Parser::AST::Node]
55
+ # @return [Parser::AST::Node, nil]
56
56
  def body_ast
57
- AST::Regexp.to_ast(body_expression)
57
+ body_expression and AST::Regexp.to_ast(body_expression)
58
58
  end
59
59
 
60
60
  # Expression representation of regexp body
61
61
  #
62
- # @return [Regexp::Expression]
62
+ # @return [Regexp::Expression, nil]
63
63
  def body_expression
64
64
  AST::Regexp.parse(body.map(&:children).join)
65
65
  end
@@ -11,7 +11,7 @@ module Mutant
11
11
 
12
12
  children :value
13
13
 
14
- PREFIX = '__mutant__'.freeze
14
+ PREFIX = '__mutant__'
15
15
 
16
16
  private
17
17
 
@@ -7,7 +7,7 @@ module Mutant
7
7
  # Utility symbol mutator
8
8
  class Symbol < self
9
9
 
10
- POSTFIX = '__mutant__'.freeze
10
+ POSTFIX = '__mutant__'
11
11
 
12
12
  private
13
13
 
@@ -97,7 +97,7 @@ module Mutant
97
97
  class Framed < self
98
98
  include anima.add(:tput)
99
99
 
100
- BUFFER_FLAGS = 'a+'.freeze
100
+ BUFFER_FLAGS = 'a+'
101
101
 
102
102
  REPORT_FREQUENCY = 20.0
103
103
  REPORT_DELAY = 1 / REPORT_FREQUENCY
@@ -35,7 +35,7 @@ module Mutant
35
35
 
36
36
  delegate :success?
37
37
 
38
- NL = "\n".freeze
38
+ NL = "\n"
39
39
 
40
40
  # Run printer
41
41
  #
@@ -6,8 +6,8 @@ module Mutant
6
6
  class Printer
7
7
  # Printer for mutation progress results
8
8
  class MutationProgressResult < self
9
- SUCCESS = '.'.freeze
10
- FAILURE = 'F'.freeze
9
+ SUCCESS = '.'
10
+ FAILURE = 'F'
11
11
 
12
12
  # Run printer
13
13
  #
@@ -9,8 +9,8 @@ module Mutant
9
9
 
10
10
  delegate(:active_jobs, :payload)
11
11
 
12
- ACTIVE_JOB_HEADER = 'Active Jobs:'.freeze
13
- ACTIVE_JOB_FORMAT = '%d: %s'.freeze
12
+ ACTIVE_JOB_HEADER = 'Active Jobs:'
13
+ ACTIVE_JOB_FORMAT = '%d: %s'
14
14
 
15
15
  # Print progress for collector
16
16
  #
@@ -6,7 +6,7 @@ module Mutant
6
6
  class Printer
7
7
  # Reporter for progressive output format on scheduler Status objects
8
8
  class StatusProgressive < self
9
- FORMAT = '(%02d/%02d) %3d%% - killtime: %0.02fs runtime: %0.02fs overhead: %0.02fs'.freeze
9
+ FORMAT = '(%02d/%02d) %3d%% - killtime: %0.02fs runtime: %0.02fs overhead: %0.02fs'
10
10
 
11
11
  delegate(
12
12
  :coverage,
@@ -6,7 +6,7 @@ module Mutant
6
6
  class Printer
7
7
  # Reporter for subject progress
8
8
  class SubjectProgress < self
9
- FORMAT = '(%02d/%02d) %3d%% - killtime: %0.02fs runtime: %0.02fs overhead: %0.02fs'.freeze
9
+ FORMAT = '(%02d/%02d) %3d%% - killtime: %0.02fs runtime: %0.02fs overhead: %0.02fs'
10
10
 
11
11
  delegate(
12
12
  :tests,
@@ -9,9 +9,9 @@ module Mutant
9
9
 
10
10
  delegate :tests, :runtime
11
11
 
12
- STATUS_FORMAT = '- %d @ runtime: %s'.freeze
13
- OUTPUT_HEADER = 'Test Output:'.freeze
14
- TEST_FORMAT = ' - %s'.freeze
12
+ STATUS_FORMAT = '- %d @ runtime: %s'
13
+ OUTPUT_HEADER = 'Test Output:'
14
+ TEST_FORMAT = ' - %s'
15
15
 
16
16
  # Run test result reporter
17
17
  #
@@ -24,7 +24,7 @@ module Mutant
24
24
  class Diff
25
25
  include Adamantium, Anima.new(:config, :from, :to)
26
26
 
27
- HEAD = 'HEAD'.freeze
27
+ HEAD = 'HEAD'
28
28
 
29
29
  # Test if diff changes file at line range
30
30
  #
@@ -41,6 +41,7 @@ module Mutant
41
41
  command = %W[
42
42
  git log
43
43
  #{from}...#{to}
44
+ --ignore-all-space
44
45
  -L #{line_range.begin},#{line_range.end}:#{path}
45
46
  ]
46
47
 
@@ -7,7 +7,7 @@ module Mutant
7
7
  class Instance < self
8
8
 
9
9
  NAME_INDEX = 0
10
- SYMBOL = '#'.freeze
10
+ SYMBOL = '#'
11
11
 
12
12
  # Prepare subject for mutation insertion
13
13
  #
@@ -7,7 +7,7 @@ module Mutant
7
7
  class Singleton < self
8
8
 
9
9
  NAME_INDEX = 1
10
- SYMBOL = '.'.freeze
10
+ SYMBOL = '.'
11
11
 
12
12
  # Prepare subject for mutation insertion
13
13
  #
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Mutant
4
4
  # Current mutant version
5
- VERSION = '0.8.23'.freeze
5
+ VERSION = '0.8.24'
6
6
  end # Mutant
@@ -121,3 +121,84 @@ Mutant::Meta::Example.add :block do
121
121
  mutation 'foo { }'
122
122
  mutation 'foo'
123
123
  end
124
+
125
+ Mutant::Meta::Example.add :block do
126
+ source 'foo { next if true }'
127
+
128
+ singleton_mutations
129
+ mutation 'foo'
130
+ mutation 'foo { }'
131
+ mutation 'foo { self }'
132
+ mutation 'foo { nil }'
133
+ mutation 'foo { raise }'
134
+ mutation 'foo { self if true }'
135
+ mutation 'foo { nil if true }'
136
+ mutation 'foo { break if true }'
137
+ mutation 'foo { next if !true }'
138
+ mutation 'foo { next if false }'
139
+ mutation 'foo { next if nil }'
140
+ mutation 'foo { next }'
141
+ end
142
+
143
+ Mutant::Meta::Example.add :block do
144
+ source 'foo { next }'
145
+
146
+ singleton_mutations
147
+ mutation 'foo { nil }'
148
+ mutation 'foo { raise }'
149
+ mutation 'foo { self }'
150
+ mutation 'foo { break }'
151
+ mutation 'foo { }'
152
+ mutation 'foo'
153
+ end
154
+
155
+ Mutant::Meta::Example.add :block do
156
+ source 'foo { break if true }'
157
+
158
+ singleton_mutations
159
+ mutation 'foo'
160
+ mutation 'foo { }'
161
+ mutation 'foo { self }'
162
+ mutation 'foo { nil }'
163
+ mutation 'foo { raise }'
164
+ mutation 'foo { self if true }'
165
+ mutation 'foo { nil if true }'
166
+ mutation 'foo { break if !true }'
167
+ mutation 'foo { break if false }'
168
+ mutation 'foo { break if nil }'
169
+ mutation 'foo { break }'
170
+ end
171
+
172
+ Mutant::Meta::Example.add :block do
173
+ source 'foo { break }'
174
+
175
+ singleton_mutations
176
+ mutation 'foo { nil }'
177
+ mutation 'foo { raise }'
178
+ mutation 'foo { self }'
179
+ mutation 'foo { }'
180
+ mutation 'foo'
181
+ end
182
+
183
+ Mutant::Meta::Example.add :block do
184
+ source 'foo(&:bar).baz {}'
185
+
186
+ singleton_mutations
187
+
188
+ mutation 'foo(&:bar).baz { raise }'
189
+ mutation 'foo.baz { }'
190
+ mutation 'foo(&:bar).baz'
191
+ mutation 'self.baz {}'
192
+ end
193
+
194
+ Mutant::Meta::Example.add :block do
195
+ source 'foo(nil, &:bar).baz {}'
196
+
197
+ singleton_mutations
198
+ mutation 'foo(nil, &:bar).baz { raise }'
199
+ mutation 'foo(&:bar).baz { }'
200
+ mutation 'foo(nil).baz { }'
201
+ mutation 'foo.baz { }'
202
+ mutation 'self.baz { }'
203
+ mutation 'foo(nil, &:bar).baz'
204
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Mutant::Meta::Example.add do
4
+ source '__FILE__'
5
+ end
@@ -7,3 +7,17 @@ Mutant::Meta::Example.add :block, :lambda do
7
7
 
8
8
  mutation '->() { raise }'
9
9
  end
10
+
11
+ Mutant::Meta::Example.add :block, :lambda do
12
+ source '->() { foo.bar }'
13
+
14
+ singleton_mutations
15
+
16
+ mutation '->() { }'
17
+ mutation '->() { self }'
18
+ mutation '->() { nil }'
19
+ mutation '->() { raise }'
20
+ mutation '->() { self.bar }'
21
+ mutation '->() { foo }'
22
+ mutation 'foo.bar'
23
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Mutant::Meta::Example.add do
4
+ source '__LINE__'
5
+ end
@@ -8,3 +8,18 @@ Mutant::Meta::Example.add :lvasgn do
8
8
  mutation 'a = false'
9
9
  mutation 'a = nil'
10
10
  end
11
+
12
+ Mutant::Meta::Example.add :array, :lvasgn do
13
+ source 'a = *b'
14
+
15
+ singleton_mutations
16
+ mutation 'a__mutant__ = *b'
17
+ mutation 'a = nil'
18
+ mutation 'a = self'
19
+ mutation 'a = []'
20
+ mutation 'a = [nil]'
21
+ mutation 'a = [self]'
22
+ mutation 'a = [*self]'
23
+ mutation 'a = [*nil]'
24
+ mutation 'a = [b]'
25
+ end
@@ -76,6 +76,15 @@ Mutant::Meta::Example.add :regexp do
76
76
  mutation '/(?(1)(foo)(?:bar))/'
77
77
  end
78
78
 
79
+ # Case where MRI would accept an expression but regexp_parser not.
80
+ Mutant::Meta::Example.add :regexp do
81
+ source '/u{/'
82
+
83
+ singleton_mutations
84
+ mutation '//'
85
+ mutation '/nomatch\A/'
86
+ end
87
+
79
88
  Pathname
80
89
  .glob(Pathname.new(__dir__).join('regexp', '*.rb'))
81
90
  .sort
@@ -8,14 +8,9 @@
8
8
  mutation_coverage: false
9
9
  mutation_generation: true
10
10
  expected_errors:
11
- "#<Parser::SyntaxError: invalid multibyte escape: /\xAA/>":
12
- - language/regexp/escapes_spec.rb
13
- '#<RegexpError: invalid multibyte escape: /\xAA/>':
14
- - language/regexp/escapes_spec.rb
15
- "#<Regexp::Scanner::PrematureEndError: Premature end of pattern at #{str}>":
16
- - language/regexp/interpolation_spec.rb
17
- '#<Regexp::Scanner::PrematureEndError: Premature end of pattern at \xA>':
18
- - language/regexp/escapes_spec.rb
11
+ "#<ArgumentError: No valid target found for '?' >":
12
+ - core/matchdata/begin_spec.rb
13
+ - core/matchdata/end_spec.rb
19
14
  exclude:
20
15
  - core/string/casecmp_spec.rb
21
16
  - core/symbol/casecmp_spec.rb
@@ -14,7 +14,7 @@ module MutantSpec
14
14
  # rubocop:disable MethodLength
15
15
  module Corpus
16
16
  TMP = ROOT.join('tmp').freeze
17
- EXCLUDE_GLOB_FORMAT = '{%s}'.freeze
17
+ EXCLUDE_GLOB_FORMAT = '{%s}'
18
18
 
19
19
  # Not in the docs. Number from chatting with their support.
20
20
  # 2 processors allocated per container, 4 processes works well.
@@ -27,9 +27,9 @@ module MutantSpec
27
27
  class Project
28
28
  MUTEX = Mutex.new
29
29
 
30
- MUTATION_GENERATION_MESSAGE = 'Total Mutations/Time/Parse-Errors: %s/%0.2fs - %0.2f/s'.freeze
31
- START_MESSAGE = 'Starting - %s'.freeze
32
- FINISH_MESSAGE = 'Mutations - %4i - %s'.freeze
30
+ MUTATION_GENERATION_MESSAGE = 'Total Mutations/Time/Parse-Errors: %s/%0.2fs - %0.2f/s'
31
+ START_MESSAGE = 'Starting - %s'
32
+ FINISH_MESSAGE = 'Mutations - %4i - %s'
33
33
 
34
34
  DEFAULT_MUTATION_COUNT = 0
35
35
 
@@ -272,7 +272,7 @@ module MutantSpec
272
272
  # Mapping of files which we expect to cause errors during mutation generation
273
273
  class ErrorWhitelist
274
274
  class UnnecessaryExpectation < StandardError
275
- MESSAGE = 'Expected to encounter %s while mutating "%s"'.freeze
275
+ MESSAGE = 'Expected to encounter %s while mutating "%s"'
276
276
 
277
277
  def initialize(*error_info)
278
278
  super(MESSAGE % error_info)
@@ -14,7 +14,7 @@ module MutantSpec
14
14
  end
15
15
 
16
16
  class UnexpectedWarnings < StandardError
17
- MSG = 'Unexpected warnings: %s'.freeze
17
+ MSG = 'Unexpected warnings: %s'
18
18
 
19
19
  def initialize(warnings)
20
20
  super(MSG % warnings.join("\n"))
@@ -1,7 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RSpec.describe Mutant::AST::Regexp, '.parse' do
4
- it 'parses using minor ruby version' do
5
- expect(described_class.parse(/foo/).to_re).to eql(/foo/)
4
+ def apply(input)
5
+ described_class.parse(input)
6
+ end
7
+
8
+ context 'on regexp regexp_parser does accept' do
9
+ it 'parses using minor ruby version' do
10
+ expect(apply(/foo/).to_re).to eql(/foo/)
11
+ end
12
+ end
13
+
14
+ context 'on regexp regexp_parser does not accept' do
15
+ it 'returns nil' do
16
+ expect(apply(/u{/)).to be(nil)
17
+ end
6
18
  end
7
19
  end
@@ -29,7 +29,7 @@ RSpec.describe Mutant::Loader, '.call' do
29
29
  it 'performs expected kernel interaction' do
30
30
  expect(kernel).to receive(:eval)
31
31
  .with(
32
- source,
32
+ "# frozen_string_literal: true\n#[InstanceDouble(String) (anonymous)]",
33
33
  binding,
34
34
  path_str,
35
35
  line
@@ -3,7 +3,7 @@
3
3
  RSpec.describe Mutant::Mutation do
4
4
  let(:mutation_class) do
5
5
  Class.new(Mutant::Mutation) do
6
- const_set(:SYMBOL, 'test'.freeze)
6
+ const_set(:SYMBOL, 'test')
7
7
  const_set(:TEST_PASS_SUCCESS, true)
8
8
  end
9
9
  end
@@ -75,7 +75,7 @@ describe Mutant::Repository::Diff do
75
75
  end
76
76
 
77
77
  let(:expected_git_log_command) do
78
- %W[git log from_rev...to_rev -L 1,2:#{path}]
78
+ %W[git log from_rev...to_rev --ignore-all-space -L 1,2:#{path}]
79
79
  end
80
80
 
81
81
  context 'on failure of git log command' do
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.8.23
4
+ version: 0.8.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - Markus Schirp
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-12-23 00:00:00.000000000 Z
11
+ date: 2018-12-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: abstract_type
@@ -464,6 +464,7 @@ files:
464
464
  - meta/dsym.rb
465
465
  - meta/ensure.rb
466
466
  - meta/false.rb
467
+ - meta/file.rb
467
468
  - meta/float.rb
468
469
  - meta/gvar.rb
469
470
  - meta/gvasgn.rb
@@ -478,6 +479,7 @@ files:
478
479
  - meta/kwbegin.rb
479
480
  - meta/kwoptarg.rb
480
481
  - meta/lambda.rb
482
+ - meta/line.rb
481
483
  - meta/lvar.rb
482
484
  - meta/lvasgn.rb
483
485
  - meta/masgn.rb