mutant 0.10.24 → 0.10.29

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mutant.rb +15 -11
  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/exception.rb +22 -0
  19. data/lib/mutant/isolation/fork.rb +9 -12
  20. data/lib/mutant/loader.rb +1 -1
  21. data/lib/mutant/matcher.rb +1 -1
  22. data/lib/mutant/matcher/config.rb +6 -3
  23. data/lib/mutant/matcher/method.rb +12 -10
  24. data/lib/mutant/matcher/method/instance.rb +6 -2
  25. data/lib/mutant/matcher/method/metaclass.rb +1 -1
  26. data/lib/mutant/matcher/methods.rb +2 -4
  27. data/lib/mutant/meta/example.rb +1 -1
  28. data/lib/mutant/meta/example/dsl.rb +0 -1
  29. data/lib/mutant/meta/example/verification.rb +1 -1
  30. data/lib/mutant/mutation.rb +1 -1
  31. data/lib/mutant/mutator.rb +8 -1
  32. data/lib/mutant/mutator/node.rb +0 -5
  33. data/lib/mutant/mutator/node/argument.rb +2 -2
  34. data/lib/mutant/mutator/node/dynamic_literal.rb +1 -1
  35. data/lib/mutant/mutator/node/index.rb +1 -0
  36. data/lib/mutant/mutator/node/literal/float.rb +1 -3
  37. data/lib/mutant/mutator/node/literal/integer.rb +3 -6
  38. data/lib/mutant/mutator/node/literal/range.rb +1 -1
  39. data/lib/mutant/mutator/node/literal/regex.rb +3 -17
  40. data/lib/mutant/mutator/node/module.rb +19 -0
  41. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +3 -3
  42. data/lib/mutant/mutator/node/regexp.rb +0 -11
  43. data/lib/mutant/mutator/node/regexp/beginning_of_line_anchor.rb +20 -0
  44. data/lib/mutant/mutator/node/regexp/character_type.rb +1 -1
  45. data/lib/mutant/mutator/node/regexp/named_group.rb +39 -0
  46. data/lib/mutant/mutator/node/regexp/zero_or_more.rb +34 -0
  47. data/lib/mutant/mutator/node/regopt.rb +1 -1
  48. data/lib/mutant/mutator/node/sclass.rb +1 -1
  49. data/lib/mutant/mutator/node/send.rb +73 -6
  50. data/lib/mutant/parallel.rb +2 -2
  51. data/lib/mutant/parallel/driver.rb +1 -1
  52. data/lib/mutant/parallel/worker.rb +1 -1
  53. data/lib/mutant/parser.rb +1 -1
  54. data/lib/mutant/pipe.rb +1 -1
  55. data/lib/mutant/procto.rb +23 -0
  56. data/lib/mutant/reporter/cli/printer.rb +10 -4
  57. data/lib/mutant/reporter/cli/printer/env.rb +3 -3
  58. data/lib/mutant/reporter/cli/printer/env_progress.rb +2 -2
  59. data/lib/mutant/reporter/cli/printer/isolation_result.rb +3 -1
  60. data/lib/mutant/selector.rb +1 -1
  61. data/lib/mutant/subject.rb +1 -1
  62. data/lib/mutant/subject/method/instance.rb +5 -42
  63. data/lib/mutant/transform.rb +25 -0
  64. data/lib/mutant/variable.rb +322 -0
  65. data/lib/mutant/version.rb +1 -1
  66. data/lib/mutant/world.rb +1 -1
  67. metadata +13 -160
  68. data/lib/mutant/mutator/node/regexp/greedy_zero_or_more.rb +0 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34fe96de54f1f220934b79bdf26e6103693d1a8cd4986bc05259d90117016801
4
- data.tar.gz: 1a3acf6dcc73c191f6264a5827910b76d2da54aaff79a75a903f45595bd653be
3
+ metadata.gz: 287c627187afa2b42509eccfbb1f6c9d6a0122b6fc27bc518746ecc4db406da5
4
+ data.tar.gz: a7310138beb4d7b415f627d82cfcb7cd6149f9d9f06272c1b31e15bfa0ec7375
5
5
  SHA512:
6
- metadata.gz: fc6d72fcd52b9167dd3d0cd030c5f29076f61b8c296d9019802106200567e60c28e011fd68b6f1e928cca1bd5c9787291b1b35e7b77a86dc8a74b3895c323dd2
7
- data.tar.gz: 53eb52604ab5b02cb412f990efa2770495df25c56cab860447ac210f4179a17cb6cf76f0641f4e362882188ee403e6402908d09d7c3673500aa00b2722084ae2
6
+ metadata.gz: 607876e535726ad8c39b1ba7d2b9cd18ca93e6579ee62142a38b31c9f4cd856d49f828398b7bee7e963ae8029b6b3f41772d2e3df6e527dd71bf303009f80f15
7
+ data.tar.gz: 7957de07e7554b3921be9311d4ea2fa67a4068c62794466853972157beeb504a20de97856001cf17decc9730b99df509c45a539db81769c42c0ecb31abd9d610
data/lib/mutant.rb CHANGED
@@ -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'
@@ -72,8 +71,9 @@ require 'mutant/ast/meta/optarg'
72
71
  require 'mutant/ast/meta/resbody'
73
72
  require 'mutant/parser'
74
73
  require 'mutant/isolation'
75
- require 'mutant/isolation/none'
74
+ require 'mutant/isolation/exception'
76
75
  require 'mutant/isolation/fork'
76
+ require 'mutant/isolation/none'
77
77
  require 'mutant/parallel'
78
78
  require 'mutant/parallel/driver'
79
79
  require 'mutant/parallel/source'
@@ -88,11 +88,13 @@ require 'mutant/mutator/node'
88
88
  require 'mutant/mutator/node/generic'
89
89
  require 'mutant/mutator/node/regexp'
90
90
  require 'mutant/mutator/node/regexp/alternation_meta'
91
+ require 'mutant/mutator/node/regexp/beginning_of_line_anchor'
91
92
  require 'mutant/mutator/node/regexp/capture_group'
93
+ require 'mutant/mutator/node/regexp/named_group'
92
94
  require 'mutant/mutator/node/regexp/character_type'
93
95
  require 'mutant/mutator/node/regexp/end_of_line_anchor'
94
96
  require 'mutant/mutator/node/regexp/end_of_string_or_before_end_of_line_anchor'
95
- require 'mutant/mutator/node/regexp/greedy_zero_or_more'
97
+ require 'mutant/mutator/node/regexp/zero_or_more'
96
98
  require 'mutant/mutator/node/literal'
97
99
  require 'mutant/mutator/node/literal/boolean'
98
100
  require 'mutant/mutator/node/literal/range'
@@ -136,6 +138,7 @@ require 'mutant/mutator/node/define'
136
138
  require 'mutant/mutator/node/mlhs'
137
139
  require 'mutant/mutator/node/nthref'
138
140
  require 'mutant/mutator/node/masgn'
141
+ require 'mutant/mutator/node/module'
139
142
  require 'mutant/mutator/node/return'
140
143
  require 'mutant/mutator/node/block'
141
144
  require 'mutant/mutator/node/block_pass'
@@ -192,6 +195,7 @@ require 'mutant/cli/command/environment'
192
195
  require 'mutant/cli/command/environment/run'
193
196
  require 'mutant/cli/command/environment/show'
194
197
  require 'mutant/cli/command/environment/subject'
198
+ require 'mutant/cli/command/environment/test'
195
199
  require 'mutant/cli/command/root'
196
200
  require 'mutant/runner'
197
201
  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
data/lib/mutant/config.rb CHANGED
@@ -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 = '::'
data/lib/mutant/env.rb CHANGED
@@ -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
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ # Module providing isolation
5
+ class Isolation
6
+ # Generic serializable exception data.
7
+ #
8
+ # This is required as our honored guests the Rails* ecosystem
9
+ # makes Marshal.dump on exceptions impossible.
10
+ #
11
+ # @see https://twitter.com/_m_b_j_/status/1356433184850907137
12
+ #
13
+ # for the full story and eventual reactions.
14
+ class Exception
15
+ include Anima.new(
16
+ :backtrace,
17
+ :message,
18
+ :original_class
19
+ )
20
+ end # Exception
21
+ end # Isolation
22
+ 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
@@ -136,7 +133,11 @@ module Mutant
136
133
  def load_result(result_fragments)
137
134
  @value = world.marshal.load(result_fragments.join)
138
135
  rescue ArgumentError => exception
139
- @exception = exception
136
+ @exception = Exception.new(
137
+ backtrace: exception.backtrace,
138
+ message: exception.message,
139
+ original_class: exception.class
140
+ )
140
141
  end
141
142
 
142
143
  # rubocop:disable Metrics/MethodLength
@@ -212,11 +213,7 @@ module Mutant
212
213
  # rubocop:enable Metrics/ClassLength
213
214
 
214
215
  class Child
215
- include(
216
- Adamantium::Flat,
217
- Anima.new(*ATTRIBUTES),
218
- Procto.call
219
- )
216
+ include(Adamantium, Anima.new(*ATTRIBUTES), Procto)
220
217
 
221
218
  # Handle child process
222
219
  #
data/lib/mutant/loader.rb CHANGED
@@ -18,7 +18,7 @@ module Mutant
18
18
 
19
19
  # Vale returned on MRI detecting void value expressions
20
20
  class VoidValue < self
21
- end # voidValue
21
+ end # VoidValue
22
22
  end # Result
23
23
 
24
24
  # Call loader