mutant 0.13.5 → 0.14.1

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: 538aeb3b4a0e294ad4e9267116dc4cfc68f42fd195da8c6dcc7ba3eeaac9378f
4
- data.tar.gz: a09a2c06315294c7c88f42e4002afc02f73bba76f6d7c04e04bc436e9f70b421
3
+ metadata.gz: 8d3a480fb26d943cb4a6b83edab4a59c8f82e887af44f2915d713187f3a1de52
4
+ data.tar.gz: fa67cd60285d710dea30b5de41d241f565b4fa1e199d957c05b2e250f3b4a71d
5
5
  SHA512:
6
- metadata.gz: 818bda670d95c84e55413051f04e0afbd47973b1edf1840126f2109df8979d7bb5be3a5d2d560dbfc50b875137c1d45826b6ba9228b939b51b5e56b7d5c5d6d2
7
- data.tar.gz: 9ad79412af1baa590c8b88487b227d6235f15b399b8ea602ad75fc25abcdce252ea3a69766989937fa9915292396f252557a807cc7b23245ded8818c5b38007a
6
+ metadata.gz: 73587288bb7e4d7c940e5fb2ed4d06c3d18c2c0e13a29e453014d2034bb84018c17b43bb6419faa51aed03363458343a83f73a016a13da21be5faea77b168b9b
7
+ data.tar.gz: fb6bd3cc6bd882b2fba0bf576de3076371d9f83711c13ea8bc88d520e36ebae516f3d618316c89092c78e4bb857018404512cf1c88d0cd52631145ca8a754c60
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.14.1
data/bin/mutant CHANGED
@@ -1,64 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- module Mutant
5
- # Record executable timestamp
6
- @executable_timestamp = Process.clock_gettime(Process::CLOCK_MONOTONIC)
4
+ # Dispatcher that selects between mutant-ruby and mutant-rust
5
+ # based on MUTANT_RUST environment variable.
6
+ #
7
+ # See RUST.md for documentation.
7
8
 
8
- trap('INT') do |status|
9
- effective_status = status ? status + 128 : 128
10
- exit! effective_status
11
- end
9
+ fail 'MUTANT_RUST=1 is not yet supported via gem installation' if ENV['MUTANT_RUST']
12
10
 
13
- require 'mutant'
14
-
15
- WORLD.record(:cli_parse) do
16
- CLI.parse(
17
- arguments: ARGV,
18
- world: Mutant::WORLD
19
- )
20
- end.either(
21
- ->(message) { Mutant::WORLD.stderr.puts(message); Kernel.exit(false) },
22
- # rubocop:disable Metrics/BlockLength
23
- lambda do |command|
24
- if command.zombie?
25
- command = WORLD.record(:zombify) do
26
- $stderr.puts('Running mutant zombified!')
27
- Zombifier.call(
28
- namespace: :Zombie,
29
- load_path: $LOAD_PATH,
30
- kernel: Kernel,
31
- pathname: Pathname,
32
- require_highjack: RequireHighjack
33
- .public_method(:call)
34
- .to_proc
35
- .curry
36
- .call(Kernel),
37
- root_require: 'mutant',
38
- includes: %w[
39
- adamantium
40
- anima
41
- concord
42
- equalizer
43
- mprelude
44
- mutant
45
- unparser
46
- variable
47
- ]
48
- )
49
-
50
- Zombie::Mutant::CLI.parse(
51
- arguments: ARGV,
52
- world: Mutant::WORLD
53
- ).from_right
54
- end
55
- end
56
-
57
- WORLD.record(:execute) { command.call }.tap do |status|
58
- WORLD.recorder.print_profile(WORLD.stderr) if command.print_profile?
59
- WORLD.kernel.exit(status)
60
- end
61
- end
62
- # rubocop:enable Metrics/BlockLength
63
- )
64
- end
11
+ exec('mutant-ruby', *ARGV)
data/bin/mutant-ruby ADDED
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ module Mutant
5
+ # Record executable timestamp
6
+ @executable_timestamp = Process.clock_gettime(Process::CLOCK_MONOTONIC)
7
+
8
+ trap('INT') do |status|
9
+ effective_status = status ? status + 128 : 128
10
+ exit! effective_status
11
+ end
12
+
13
+ require 'mutant'
14
+
15
+ WORLD.record(:cli_parse) do
16
+ CLI.parse(
17
+ arguments: ARGV,
18
+ world: Mutant::WORLD
19
+ )
20
+ end.either(
21
+ ->(message) { Mutant::WORLD.stderr.puts(message); Kernel.exit(false) },
22
+ # rubocop:disable Metrics/BlockLength
23
+ lambda do |command|
24
+ if command.zombie?
25
+ command = WORLD.record(:zombify) do
26
+ $stderr.puts('Running mutant zombified!')
27
+ Zombifier.call(
28
+ namespace: :Zombie,
29
+ load_path: $LOAD_PATH,
30
+ kernel: Kernel,
31
+ pathname: Pathname,
32
+ require_highjack: RequireHighjack
33
+ .public_method(:call)
34
+ .to_proc
35
+ .curry
36
+ .call(Kernel),
37
+ root_require: 'mutant',
38
+ includes: %w[
39
+ adamantium
40
+ anima
41
+ concord
42
+ equalizer
43
+ mprelude
44
+ mutant
45
+ unparser
46
+ variable
47
+ ]
48
+ )
49
+
50
+ Zombie::Mutant::CLI.parse(
51
+ arguments: ARGV,
52
+ world: Mutant::WORLD
53
+ ).from_right
54
+ end
55
+ end
56
+
57
+ WORLD.record(:execute) { command.call }.tap do |status|
58
+ WORLD.recorder.print_profile(WORLD.stderr) if command.print_profile?
59
+ WORLD.kernel.exit(status)
60
+ end
61
+ end
62
+ # rubocop:enable Metrics/BlockLength
63
+ )
64
+ end
@@ -6,7 +6,7 @@ module Mutant
6
6
  module NodePredicates
7
7
 
8
8
  Types::ALL.each do |type|
9
- fail "method: #{type} is already defined" if instance_methods(true).include?(type)
9
+ fail "method: #{type} is already defined" if method_defined?(type)
10
10
 
11
11
  name = "n_#{type.to_s.chomp('?')}?"
12
12
 
@@ -73,7 +73,6 @@ module Mutant
73
73
  end
74
74
 
75
75
  # rubocop:disable Metrics/MethodLength
76
- # rubocop:disable Style/MultilineBlockChain
77
76
  def self.setup_integration(env:, mutations:, selected_subjects:)
78
77
  env.record(__method__) do
79
78
  hooks = env.hooks
@@ -84,14 +83,13 @@ module Mutant
84
83
  mutations:,
85
84
  selector: Selector::Expression.new(integration:),
86
85
  subjects: selected_subjects
87
- )
88
- end.tap { hooks.run(:setup_integration_post) }
86
+ ).tap { hooks.run(:setup_integration_post) }
87
+ end
89
88
  end
90
89
  end
91
90
  private_class_method :setup_integration
92
- # rubocop:enable Metrics/MethodLength
93
- # rubocop:enable Style/MultilineBlockChain
94
91
 
92
+ # rubocop:enable Metrics/MethodLength
95
93
  def self.load_hooks(env)
96
94
  env.record(__method__) do
97
95
  env.with(hooks: Hooks.load_config(env.config))
@@ -36,7 +36,7 @@ module Mutant
36
36
  def methods(env)
37
37
  candidate_names.each_with_object([]) do |name, methods|
38
38
  method = access(env, name)
39
- methods << method if method&.owner.equal?(candidate_scope)
39
+ methods << method if method
40
40
  end
41
41
  end
42
42
 
@@ -50,10 +50,8 @@ module Mutant
50
50
  abstract_method :candidate_scope
51
51
  private :candidate_scope
52
52
 
53
- # Matcher for singleton methods
54
- class Singleton < self
55
- MATCHER = Matcher::Method::Singleton
56
-
53
+ # Matcher for eigenclass methods
54
+ class Eigenclass < self
57
55
  private
58
56
 
59
57
  def access(_env, method_name)
@@ -63,22 +61,16 @@ module Mutant
63
61
  def candidate_scope
64
62
  scope.raw.singleton_class
65
63
  end
64
+ end # Eigenclass
66
65
 
66
+ # Matcher for singleton methods
67
+ class Singleton < Eigenclass
68
+ MATCHER = Matcher::Method::Singleton
67
69
  end # Singleton
68
70
 
69
71
  # Matcher for metaclass methods
70
- class Metaclass < self
72
+ class Metaclass < Eigenclass
71
73
  MATCHER = Matcher::Method::Metaclass
72
-
73
- private
74
-
75
- def access(_env, method_name)
76
- scope.raw.method(method_name)
77
- end
78
-
79
- def candidate_scope
80
- scope.raw.singleton_class
81
- end
82
74
  end # Metaclass
83
75
 
84
76
  # Matcher for instance methods
@@ -104,17 +96,14 @@ module Mutant
104
96
 
105
97
  private
106
98
 
107
- # rubocop:disable Lint/RescueException
108
- # mutant:disable - unstable source locations under < ruby-3.2
109
99
  def access(env, method_name)
110
100
  candidate_scope.instance_method(method_name)
111
- rescue Exception => exception
101
+ rescue => exception
112
102
  env.warn(
113
103
  MESSAGE % { scope:, method_name:, exception: exception.inspect }
114
104
  )
115
105
  nil
116
106
  end
117
- # rubocop:enable Lint/RescueException
118
107
 
119
108
  def candidate_scope
120
109
  scope.raw
@@ -46,7 +46,7 @@ module Mutant
46
46
  to_i: %i[to_int],
47
47
  to_s: %i[to_str],
48
48
  values_at: %i[fetch_values]
49
- }.freeze.tap { |hash| hash.values(&:freeze) }
49
+ }.freeze.tap { |hash| hash.each_value(&:freeze) }
50
50
  end
51
51
 
52
52
  class Light < self
@@ -8,7 +8,7 @@ module Mutant
8
8
  # Mutation emitter to handle named value access nodes
9
9
  class Access < Node
10
10
 
11
- handle(:gvar, :cvar, :lvar, :self)
11
+ handle(:gvar, :cvar, :ivar, :lvar, :self)
12
12
 
13
13
  private
14
14
 
@@ -16,30 +16,6 @@ module Mutant
16
16
  emit_singletons
17
17
  end
18
18
 
19
- # Named value access emitter for instance variables
20
- class Ivar < Access
21
- NAME_RANGE = (1..-1)
22
-
23
- handle(:ivar)
24
-
25
- children :name
26
-
27
- private
28
-
29
- def dispatch
30
- emit_attribute_read
31
- super
32
- end
33
-
34
- def emit_attribute_read
35
- emit(s(:send, nil, attribute_name))
36
- end
37
-
38
- def attribute_name
39
- name.slice(NAME_RANGE).to_sym
40
- end
41
- end # Ivar
42
-
43
19
  end # Access
44
20
  end # NamedValue
45
21
  end # Node
@@ -10,6 +10,7 @@ module Mutant
10
10
  HEADER_FORMAT = 'N'
11
11
  HEADER_SIZE = 4
12
12
  MAX_BYTES = (2**32).pred
13
+ MAX_LOG_CHUNK = 4096
13
14
 
14
15
  class Reader
15
16
  include Anima.new(:deadline, :io, :marshal, :response_reader, :log_reader)
@@ -86,10 +87,10 @@ module Mutant
86
87
 
87
88
  def advance_result
88
89
  if length
89
- if read_buffer(length)
90
+ if read_result_buffer(length)
90
91
  @results << marshal.load(@buffer)
91
92
  end
92
- elsif read_buffer(HEADER_SIZE)
93
+ elsif read_result_buffer(HEADER_SIZE)
93
94
  @lengths << Util.one(@buffer.unpack(HEADER_FORMAT))
94
95
  @buffer = +''
95
96
  end
@@ -100,17 +101,16 @@ module Mutant
100
101
  end
101
102
 
102
103
  def advance_log
103
- with_nonblock_read(io: log_reader, max_bytes: 4096, &log.public_method(:<<))
104
+ while with_nonblock_read(io: log_reader, max_bytes: MAX_LOG_CHUNK, &@log.public_method(:<<))
105
+ end
104
106
  end
105
107
 
106
- def read_buffer(max_bytes)
108
+ def read_result_buffer(max_bytes)
107
109
  with_nonblock_read(
108
110
  io: response_reader,
109
- max_bytes: max_bytes - @buffer.bytesize
110
- ) do |chunk|
111
- @buffer << chunk
112
- @buffer.bytesize.equal?(max_bytes)
113
- end
111
+ max_bytes: max_bytes - @buffer.bytesize,
112
+ &@buffer.public_method(:<<)
113
+ )
114
114
  end
115
115
 
116
116
  # rubocop:disable Metrics/MethodLength
@@ -123,8 +123,11 @@ module Mutant
123
123
  when nil
124
124
  @errors << EOFError
125
125
  false
126
+ when :wait_readable
127
+ false
126
128
  when String
127
129
  yield chunk
130
+ chunk.bytesize.equal?(max_bytes)
128
131
  else
129
132
  fail "Unexpected nonblocking read return: #{chunk.inspect}"
130
133
  end
data/lib/mutant/range.rb CHANGED
@@ -12,4 +12,4 @@ module Mutant
12
12
  left.end >= right.begin && right.end >= left.begin
13
13
  end
14
14
  end # Range
15
- end # end
15
+ end # Mutant
@@ -1,6 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'pathname'
4
+
3
5
  module Mutant
4
6
  # Current mutant version
5
- VERSION = '0.13.5'
7
+ #
8
+ # See RUST.md for documentation on version loading behavior.
9
+ VERSION =
10
+ if ENV['MUTANT_RUST']
11
+ ENV.fetch('MUTANT_VERSION').freeze
12
+ else
13
+ Pathname
14
+ .new(__dir__)
15
+ .parent
16
+ .parent
17
+ .join('VERSION')
18
+ .read
19
+ .chomp
20
+ .freeze
21
+ end
6
22
  end # Mutant
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mutant
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.5
4
+ version: 0.14.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Markus Schirp
@@ -29,14 +29,14 @@ dependencies:
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: 1.15.2
32
+ version: '1.15'
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 1.15.2
39
+ version: '1.15'
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: parser
42
42
  requirement: !ruby/object:Gem::Requirement
@@ -71,14 +71,14 @@ dependencies:
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: 0.5.0
74
+ version: 0.6.0
75
75
  type: :runtime
76
76
  prerelease: false
77
77
  version_requirements: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
- version: 0.5.0
81
+ version: 0.6.0
82
82
  - !ruby/object:Gem::Dependency
83
83
  name: unparser
84
84
  requirement: !ruby/object:Gem::Requirement
@@ -127,14 +127,14 @@ dependencies:
127
127
  requirements:
128
128
  - - "~>"
129
129
  - !ruby/object:Gem::Version
130
- version: 1.3.0
130
+ version: '2.0'
131
131
  type: :development
132
132
  prerelease: false
133
133
  version_requirements: !ruby/object:Gem::Requirement
134
134
  requirements:
135
135
  - - "~>"
136
136
  - !ruby/object:Gem::Version
137
- version: 1.3.0
137
+ version: '2.0'
138
138
  - !ruby/object:Gem::Dependency
139
139
  name: rubocop
140
140
  requirement: !ruby/object:Gem::Requirement
@@ -154,12 +154,15 @@ email:
154
154
  - mbj@schirp-dso.com
155
155
  executables:
156
156
  - mutant
157
+ - mutant-ruby
157
158
  extensions: []
158
159
  extra_rdoc_files:
159
160
  - LICENSE
160
161
  files:
161
162
  - LICENSE
163
+ - VERSION
162
164
  - bin/mutant
165
+ - bin/mutant-ruby
163
166
  - lib/mutant.rb
164
167
  - lib/mutant/ast.rb
165
168
  - lib/mutant/ast/find_metaclass_containing.rb