mutant 0.8.23 → 0.8.24

Sign up to get free protection for your applications and to get access to all the features.
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