mutant 0.10.25 → 0.10.26

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/lib/mutant.rb +10 -9
  3. data/lib/mutant/ast/find_metaclass_containing.rb +1 -1
  4. data/lib/mutant/ast/regexp.rb +17 -0
  5. data/lib/mutant/ast/regexp/transformer.rb +2 -2
  6. data/lib/mutant/ast/regexp/transformer/quantifier.rb +4 -2
  7. data/lib/mutant/bootstrap.rb +1 -1
  8. data/lib/mutant/cli/command.rb +4 -0
  9. data/lib/mutant/cli/command/environment/subject.rb +0 -4
  10. data/lib/mutant/cli/command/environment/test.rb +36 -0
  11. data/lib/mutant/cli/command/root.rb +1 -1
  12. data/lib/mutant/config.rb +1 -1
  13. data/lib/mutant/context.rb +1 -1
  14. data/lib/mutant/env.rb +2 -2
  15. data/lib/mutant/expression/method.rb +4 -4
  16. data/lib/mutant/expression/methods.rb +5 -4
  17. data/lib/mutant/integration.rb +8 -2
  18. data/lib/mutant/isolation/fork.rb +4 -11
  19. data/lib/mutant/matcher.rb +1 -1
  20. data/lib/mutant/matcher/config.rb +3 -2
  21. data/lib/mutant/matcher/method.rb +8 -6
  22. data/lib/mutant/matcher/method/instance.rb +6 -2
  23. data/lib/mutant/matcher/methods.rb +2 -4
  24. data/lib/mutant/meta/example.rb +1 -1
  25. data/lib/mutant/meta/example/verification.rb +1 -1
  26. data/lib/mutant/mutation.rb +1 -1
  27. data/lib/mutant/mutator.rb +8 -1
  28. data/lib/mutant/mutator/node/argument.rb +2 -2
  29. data/lib/mutant/mutator/node/literal/regex.rb +3 -17
  30. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +3 -3
  31. data/lib/mutant/mutator/node/regexp/character_type.rb +1 -1
  32. data/lib/mutant/mutator/node/regexp/named_group.rb +39 -0
  33. data/lib/mutant/mutator/node/regexp/zero_or_more.rb +2 -2
  34. data/lib/mutant/mutator/node/regopt.rb +1 -1
  35. data/lib/mutant/mutator/node/send.rb +40 -6
  36. data/lib/mutant/parallel.rb +2 -2
  37. data/lib/mutant/parallel/driver.rb +1 -1
  38. data/lib/mutant/parallel/worker.rb +1 -1
  39. data/lib/mutant/parser.rb +1 -1
  40. data/lib/mutant/pipe.rb +1 -1
  41. data/lib/mutant/procto.rb +23 -0
  42. data/lib/mutant/reporter/cli/printer.rb +10 -4
  43. data/lib/mutant/reporter/cli/printer/env.rb +2 -2
  44. data/lib/mutant/reporter/cli/printer/env_progress.rb +2 -2
  45. data/lib/mutant/selector.rb +1 -1
  46. data/lib/mutant/subject.rb +1 -1
  47. data/lib/mutant/subject/method/instance.rb +5 -42
  48. data/lib/mutant/variable.rb +322 -0
  49. data/lib/mutant/version.rb +1 -1
  50. data/lib/mutant/world.rb +1 -1
  51. metadata +8 -158
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f02dec057593b8e61c2b207918ed6f1b9c4aa1d41bc0b2f2390c2a1da9172b5d
4
- data.tar.gz: 99a318ed1f02d9976037694b2dbc1de463863f747cc4b8b5e95809b307680f03
3
+ metadata.gz: 05b03b60df6a63d9fa7a8b64affcaae0a804a753764b636fee625b1edc597e25
4
+ data.tar.gz: b61c7b228cdffed5d67cd38ffb3cc55b00d2bace308e56f8f48e828987fa76ba
5
5
  SHA512:
6
- metadata.gz: 5312bd87fc41b3442b886b40b200357c254c2d7d65081cdfaca2e2e213311539eb23b00eb5b792c9f7978c8eeed5a6cbd72b3a513598b9abc9f72a3beaa9989d
7
- data.tar.gz: 922e0ea1878e05f014a3c832d7bfa23e0d8410cc8c8d5b9bd1bc8b81124a18ecd11403be643cd144772863950076b17ab06ce3cd809cda01a9563ec815c95e79
6
+ metadata.gz: 50fbc446b2f03edb5015136920a95e3fa9dbbc55b10a530dcea755ed9bf0bb0fea2e99bc3bdf6503cc564fc4d71ea8b1fbcabe97f6959df9ec800e68c81220cb
7
+ data.tar.gz: fa841fc57b3422780840b66368bbac984b42bd2cef27aee58d07b783b952cbece2fddb0d821ff489279c808db64802c254df84774fcc4a50b887ec068c455b00
@@ -1,16 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'abstract_type'
4
- require 'adamantium'
5
- require 'anima'
6
- require 'concord'
7
3
  require 'diff/lcs'
8
4
  require 'diff/lcs/hunk'
9
5
  require 'digest/sha1'
10
- require 'equalizer'
11
6
  require 'etc'
12
- require 'ice_nine'
13
- require 'mprelude'
14
7
  require 'json'
15
8
  require 'open3'
16
9
  require 'optparse'
@@ -22,7 +15,6 @@ require 'set'
22
15
  require 'singleton'
23
16
  require 'stringio'
24
17
  require 'unparser'
25
- require 'variable'
26
18
  require 'yaml'
27
19
 
28
20
  # This setting is done to make errors within the parallel
@@ -33,7 +25,12 @@ Thread.abort_on_exception = true
33
25
  #
34
26
  # @api private
35
27
  module Mutant
36
- Either = MPrelude::Either
28
+ AbstractType = Unparser::AbstractType
29
+ Adamantium = Unparser::Adamantium
30
+ Anima = Unparser::Anima
31
+ Concord = Unparser::Concord
32
+ Either = Unparser::Either
33
+ Equalizer = Unparser::Equalizer
37
34
 
38
35
  EMPTY_STRING = ''
39
36
  EMPTY_ARRAY = [].freeze
@@ -41,7 +38,9 @@ module Mutant
41
38
  SCOPE_OPERATOR = '::'
42
39
  end # Mutant
43
40
 
41
+ require 'mutant/procto'
44
42
  require 'mutant/transform'
43
+ require 'mutant/variable'
45
44
  require 'mutant/bootstrap'
46
45
  require 'mutant/version'
47
46
  require 'mutant/env'
@@ -90,6 +89,7 @@ require 'mutant/mutator/node/regexp'
90
89
  require 'mutant/mutator/node/regexp/alternation_meta'
91
90
  require 'mutant/mutator/node/regexp/beginning_of_line_anchor'
92
91
  require 'mutant/mutator/node/regexp/capture_group'
92
+ require 'mutant/mutator/node/regexp/named_group'
93
93
  require 'mutant/mutator/node/regexp/character_type'
94
94
  require 'mutant/mutator/node/regexp/end_of_line_anchor'
95
95
  require 'mutant/mutator/node/regexp/end_of_string_or_before_end_of_line_anchor'
@@ -193,6 +193,7 @@ require 'mutant/cli/command/environment'
193
193
  require 'mutant/cli/command/environment/run'
194
194
  require 'mutant/cli/command/environment/show'
195
195
  require 'mutant/cli/command/environment/subject'
196
+ require 'mutant/cli/command/environment/test'
196
197
  require 'mutant/cli/command/root'
197
198
  require 'mutant/runner'
198
199
  require 'mutant/runner/sink'
@@ -10,7 +10,7 @@ module Mutant
10
10
  # Descending into 'begin' nodes is supported because these are generated for
11
11
  # the one-line syntax class << self; def foo; end
12
12
  class FindMetaclassContaining
13
- include NodePredicates, Concord.new(:root, :target), Procto.call
13
+ include NodePredicates, Concord.new(:root, :target), Procto
14
14
 
15
15
  SCLASS_BODY_INDEX = 1
16
16
 
@@ -32,6 +32,23 @@ module Mutant
32
32
  def self.to_expression(node)
33
33
  Transformer.lookup(node.type).to_expression(node)
34
34
  end
35
+
36
+ # Convert's a `parser` `regexp` node into more fine-grained AST nodes.
37
+ #
38
+ # @param node [Parser::AST::Node]
39
+ #
40
+ # @return [Parser::AST::Node]
41
+ def self.expand_regexp_ast(node)
42
+ *body, _opts = node.children
43
+
44
+ # NOTE: We only mutate parts of regexp body if the body is composed of
45
+ # only strings. Regular expressions with interpolation are skipped
46
+ return unless body.all? { |child| child.type.equal?(:str) }
47
+
48
+ body_expression = parse(body.map(&:children).join)
49
+
50
+ to_ast(body_expression)
51
+ end
35
52
  end # Regexp
36
53
  end # AST
37
54
  end # Mutant
@@ -49,7 +49,7 @@ module Mutant
49
49
  class ExpressionToAST
50
50
  PREFIX = :regexp
51
51
 
52
- include Concord.new(:expression), Procto.call, AST::Sexp, AbstractType, Adamantium
52
+ include Concord.new(:expression), Procto, AST::Sexp, AbstractType, Adamantium
53
53
 
54
54
  private
55
55
 
@@ -74,7 +74,7 @@ module Mutant
74
74
 
75
75
  # Abstract node transformer
76
76
  class ASTToExpression
77
- include Concord.new(:node), Procto.call, AbstractType, Adamantium
77
+ include Concord.new(:node), Procto, AbstractType, Adamantium
78
78
 
79
79
  # Call generic transform method and freeze result
80
80
  #
@@ -30,7 +30,7 @@ module Mutant
30
30
 
31
31
  Quantifier = Class.new.include(Concord::Public.new(:type, :suffix, :mode))
32
32
 
33
- QUANTIFIER_MAP = IceNine.deep_freeze({
33
+ QUANTIFIER_MAP = {
34
34
  regexp_greedy_zero_or_more: [:zero_or_more, '*', :greedy],
35
35
  regexp_greedy_one_or_more: [:one_or_more, '+', :greedy],
36
36
  regexp_greedy_zero_or_one: [:zero_or_one, '?', :greedy],
@@ -42,7 +42,9 @@ module Mutant
42
42
  regexp_greedy_interval: [:interval, '', :greedy],
43
43
  regexp_reluctant_interval: [:interval, '?', :reluctant],
44
44
  regexp_possessive_interval: [:interval, '+', :possessive]
45
- }.transform_values { |arguments| Quantifier.new(*arguments) }.to_h)
45
+ }.transform_values { |arguments| Quantifier.new(*arguments) }
46
+ .to_h
47
+ .freeze
46
48
 
47
49
  private
48
50
 
@@ -8,7 +8,7 @@ module Mutant
8
8
  #
9
9
  # env = config interpreted against the world
10
10
  module Bootstrap
11
- include Adamantium::Flat, Anima.new(:config, :parser, :world)
11
+ include Adamantium, Anima.new(:config, :parser, :world)
12
12
 
13
13
  SEMANTICS_MESSAGE_FORMAT =
14
14
  "%<message>s. Fix your lib to follow normal ruby semantics!\n" \
@@ -204,6 +204,10 @@ module Mutant
204
204
  def with_help(message)
205
205
  "#{full_name}: #{message}\n\n#{parser}"
206
206
  end
207
+
208
+ def print(message)
209
+ world.stdout.puts(message)
210
+ end
207
211
  end # Command
208
212
  # rubocop:enable Metrics/ClassLength
209
213
  end # CLI
@@ -25,10 +25,6 @@ module Mutant
25
25
  print(subject.expression.syntax)
26
26
  end
27
27
  end
28
-
29
- def print(message)
30
- world.stdout.puts(message)
31
- end
32
28
  end
33
29
 
34
30
  SUBCOMMANDS = [List].freeze
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ module CLI
5
+ class Command
6
+ class Environment
7
+ class Test < self
8
+ NAME = 'test'
9
+ SHORT_DESCRIPTION = 'test subcommands'
10
+
11
+ class List < self
12
+ NAME = 'list'
13
+ SHORT_DESCRIPTION = 'List tests detected in the environment'
14
+ SUBCOMMANDS = EMPTY_ARRAY
15
+
16
+ private
17
+
18
+ def action
19
+ bootstrap.fmap(&method(:list_tests))
20
+ end
21
+
22
+ def list_tests(env)
23
+ tests = env.integration.all_tests
24
+ print('Tests in environment: %d' % tests.length)
25
+ tests.each do |test|
26
+ print(test.identification)
27
+ end
28
+ end
29
+ end
30
+
31
+ SUBCOMMANDS = [List].freeze
32
+ end # Test
33
+ end # Environment
34
+ end # Command
35
+ end # CLI
36
+ end # Mutant
@@ -4,7 +4,7 @@ module Mutant
4
4
  module CLI
5
5
  class Command
6
6
  class Environment < self
7
- SUBCOMMANDS = [Environment::Subject, Environment::Show].freeze
7
+ SUBCOMMANDS = [Environment::Subject, Environment::Show, Environment::Test].freeze
8
8
  end # Environment
9
9
 
10
10
  class Root < self
@@ -6,7 +6,7 @@ module Mutant
6
6
  # Does not reference any "external" volatile state. The configuration applied
7
7
  # to current environment is being represented by the Mutant::Env object.
8
8
  class Config
9
- include Adamantium::Flat, Anima.new(
9
+ include Adamantium, Anima.new(
10
10
  :coverage_criteria,
11
11
  :expression_parser,
12
12
  :fail_fast,
@@ -3,7 +3,7 @@
3
3
  module Mutant
4
4
  # An abstract context where mutations can be applied to.
5
5
  class Context
6
- include Adamantium::Flat, Concord::Public.new(:scope, :source_path)
6
+ include Adamantium, Concord::Public.new(:scope, :source_path)
7
7
  extend AST::Sexp
8
8
 
9
9
  NAMESPACE_DELIMITER = '::'
@@ -3,7 +3,7 @@
3
3
  module Mutant
4
4
  # Abstract base class for mutant environments
5
5
  class Env
6
- include Adamantium::Flat, Anima.new(
6
+ include Adamantium, Anima.new(
7
7
  :config,
8
8
  :integration,
9
9
  :matchable_scopes,
@@ -31,7 +31,7 @@ module Mutant
31
31
  config: config,
32
32
  integration: Integration::Null.new(
33
33
  expression_parser: config.expression_parser,
34
- timer: world.timer
34
+ world: world
35
35
  ),
36
36
  matchable_scopes: EMPTY_ARRAY,
37
37
  mutations: EMPTY_ARRAY,
@@ -15,10 +15,10 @@ module Mutant
15
15
 
16
16
  private(*anima.attribute_names)
17
17
 
18
- MATCHERS = IceNine.deep_freeze(
19
- '.' => [Matcher::Methods::Singleton, Matcher::Methods::Metaclass],
20
- '#' => [Matcher::Methods::Instance]
21
- )
18
+ MATCHERS = {
19
+ '.' => [Matcher::Methods::Singleton, Matcher::Methods::Metaclass].freeze,
20
+ '#' => [Matcher::Methods::Instance].freeze
21
+ }.freeze
22
22
 
23
23
  METHOD_NAME_PATTERN = /(?<method_name>.+)/.freeze
24
24
 
@@ -12,10 +12,11 @@ module Mutant
12
12
 
13
13
  private(*anima.attribute_names)
14
14
 
15
- MATCHERS = IceNine.deep_freeze(
16
- '.' => [Matcher::Methods::Singleton, Matcher::Methods::Metaclass],
17
- '#' => [Matcher::Methods::Instance]
18
- )
15
+ MATCHERS = {
16
+ '.' => [Matcher::Methods::Singleton, Matcher::Methods::Metaclass].freeze,
17
+ '#' => [Matcher::Methods::Instance].freeze
18
+ }.freeze
19
+
19
20
  private_constant(*constants(false))
20
21
 
21
22
  REGEXP = /\A#{SCOPE_NAME_PATTERN}#{SCOPE_SYMBOL_PATTERN}\z/.freeze
@@ -4,7 +4,7 @@ module Mutant
4
4
 
5
5
  # Abstract base class mutant test framework integrations
6
6
  class Integration
7
- include AbstractType, Adamantium::Flat, Anima.new(:expression_parser, :timer)
7
+ include AbstractType, Adamantium, Anima.new(:expression_parser, :world)
8
8
 
9
9
  LOAD_MESSAGE = <<~'MESSAGE'
10
10
  Unable to load integration mutant-%<integration_name>s:
@@ -39,7 +39,7 @@ module Mutant
39
39
  attempt_require(env).bind { attempt_const_get(env) }.fmap do |klass|
40
40
  klass.new(
41
41
  expression_parser: env.config.expression_parser,
42
- timer: env.world.timer
42
+ world: env.world
43
43
  ).setup
44
44
  end
45
45
  end
@@ -92,5 +92,11 @@ module Mutant
92
92
  #
93
93
  # @return [Enumerable<Test>]
94
94
  abstract_method :all_tests
95
+
96
+ private
97
+
98
+ def timer
99
+ world.timer
100
+ end
95
101
  end # Integration
96
102
  end # Mutant
@@ -28,7 +28,7 @@ module Mutant
28
28
  # * Child process freezing after closing the pipes needs to be
29
29
  # detected by parent process.
30
30
  class Fork < self
31
- include(Adamantium::Flat, Concord.new(:world))
31
+ include(Adamantium, Concord.new(:world))
32
32
 
33
33
  READ_SIZE = 4096
34
34
 
@@ -36,7 +36,7 @@ module Mutant
36
36
 
37
37
  # Pipe abstraction
38
38
  class Pipe
39
- include Adamantium::Flat, Anima.new(:reader, :writer)
39
+ include Adamantium, Anima.new(:reader, :writer)
40
40
 
41
41
  # Run block with pipe in binmode
42
42
  #
@@ -66,10 +66,7 @@ module Mutant
66
66
 
67
67
  # rubocop:disable Metrics/ClassLength
68
68
  class Parent
69
- include(
70
- Anima.new(*ATTRIBUTES),
71
- Procto.call
72
- )
69
+ include(Anima.new(*ATTRIBUTES), Procto)
73
70
 
74
71
  # Prevent mutation from `process.fork` to `fork` to call Kernel#fork
75
72
  undef_method :fork
@@ -212,11 +209,7 @@ module Mutant
212
209
  # rubocop:enable Metrics/ClassLength
213
210
 
214
211
  class Child
215
- include(
216
- Adamantium::Flat,
217
- Anima.new(*ATTRIBUTES),
218
- Procto.call
219
- )
212
+ include(Adamantium, Anima.new(*ATTRIBUTES), Procto)
220
213
 
221
214
  # Handle child process
222
215
  #
@@ -3,7 +3,7 @@
3
3
  module Mutant
4
4
  # Abstract matcher to find subjects to mutate
5
5
  class Matcher
6
- include Adamantium::Flat, AbstractType
6
+ include Adamantium, AbstractType
7
7
 
8
8
  # Call matcher
9
9
  #
@@ -16,12 +16,13 @@ module Mutant
16
16
  ATTRIBUTE_FORMAT = '%s: [%s]'
17
17
  ENUM_DELIMITER = ','
18
18
  EMPTY_ATTRIBUTES = 'empty'
19
- PRESENTATIONS = IceNine.deep_freeze(
19
+ PRESENTATIONS = {
20
20
  ignore: :syntax,
21
21
  start_expressions: :syntax,
22
22
  subject_filters: :inspect,
23
23
  subjects: :syntax
24
- )
24
+ }.freeze
25
+
25
26
  private_constant(*constants(false))
26
27
 
27
28
  DEFAULT = new(Hash[anima.attribute_names.map { |name| [name, []] }])
@@ -5,7 +5,7 @@ module Mutant
5
5
  # Abstract base class for method matchers
6
6
  class Method < self
7
7
  include AbstractType,
8
- Adamantium::Flat,
8
+ Adamantium,
9
9
  Concord::Public.new(:scope, :target_method, :evaluator)
10
10
 
11
11
  # Source locations we cannot acces
@@ -32,11 +32,13 @@ module Mutant
32
32
  # logic would be implemented directly on the Matcher::Method
33
33
  # instance
34
34
  class Evaluator
35
- include AbstractType,
36
- Adamantium,
37
- Concord.new(:scope, :target_method, :env),
38
- Procto.call,
39
- AST::NodePredicates
35
+ include(
36
+ AbstractType,
37
+ Adamantium,
38
+ Concord.new(:scope, :target_method, :env),
39
+ Procto,
40
+ AST::NodePredicates
41
+ )
40
42
 
41
43
  # Matched subjects
42
44
  #
@@ -13,9 +13,8 @@ module Mutant
13
13
  #
14
14
  # @return [Matcher::Method::Instance]
15
15
  def self.new(scope, target_method)
16
- name = target_method.name
17
16
  evaluator =
18
- if scope.respond_to?(:memoized?) && scope.memoized?(name)
17
+ if memoized_method?(scope, target_method.name)
19
18
  Evaluator::Memoized
20
19
  else
21
20
  Evaluator
@@ -24,6 +23,11 @@ module Mutant
24
23
  super(scope, target_method, evaluator)
25
24
  end
26
25
 
26
+ def self.memoized_method?(scope, method_name)
27
+ scope < Adamantium && scope.memoized?(method_name)
28
+ end
29
+ private_class_method :memoized_method?
30
+
27
31
  # Instance method specific evaluator
28
32
  class Evaluator < Evaluator
29
33
  SUBJECT_CLASS = Subject::Method::Instance