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/meta.rb
CHANGED
@@ -10,14 +10,12 @@ module Mutant
|
|
10
10
|
# Mutation example
|
11
11
|
class Example
|
12
12
|
|
13
|
-
# rubocop:disable MutableConstant
|
13
|
+
# rubocop:disable Style/MutableConstant
|
14
14
|
ALL = []
|
15
15
|
|
16
16
|
# Add example
|
17
17
|
#
|
18
18
|
# @return [undefined]
|
19
|
-
#
|
20
|
-
# rubocop:disable Performance/Caller
|
21
19
|
def self.add(*types, &block)
|
22
20
|
file = caller.first.split(':in', 2).first
|
23
21
|
ALL << DSL.call(file, Set.new(types), block)
|
@@ -61,7 +61,7 @@ module Mutant
|
|
61
61
|
def invalid_syntax
|
62
62
|
mutations.reject do |mutation|
|
63
63
|
::Parser::CurrentRuby.parse(mutation.source)
|
64
|
-
rescue ::Parser::SyntaxError # rubocop:disable Lint/
|
64
|
+
rescue ::Parser::SyntaxError # rubocop:disable Lint/SuppressedException
|
65
65
|
end
|
66
66
|
end
|
67
67
|
memoize :invalid_syntax
|
@@ -12,30 +12,52 @@ module Mutant
|
|
12
12
|
__LINE__
|
13
13
|
alias
|
14
14
|
arg_expr
|
15
|
+
array_pattern
|
16
|
+
array_pattern_with_tail
|
15
17
|
back_ref
|
16
18
|
blockarg
|
17
19
|
blockarg_expr
|
20
|
+
case_match
|
18
21
|
complex
|
22
|
+
const_pattern
|
23
|
+
def_e
|
24
|
+
defs_e
|
19
25
|
eflipflop
|
20
26
|
empty
|
27
|
+
empty_else
|
21
28
|
ensure
|
29
|
+
find_pattern
|
22
30
|
for
|
31
|
+
forward_arg
|
32
|
+
forward_args
|
33
|
+
forwarded_args
|
34
|
+
hash_pattern
|
23
35
|
ident
|
36
|
+
if_guard
|
24
37
|
iflipflop
|
38
|
+
in_match
|
39
|
+
in_pattern
|
25
40
|
kwnilarg
|
26
41
|
kwrestarg
|
27
42
|
kwsplat
|
43
|
+
match_alt
|
44
|
+
match_as
|
45
|
+
match_nil_pattern
|
46
|
+
match_rest
|
47
|
+
match_var
|
28
48
|
match_with_lvasgn
|
29
|
-
|
49
|
+
match_with_trailing_comma
|
30
50
|
module
|
51
|
+
mrasgn
|
31
52
|
numargs
|
32
53
|
numblock
|
33
|
-
numparam
|
34
54
|
objc_kwarg
|
35
55
|
objc_restarg
|
36
56
|
objc_varargs
|
57
|
+
pin
|
37
58
|
postexe
|
38
59
|
preexe
|
60
|
+
rasgn
|
39
61
|
rational
|
40
62
|
redo
|
41
63
|
restarg
|
@@ -45,6 +67,7 @@ module Mutant
|
|
45
67
|
sclass
|
46
68
|
shadowarg
|
47
69
|
undef
|
70
|
+
unless_guard
|
48
71
|
until_post
|
49
72
|
while_post
|
50
73
|
xstr
|
data/lib/mutant/parallel.rb
CHANGED
data/lib/mutant/transform.rb
CHANGED
@@ -263,7 +263,7 @@ module Mutant
|
|
263
263
|
PRIMITIVE
|
264
264
|
.apply(input)
|
265
265
|
.lmap(&method(:lift_error))
|
266
|
-
.
|
266
|
+
.bind(&method(:run))
|
267
267
|
end
|
268
268
|
|
269
269
|
private
|
@@ -351,8 +351,8 @@ module Mutant
|
|
351
351
|
PRIMITIVE
|
352
352
|
.apply(input)
|
353
353
|
.lmap(&method(:lift_error))
|
354
|
-
.
|
355
|
-
.
|
354
|
+
.bind(&method(:reject_keys))
|
355
|
+
.bind(&method(:transform))
|
356
356
|
end
|
357
357
|
|
358
358
|
private
|
@@ -363,7 +363,7 @@ module Mutant
|
|
363
363
|
#
|
364
364
|
# @return [Either<Error, Hash>]
|
365
365
|
def transform(input)
|
366
|
-
transform_required(input).
|
366
|
+
transform_required(input).bind do |required|
|
367
367
|
transform_optional(input).fmap(&required.method(:merge))
|
368
368
|
end
|
369
369
|
end
|
@@ -503,7 +503,8 @@ module Mutant
|
|
503
503
|
#
|
504
504
|
# @return [Either<Error, Object>]
|
505
505
|
def apply(input)
|
506
|
-
Either
|
506
|
+
Either
|
507
|
+
.wrap_error(error_class) { block.call(input) }
|
507
508
|
.lmap { |exception| error(input: input, message: exception.to_s) }
|
508
509
|
end
|
509
510
|
end # Exception
|
data/lib/mutant/version.rb
CHANGED
data/lib/mutant/warnings.rb
CHANGED
@@ -55,7 +55,7 @@ module Mutant
|
|
55
55
|
# For that reason we do have to use the original method capture to dispatch
|
56
56
|
# in disabled state.
|
57
57
|
#
|
58
|
-
# :reek:
|
58
|
+
# ignore :reek:RepeatedConditional
|
59
59
|
class Warnings
|
60
60
|
# Error raised when warning capture is used recursively
|
61
61
|
class RecursiveUseError < RuntimeError; end
|
data/lib/mutant/zombifier.rb
CHANGED
data/mutant.gemspec
CHANGED
@@ -7,8 +7,8 @@ Gem::Specification.new do |gem|
|
|
7
7
|
gem.version = Mutant::VERSION.dup
|
8
8
|
gem.authors = ['Markus Schirp']
|
9
9
|
gem.email = ['mbj@schirp-dso.com']
|
10
|
-
gem.description = 'Mutation
|
11
|
-
gem.summary = '
|
10
|
+
gem.description = 'Mutation Testing for Ruby.'
|
11
|
+
gem.summary = ''
|
12
12
|
gem.homepage = 'https://github.com/mbj/mutant'
|
13
13
|
gem.license = 'Nonstandard'
|
14
14
|
|
@@ -21,20 +21,21 @@ Gem::Specification.new do |gem|
|
|
21
21
|
gem.extra_rdoc_files = %w[LICENSE]
|
22
22
|
gem.executables = %w[mutant]
|
23
23
|
|
24
|
-
gem.add_runtime_dependency('abstract_type',
|
25
|
-
gem.add_runtime_dependency('adamantium',
|
26
|
-
gem.add_runtime_dependency('anima',
|
27
|
-
gem.add_runtime_dependency('ast',
|
28
|
-
gem.add_runtime_dependency('concord',
|
29
|
-
gem.add_runtime_dependency('diff-lcs',
|
30
|
-
gem.add_runtime_dependency('equalizer',
|
31
|
-
gem.add_runtime_dependency('ice_nine',
|
32
|
-
gem.add_runtime_dependency('memoizable',
|
33
|
-
gem.add_runtime_dependency('
|
34
|
-
gem.add_runtime_dependency('parser',
|
35
|
-
gem.add_runtime_dependency('procto',
|
36
|
-
gem.add_runtime_dependency('unparser',
|
24
|
+
gem.add_runtime_dependency('abstract_type', '~> 0.0.7')
|
25
|
+
gem.add_runtime_dependency('adamantium', '~> 0.2.0')
|
26
|
+
gem.add_runtime_dependency('anima', '~> 0.3.1')
|
27
|
+
gem.add_runtime_dependency('ast', '~> 2.2')
|
28
|
+
gem.add_runtime_dependency('concord', '~> 0.1.5')
|
29
|
+
gem.add_runtime_dependency('diff-lcs', '= 1.3')
|
30
|
+
gem.add_runtime_dependency('equalizer', '~> 0.0.9')
|
31
|
+
gem.add_runtime_dependency('ice_nine', '~> 0.11.1')
|
32
|
+
gem.add_runtime_dependency('memoizable', '~> 0.4.2')
|
33
|
+
gem.add_runtime_dependency('mprelude', '~> 0.1.0')
|
34
|
+
gem.add_runtime_dependency('parser', '~> 2.7.1')
|
35
|
+
gem.add_runtime_dependency('procto', '~> 0.0.2')
|
36
|
+
gem.add_runtime_dependency('unparser', '~> 0.4.6')
|
37
|
+
gem.add_runtime_dependency('variable', '~> 0.0.1')
|
37
38
|
|
38
|
-
gem.add_development_dependency('devtools', '~> 0.1.
|
39
|
+
gem.add_development_dependency('devtools', '~> 0.1.25')
|
39
40
|
gem.add_development_dependency('parallel', '~> 1.3')
|
40
41
|
end
|
data/spec/integrations.yml
CHANGED
@@ -2,13 +2,14 @@
|
|
2
2
|
- name: rubyspec
|
3
3
|
namespace: Rubyspec
|
4
4
|
repo_uri: 'https://github.com/ruby/rubyspec.git'
|
5
|
-
repo_ref:
|
5
|
+
repo_ref: 249a36c2e9fcddbb208a0d618d05f6bd9a64fd17
|
6
6
|
integration: mspec
|
7
7
|
mutation_coverage: false
|
8
8
|
mutation_generation: true
|
9
9
|
exclude:
|
10
10
|
- command_line/fixtures/bad_syntax.rb
|
11
11
|
- command_line/fixtures/freeze_flag_required_diff_enc.rb
|
12
|
+
- core/file/stat_spec.rb
|
12
13
|
- core/kernel/shared/sprintf_encoding.rb
|
13
14
|
- core/module/fixtures/autoload_empty.rb
|
14
15
|
- core/module/fixtures/autoload_never_set.rb
|
@@ -26,6 +27,7 @@
|
|
26
27
|
- language/predefined/fixtures/data_only.rb
|
27
28
|
- language/source_encoding_spec.rb
|
28
29
|
- library/base64/decode64_spec.rb
|
30
|
+
- library/cgi/escapeHTML_spec.rb
|
29
31
|
- security/cve_2010_1330_spec.rb
|
30
32
|
- name: regexp_parser
|
31
33
|
namespace: Regexp
|
data/spec/support/corpus.rb
CHANGED
@@ -10,7 +10,7 @@ module MutantSpec
|
|
10
10
|
|
11
11
|
# Namespace module for corpus testing
|
12
12
|
#
|
13
|
-
# rubocop:disable MethodLength
|
13
|
+
# rubocop:disable Metrics/MethodLength
|
14
14
|
module Corpus
|
15
15
|
TMP = ROOT.join('tmp').freeze
|
16
16
|
EXCLUDE_GLOB_FORMAT = '{%s}'
|
@@ -22,7 +22,7 @@ module MutantSpec
|
|
22
22
|
private_constant(*constants(false))
|
23
23
|
|
24
24
|
# Project under corpus test
|
25
|
-
# rubocop:disable ClassLength
|
25
|
+
# rubocop:disable Metrics/ClassLength
|
26
26
|
class Project
|
27
27
|
MUTEX = Mutex.new
|
28
28
|
|
@@ -259,7 +259,7 @@ module MutantSpec
|
|
259
259
|
#
|
260
260
|
# @param [Array<String>] arguments
|
261
261
|
#
|
262
|
-
# rubocop:disable GuardClause - guard clause without else does not make sense
|
262
|
+
# rubocop:disable Style/GuardClause - guard clause without else does not make sense
|
263
263
|
def system(arguments)
|
264
264
|
return if Kernel.system(*arguments)
|
265
265
|
|
data/spec/support/ruby_vm.rb
CHANGED
@@ -26,8 +26,7 @@ module MutantSpec
|
|
26
26
|
super(DEFAULTS.merge(attributes))
|
27
27
|
end
|
28
28
|
|
29
|
-
# rubocop:disable Naming/
|
30
|
-
def handle(vm, observation)
|
29
|
+
def handle(vm, observation) # rubocop:disable Naming/MethodParameterName
|
31
30
|
unless match?(observation)
|
32
31
|
fail "Unexpected event observation: #{observation.inspect}, expected #{inspect}"
|
33
32
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# rubocop:disable ModuleLength
|
3
|
+
# rubocop:disable Metrics/ModuleLength
|
4
4
|
module SharedContext
|
5
5
|
# Prepend an anonymous module with the new `with` method
|
6
6
|
#
|
@@ -25,8 +25,8 @@ module SharedContext
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
# rubocop:disable MethodLength
|
29
|
-
# rubocop:disable AbcSize
|
28
|
+
# rubocop:disable Metrics/MethodLength
|
29
|
+
# rubocop:disable Metrics/AbcSize
|
30
30
|
def setup_shared_context
|
31
31
|
let(:mutation_a) { Mutant::Mutation::Evil.new(subject_a, mutation_a_node) }
|
32
32
|
let(:mutation_a_node) { s(:false) }
|
data/spec/support/xspec.rb
CHANGED
@@ -93,7 +93,7 @@ module XSpec
|
|
93
93
|
class MessageExpectation
|
94
94
|
include Anima.new(:receiver, :selector, :arguments, :reaction)
|
95
95
|
|
96
|
-
# rubocop:disable ParameterLists
|
96
|
+
# rubocop:disable Metrics/ParameterLists
|
97
97
|
def self.parse(receiver:, selector:, arguments: [], reaction: nil)
|
98
98
|
new(
|
99
99
|
receiver: receiver,
|
@@ -152,7 +152,7 @@ module XSpec
|
|
152
152
|
expectations.empty? or fail "unconsumed expectations:\n#{expectations.map(&:inspect).join("\n")}"
|
153
153
|
end
|
154
154
|
|
155
|
-
# rubocop:disable MethodLength
|
155
|
+
# rubocop:disable Metrics/MethodLength
|
156
156
|
def self.verify(rspec_context, expectations)
|
157
157
|
verifier = new(expectations)
|
158
158
|
|
@@ -6,11 +6,13 @@ RSpec.describe Mutant::License do
|
|
6
6
|
end
|
7
7
|
|
8
8
|
let(:gem) { class_double(Gem, loaded_specs: loaded_specs) }
|
9
|
+
let(:gem_method) { instance_double(Method) }
|
9
10
|
let(:gem_path) { '/path/to/mutant-license' }
|
10
11
|
let(:gem_pathname) { instance_double(Pathname) }
|
11
12
|
let(:json) { class_double(JSON) }
|
12
13
|
let(:kernel) { class_double(Kernel) }
|
13
14
|
let(:license_pathname) { instance_double(Pathname) }
|
15
|
+
let(:load_json) { true }
|
14
16
|
let(:loaded_specs) { { 'mutant-license' => spec } }
|
15
17
|
let(:path) { instance_double(Pathname) }
|
16
18
|
let(:pathname) { class_double(Pathname) }
|
@@ -26,15 +28,17 @@ RSpec.describe Mutant::License do
|
|
26
28
|
let(:world) do
|
27
29
|
instance_double(
|
28
30
|
Mutant::World,
|
29
|
-
gem:
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
gem: gem,
|
32
|
+
gem_method: gem_method,
|
33
|
+
json: json,
|
34
|
+
kernel: kernel,
|
35
|
+
pathname: pathname,
|
36
|
+
stderr: stderr
|
34
37
|
)
|
35
38
|
end
|
36
39
|
|
37
40
|
before do
|
41
|
+
allow(gem_method).to receive_messages(call: undefined)
|
38
42
|
allow(gem_pathname).to receive_messages(join: license_pathname)
|
39
43
|
allow(json).to receive_messages(load: license_json)
|
40
44
|
allow(kernel).to receive_messages(sleep: undefined)
|
@@ -66,6 +70,18 @@ RSpec.describe Mutant::License do
|
|
66
70
|
it 'performs IO in expected sequence' do
|
67
71
|
expect(apply).to eql(Mutant::Either::Right.new(true))
|
68
72
|
|
73
|
+
expect(gem_method)
|
74
|
+
.to have_received(:call)
|
75
|
+
.with('mutant-license', '~> 0.1.0')
|
76
|
+
.ordered
|
77
|
+
|
78
|
+
if load_json
|
79
|
+
expect(json)
|
80
|
+
.to have_received(:load)
|
81
|
+
.with(license_pathname)
|
82
|
+
.ordered
|
83
|
+
end
|
84
|
+
|
69
85
|
expect(stderr)
|
70
86
|
.to have_received(:puts)
|
71
87
|
.with(expected)
|
@@ -73,12 +89,22 @@ RSpec.describe Mutant::License do
|
|
73
89
|
|
74
90
|
expect(stderr)
|
75
91
|
.to have_received(:puts)
|
76
|
-
.with('Soft fail, continuing in
|
92
|
+
.with('[Mutant-License-Error]: Soft fail, continuing in 40 seconds')
|
93
|
+
.ordered
|
94
|
+
|
95
|
+
expect(stderr)
|
96
|
+
.to have_received(:puts)
|
97
|
+
.with('[Mutant-License-Error]: Next major version will enforce the license')
|
98
|
+
.ordered
|
99
|
+
|
100
|
+
expect(stderr)
|
101
|
+
.to have_received(:puts)
|
102
|
+
.with('[Mutant-License-Error]: See https://github.com/mbj/mutant#licensing')
|
77
103
|
.ordered
|
78
104
|
|
79
105
|
expect(kernel)
|
80
106
|
.to have_received(:sleep)
|
81
|
-
.with(
|
107
|
+
.with(40)
|
82
108
|
.ordered
|
83
109
|
end
|
84
110
|
end
|
@@ -253,5 +279,15 @@ RSpec.describe Mutant::License do
|
|
253
279
|
[none]
|
254
280
|
MESSAGE
|
255
281
|
end
|
282
|
+
|
283
|
+
context 'when mutant-license gem cannot be loaded' do
|
284
|
+
let(:load_json) { false }
|
285
|
+
|
286
|
+
before do
|
287
|
+
allow(gem_method).to receive(:call).and_raise(Gem::LoadError, 'test-error')
|
288
|
+
end
|
289
|
+
|
290
|
+
it_fails_with_message '[Mutant-License-Error]: test-error'
|
291
|
+
end
|
256
292
|
end
|
257
293
|
end
|
@@ -16,15 +16,15 @@ RSpec.describe Mutant::Parallel::Driver do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
let(:var_active_jobs) do
|
19
|
-
instance_double(
|
19
|
+
instance_double(Variable::IVar, 'active jobs')
|
20
20
|
end
|
21
21
|
|
22
22
|
let(:var_final) do
|
23
|
-
instance_double(
|
23
|
+
instance_double(Variable::IVar, 'final')
|
24
24
|
end
|
25
25
|
|
26
26
|
let(:var_sink) do
|
27
|
-
instance_double(
|
27
|
+
instance_double(Variable::IVar, 'sink')
|
28
28
|
end
|
29
29
|
|
30
30
|
subject do
|
@@ -64,7 +64,7 @@ RSpec.describe Mutant::Parallel::Driver do
|
|
64
64
|
receiver: var_final,
|
65
65
|
selector: :take_timeout,
|
66
66
|
arguments: [timeout],
|
67
|
-
reaction: { return:
|
67
|
+
reaction: { return: Variable.const_get(:Result)::Timeout.new }
|
68
68
|
},
|
69
69
|
{
|
70
70
|
receiver: var_active_jobs,
|