mutant 0.9.2 → 0.9.7
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 +4 -4
- data/Changelog.md +26 -0
- data/Gemfile +0 -8
- data/Gemfile.lock +55 -59
- data/LICENSE +1 -1
- data/README.md +9 -0
- data/config/rubocop.yml +10 -3
- data/docs/commercial-support.md +14 -0
- data/lib/mutant.rb +5 -4
- data/lib/mutant/cli.rb +5 -5
- data/lib/mutant/config.rb +1 -0
- data/lib/mutant/integration.rb +1 -1
- data/lib/mutant/license.rb +33 -6
- data/lib/mutant/license/subscription/opensource.rb +1 -1
- data/lib/mutant/meta.rb +1 -3
- data/lib/mutant/meta/example/verification.rb +1 -1
- data/lib/mutant/mutator/node/generic.rb +25 -2
- data/lib/mutant/mutator/node/send.rb +1 -1
- data/lib/mutant/parallel.rb +1 -1
- data/lib/mutant/reporter/cli/format.rb +1 -1
- data/lib/mutant/transform.rb +6 -5
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/warnings.rb +1 -1
- data/lib/mutant/zombifier.rb +2 -0
- data/mutant.gemspec +17 -16
- data/spec/integrations.yml +3 -1
- data/spec/support/corpus.rb +3 -3
- data/spec/support/ruby_vm.rb +1 -2
- data/spec/support/shared_context.rb +3 -3
- data/spec/support/xspec.rb +2 -2
- data/spec/unit/mutant/license_spec.rb +43 -7
- data/spec/unit/mutant/parallel/driver_spec.rb +4 -4
- data/spec/unit/mutant/parallel/worker_spec.rb +5 -5
- data/spec/unit/mutant/parallel_spec.rb +7 -7
- data/spec/unit/mutant/repository/diff/ranges_spec.rb +2 -2
- metadata +31 -24
- data/lib/mutant/base.rb +0 -192
- data/lib/mutant/variable.rb +0 -282
- data/spec/unit/mutant/either_spec.rb +0 -247
- data/spec/unit/mutant/maybe_spec.rb +0 -60
- data/spec/unit/mutant/variable_spec.rb +0 -618
data/lib/mutant/variable.rb
DELETED
@@ -1,282 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mutant
|
4
|
-
# Lightweight shared variables
|
5
|
-
#
|
6
|
-
# ignore :reek:TooManyMethods
|
7
|
-
class Variable
|
8
|
-
EMPTY = Class.new do
|
9
|
-
const_set(:INSPECT, 'Mutant::Variable::EMPTY')
|
10
|
-
end.new.freeze
|
11
|
-
|
12
|
-
TIMEOUT = Class.new do
|
13
|
-
const_set(:INSPECT, 'Mutant::Variable::TIMEOUT')
|
14
|
-
end.new.freeze
|
15
|
-
|
16
|
-
# Result of operation that may time out
|
17
|
-
class Result
|
18
|
-
include AbstractType, Adamantium::Flat
|
19
|
-
|
20
|
-
# Test if take resulted in a timeout
|
21
|
-
#
|
22
|
-
# @return [Boolean]
|
23
|
-
#
|
24
|
-
# @api private
|
25
|
-
def timeout?
|
26
|
-
instance_of?(Timeout)
|
27
|
-
end
|
28
|
-
|
29
|
-
abstract_method :value
|
30
|
-
|
31
|
-
# Instance returned on timeouts
|
32
|
-
class Timeout < self
|
33
|
-
include Equalizer.new
|
34
|
-
|
35
|
-
INSTANCE = new
|
36
|
-
|
37
|
-
# Construct new object
|
38
|
-
#
|
39
|
-
# @return [Timeout]
|
40
|
-
def self.new
|
41
|
-
INSTANCE
|
42
|
-
end
|
43
|
-
end # Timeout
|
44
|
-
|
45
|
-
# Instance returned without timeouts
|
46
|
-
class Value < self
|
47
|
-
include Concord::Public.new(:value)
|
48
|
-
end # Value
|
49
|
-
end # Result
|
50
|
-
|
51
|
-
private_constant(*constants(false))
|
52
|
-
|
53
|
-
# Initialize object
|
54
|
-
#
|
55
|
-
# @param [Object] value
|
56
|
-
# the initial value
|
57
|
-
#
|
58
|
-
# @return [undefined]
|
59
|
-
def initialize(condition_variable:, mutex:, value: EMPTY)
|
60
|
-
@full = condition_variable.new
|
61
|
-
@mutex = mutex.new
|
62
|
-
@value = value
|
63
|
-
end
|
64
|
-
|
65
|
-
# Take value from mvar, block on empty
|
66
|
-
#
|
67
|
-
# @return [Object]
|
68
|
-
def take
|
69
|
-
synchronize do
|
70
|
-
wait_full
|
71
|
-
perform_take
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
# Take value from mvar, with timeout
|
76
|
-
#
|
77
|
-
# @param [Float] Timeout
|
78
|
-
#
|
79
|
-
# @return [Result::Timeout]
|
80
|
-
# in case take resulted in a timeout
|
81
|
-
#
|
82
|
-
# @return [Result::Value]
|
83
|
-
# in case take resulted in a value
|
84
|
-
def take_timeout(timeout)
|
85
|
-
synchronize do
|
86
|
-
if wait_timeout(@full, timeout, &method(:full?))
|
87
|
-
Result::Timeout.new
|
88
|
-
else
|
89
|
-
Result::Value.new(perform_take)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
# Read value from variable
|
95
|
-
#
|
96
|
-
# @return [Object]
|
97
|
-
# the contents of the mvar
|
98
|
-
def read
|
99
|
-
synchronize do
|
100
|
-
wait_full
|
101
|
-
@value
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
# Try put value into the variable, non blocking
|
106
|
-
#
|
107
|
-
# @param [Object] value
|
108
|
-
#
|
109
|
-
# @return [self]
|
110
|
-
def try_put(value)
|
111
|
-
synchronize do
|
112
|
-
perform_put(value) if empty?
|
113
|
-
end
|
114
|
-
|
115
|
-
self
|
116
|
-
end
|
117
|
-
|
118
|
-
# Execute block with value, blocking
|
119
|
-
#
|
120
|
-
# @yield [Object]
|
121
|
-
#
|
122
|
-
# @return [Object]
|
123
|
-
# the blocks return value
|
124
|
-
def with
|
125
|
-
synchronize do
|
126
|
-
wait_full
|
127
|
-
yield @value
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
private
|
132
|
-
|
133
|
-
# Perform the put
|
134
|
-
#
|
135
|
-
# @param [Object] value
|
136
|
-
def perform_put(value)
|
137
|
-
(@value = value).tap { @full.signal }
|
138
|
-
end
|
139
|
-
|
140
|
-
# Execute block under mutex
|
141
|
-
#
|
142
|
-
# @return [self]
|
143
|
-
def synchronize(&block)
|
144
|
-
@mutex.synchronize(&block)
|
145
|
-
end
|
146
|
-
|
147
|
-
# Wait for block predicate
|
148
|
-
#
|
149
|
-
# @param [ConditionVariable] event
|
150
|
-
#
|
151
|
-
# @return [undefined]
|
152
|
-
def wait(event)
|
153
|
-
event.wait(@mutex) until yield
|
154
|
-
end
|
155
|
-
|
156
|
-
# Wait with timeout for block predicate
|
157
|
-
#
|
158
|
-
# @param [ConditionVariable] event
|
159
|
-
#
|
160
|
-
# @return [Boolean]
|
161
|
-
# if wait was terminated due a timeout
|
162
|
-
#
|
163
|
-
# @return [undefined]
|
164
|
-
# otherwise
|
165
|
-
def wait_timeout(event, timeout)
|
166
|
-
loop do
|
167
|
-
break true if timeout <= 0
|
168
|
-
break if yield
|
169
|
-
timeout -= Timer.elapsed { event.wait(@mutex, timeout) }
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
# Wait till mvar is full
|
174
|
-
#
|
175
|
-
# @return [undefined]
|
176
|
-
def wait_full
|
177
|
-
wait(@full, &method(:full?))
|
178
|
-
end
|
179
|
-
|
180
|
-
# Test if state is full
|
181
|
-
#
|
182
|
-
# @return [Boolean]
|
183
|
-
def full?
|
184
|
-
!empty?
|
185
|
-
end
|
186
|
-
|
187
|
-
# Test if state is empty
|
188
|
-
#
|
189
|
-
# @return [Boolean]
|
190
|
-
def empty?
|
191
|
-
@value.equal?(EMPTY)
|
192
|
-
end
|
193
|
-
|
194
|
-
# Shared variable that can be written at most once
|
195
|
-
#
|
196
|
-
# ignore :reek:InstanceVariableAssumption
|
197
|
-
class IVar < self
|
198
|
-
|
199
|
-
# Exception raised on ivar errors
|
200
|
-
class Error < RuntimeError; end
|
201
|
-
|
202
|
-
# Put valie into the mvar, raises if already full
|
203
|
-
#
|
204
|
-
# @param [Object] value
|
205
|
-
#
|
206
|
-
# @return [self]
|
207
|
-
#
|
208
|
-
# @raise Error
|
209
|
-
# if already full
|
210
|
-
def put(value)
|
211
|
-
synchronize do
|
212
|
-
fail Error, 'is immutable' if full?
|
213
|
-
perform_put(value)
|
214
|
-
end
|
215
|
-
|
216
|
-
self
|
217
|
-
end
|
218
|
-
|
219
|
-
private
|
220
|
-
|
221
|
-
# Perform take operation
|
222
|
-
#
|
223
|
-
# @return [Object]
|
224
|
-
def perform_take
|
225
|
-
@value
|
226
|
-
end
|
227
|
-
end # IVar
|
228
|
-
|
229
|
-
# Shared variable that can be written multiple times
|
230
|
-
#
|
231
|
-
# ignore :reek:InstanceVariableAssumption
|
232
|
-
class MVar < self
|
233
|
-
|
234
|
-
# Initialize object
|
235
|
-
#
|
236
|
-
# @param [Object] value
|
237
|
-
# the initial value
|
238
|
-
#
|
239
|
-
# @return [undefined]
|
240
|
-
def initialize(condition_variable:, mutex:, value: EMPTY)
|
241
|
-
super
|
242
|
-
@empty = condition_variable.new
|
243
|
-
end
|
244
|
-
|
245
|
-
# Put value into mvar, block on full
|
246
|
-
#
|
247
|
-
# @param [Object] value
|
248
|
-
#
|
249
|
-
# @return [self]
|
250
|
-
def put(value)
|
251
|
-
synchronize do
|
252
|
-
wait(@empty, &method(:empty?))
|
253
|
-
perform_put(value)
|
254
|
-
end
|
255
|
-
|
256
|
-
self
|
257
|
-
end
|
258
|
-
|
259
|
-
# Modify mvar
|
260
|
-
#
|
261
|
-
# @return [Object]
|
262
|
-
def modify
|
263
|
-
synchronize do
|
264
|
-
wait_full
|
265
|
-
perform_put(yield(@value))
|
266
|
-
end
|
267
|
-
end
|
268
|
-
|
269
|
-
private
|
270
|
-
|
271
|
-
# Empty the mvar
|
272
|
-
#
|
273
|
-
# @return [Object]
|
274
|
-
def perform_take
|
275
|
-
@value.tap do
|
276
|
-
@value = EMPTY
|
277
|
-
@empty.signal
|
278
|
-
end
|
279
|
-
end
|
280
|
-
end # MVar
|
281
|
-
end # Variable
|
282
|
-
end # Mutant
|
@@ -1,247 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Mutant::Either do
|
4
|
-
describe '.wrap_error' do
|
5
|
-
def apply
|
6
|
-
described_class.wrap_error(error, &block)
|
7
|
-
end
|
8
|
-
|
9
|
-
let(:error) { TestError }
|
10
|
-
|
11
|
-
class TestError < RuntimeError; end
|
12
|
-
|
13
|
-
context 'when block returns' do
|
14
|
-
let(:value) { instance_double(Object, 'value') }
|
15
|
-
let(:block) { -> { value } }
|
16
|
-
|
17
|
-
it 'returns right wrapping block value' do
|
18
|
-
expect(apply).to eql(described_class::Right.new(value))
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
context 'when block raises' do
|
23
|
-
let(:exception) { error.new }
|
24
|
-
let(:block) { -> { fail exception } }
|
25
|
-
|
26
|
-
context 'with covered exception' do
|
27
|
-
it 'returns left wrapping exception' do
|
28
|
-
expect(apply).to eql(described_class::Left.new(exception))
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
context 'with uncovered exception' do
|
33
|
-
let(:exception) { StandardError.new }
|
34
|
-
|
35
|
-
it 'returns raises error' do
|
36
|
-
expect { apply }.to raise_error(StandardError)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
RSpec.describe Mutant::Either::Left do
|
44
|
-
subject { described_class.new(value) }
|
45
|
-
|
46
|
-
let(:block_result) { instance_double(Object, 'block result') }
|
47
|
-
let(:value) { instance_double(Object, 'value') }
|
48
|
-
let(:yields) { [] }
|
49
|
-
|
50
|
-
let(:block) do
|
51
|
-
lambda do |value|
|
52
|
-
yields << value
|
53
|
-
block_result
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
class TestError < RuntimeError; end
|
58
|
-
|
59
|
-
describe '#fmap' do
|
60
|
-
def apply
|
61
|
-
subject.fmap(&block)
|
62
|
-
end
|
63
|
-
|
64
|
-
include_examples 'no block evaluation'
|
65
|
-
include_examples 'requires block'
|
66
|
-
include_examples 'returns self'
|
67
|
-
end
|
68
|
-
|
69
|
-
describe '#apply' do
|
70
|
-
def apply
|
71
|
-
subject.apply(&block)
|
72
|
-
end
|
73
|
-
|
74
|
-
include_examples 'no block evaluation'
|
75
|
-
include_examples 'requires block'
|
76
|
-
include_examples 'returns self'
|
77
|
-
end
|
78
|
-
|
79
|
-
describe '#from_left' do
|
80
|
-
def apply
|
81
|
-
subject.from_left(&block)
|
82
|
-
end
|
83
|
-
|
84
|
-
it 'returns left value' do
|
85
|
-
expect(apply).to be(value)
|
86
|
-
end
|
87
|
-
|
88
|
-
include_examples 'no block evaluation'
|
89
|
-
end
|
90
|
-
|
91
|
-
describe '#from_right' do
|
92
|
-
def apply
|
93
|
-
subject.from_right(&block)
|
94
|
-
end
|
95
|
-
|
96
|
-
context 'without block' do
|
97
|
-
let(:block) { nil }
|
98
|
-
|
99
|
-
it 'raises RuntimeError error' do
|
100
|
-
expect { apply }.to raise_error(
|
101
|
-
RuntimeError,
|
102
|
-
"Expected right value, got #{subject.inspect}"
|
103
|
-
)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
context 'with block' do
|
108
|
-
let(:yields) { [] }
|
109
|
-
let(:block_return) { instance_double(Object, 'block-return') }
|
110
|
-
|
111
|
-
let(:block) do
|
112
|
-
lambda do |value|
|
113
|
-
yields << value
|
114
|
-
block_return
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
it 'calls block with left value' do
|
119
|
-
expect { apply }.to change(yields, :to_a).from([]).to([value])
|
120
|
-
end
|
121
|
-
|
122
|
-
it 'returns block value' do
|
123
|
-
expect(apply).to be(block_return)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
describe '#lmap' do
|
129
|
-
def apply
|
130
|
-
subject.lmap(&block)
|
131
|
-
end
|
132
|
-
|
133
|
-
include_examples 'requires block'
|
134
|
-
include_examples 'Functor#fmap block evaluation'
|
135
|
-
end
|
136
|
-
|
137
|
-
describe '#either' do
|
138
|
-
def apply
|
139
|
-
subject.either(block, -> { fail })
|
140
|
-
end
|
141
|
-
|
142
|
-
include_examples '#apply block evaluation'
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
RSpec.describe Mutant::Either::Right do
|
147
|
-
subject { described_class.new(value) }
|
148
|
-
|
149
|
-
let(:block_result) { instance_double(Object, 'block result') }
|
150
|
-
let(:value) { instance_double(Object, 'value') }
|
151
|
-
let(:yields) { [] }
|
152
|
-
|
153
|
-
let(:block) do
|
154
|
-
lambda do |value|
|
155
|
-
yields << value
|
156
|
-
block_result
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
describe '#fmap' do
|
161
|
-
def apply
|
162
|
-
subject.fmap(&block)
|
163
|
-
end
|
164
|
-
|
165
|
-
include_examples 'requires block'
|
166
|
-
include_examples 'Functor#fmap block evaluation'
|
167
|
-
end
|
168
|
-
|
169
|
-
describe '#apply' do
|
170
|
-
def apply
|
171
|
-
subject.apply(&block)
|
172
|
-
end
|
173
|
-
|
174
|
-
include_examples 'requires block'
|
175
|
-
include_examples '#apply block evaluation'
|
176
|
-
end
|
177
|
-
|
178
|
-
describe '#from_left' do
|
179
|
-
def apply
|
180
|
-
subject.from_left(&block)
|
181
|
-
end
|
182
|
-
|
183
|
-
context 'without block' do
|
184
|
-
let(:block) { nil }
|
185
|
-
|
186
|
-
it 'raises RuntimeError error' do
|
187
|
-
expect { apply }.to raise_error(
|
188
|
-
RuntimeError,
|
189
|
-
"Expected left value, got #{subject.inspect}"
|
190
|
-
)
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
context 'with block' do
|
195
|
-
let(:yields) { [] }
|
196
|
-
let(:block_return) { instance_double(Object, 'block-return') }
|
197
|
-
|
198
|
-
let(:block) do
|
199
|
-
lambda do |value|
|
200
|
-
yields << value
|
201
|
-
block_return
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
it 'calls block with right value' do
|
206
|
-
expect { apply }.to change(yields, :to_a).from([]).to([value])
|
207
|
-
end
|
208
|
-
|
209
|
-
it 'returns block value' do
|
210
|
-
expect(apply).to be(block_return)
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
describe '#from_right' do
|
216
|
-
def apply
|
217
|
-
subject.from_right(&block)
|
218
|
-
end
|
219
|
-
|
220
|
-
it 'returns right value' do
|
221
|
-
expect(apply).to be(value)
|
222
|
-
end
|
223
|
-
|
224
|
-
include_examples 'no block evaluation'
|
225
|
-
end
|
226
|
-
|
227
|
-
describe '#lmap' do
|
228
|
-
def apply
|
229
|
-
subject.lmap(&block)
|
230
|
-
end
|
231
|
-
|
232
|
-
include_examples 'requires block'
|
233
|
-
include_examples 'no block evaluation'
|
234
|
-
|
235
|
-
it 'returns self' do
|
236
|
-
expect(apply).to be(subject)
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
describe '#either' do
|
241
|
-
def apply
|
242
|
-
subject.either(-> { fail }, block)
|
243
|
-
end
|
244
|
-
|
245
|
-
include_examples '#apply block evaluation'
|
246
|
-
end
|
247
|
-
end
|