mutant 0.8.24 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +3 -3
- data/Changelog.md +14 -654
- data/Gemfile +13 -0
- data/Gemfile.lock +59 -64
- data/LICENSE +271 -20
- data/README.md +73 -140
- data/Rakefile +0 -21
- data/bin/mutant +7 -2
- data/config/reek.yml +2 -1
- data/config/rubocop.yml +5 -9
- data/docs/incremental.md +76 -0
- data/docs/known-problems.md +0 -14
- data/docs/mutant-minitest.md +1 -1
- data/docs/mutant-rspec.md +2 -24
- data/lib/mutant.rb +45 -53
- data/lib/mutant/ast/nodes.rb +0 -2
- data/lib/mutant/ast/types.rb +1 -117
- data/lib/mutant/base.rb +192 -0
- data/lib/mutant/bootstrap.rb +145 -0
- data/lib/mutant/cli.rb +68 -54
- data/lib/mutant/config.rb +119 -6
- data/lib/mutant/env.rb +94 -8
- data/lib/mutant/expression.rb +6 -1
- data/lib/mutant/expression/parser.rb +9 -31
- data/lib/mutant/integration.rb +64 -36
- data/lib/mutant/isolation.rb +16 -1
- data/lib/mutant/isolation/fork.rb +105 -40
- data/lib/mutant/license.rb +34 -0
- data/lib/mutant/license/subscription.rb +47 -0
- data/lib/mutant/license/subscription/commercial.rb +57 -0
- data/lib/mutant/license/subscription/opensource.rb +77 -0
- data/lib/mutant/loader.rb +27 -4
- data/lib/mutant/matcher.rb +48 -1
- data/lib/mutant/matcher/chain.rb +1 -1
- data/lib/mutant/matcher/config.rb +0 -2
- data/lib/mutant/matcher/filter.rb +1 -1
- data/lib/mutant/matcher/method.rb +11 -7
- data/lib/mutant/matcher/methods.rb +1 -1
- data/lib/mutant/matcher/namespace.rb +1 -1
- data/lib/mutant/matcher/null.rb +1 -1
- data/lib/mutant/matcher/scope.rb +1 -1
- data/lib/mutant/meta/example/dsl.rb +0 -8
- data/lib/mutant/mutation.rb +1 -2
- data/lib/mutant/mutator/node.rb +2 -9
- data/lib/mutant/mutator/node/arguments.rb +1 -1
- data/lib/mutant/mutator/node/class.rb +0 -8
- data/lib/mutant/mutator/node/define.rb +0 -12
- data/lib/mutant/mutator/node/generic.rb +30 -44
- data/lib/mutant/mutator/node/index.rb +4 -4
- data/lib/mutant/mutator/node/literal/regex.rb +0 -39
- data/lib/mutant/mutator/node/send.rb +13 -12
- data/lib/mutant/parallel.rb +61 -40
- data/lib/mutant/parallel/driver.rb +59 -0
- data/lib/mutant/parallel/source.rb +6 -2
- data/lib/mutant/parallel/worker.rb +63 -45
- data/lib/mutant/range.rb +15 -0
- data/lib/mutant/reporter/cli.rb +5 -11
- data/lib/mutant/reporter/cli/format.rb +3 -46
- data/lib/mutant/reporter/cli/printer/config.rb +5 -6
- data/lib/mutant/reporter/cli/printer/env.rb +40 -0
- data/lib/mutant/reporter/cli/printer/env_progress.rb +13 -17
- data/lib/mutant/reporter/cli/printer/isolation_result.rb +17 -3
- data/lib/mutant/reporter/cli/printer/mutation_result.rb +2 -3
- data/lib/mutant/reporter/cli/printer/status_progressive.rb +19 -10
- data/lib/mutant/repository.rb +0 -65
- data/lib/mutant/repository/diff.rb +104 -0
- data/lib/mutant/repository/diff/ranges.rb +52 -0
- data/lib/mutant/result.rb +16 -7
- data/lib/mutant/runner.rb +38 -47
- data/lib/mutant/runner/sink.rb +1 -1
- data/lib/mutant/selector/null.rb +19 -0
- data/lib/mutant/subject.rb +3 -1
- data/lib/mutant/subject/method/instance.rb +3 -1
- data/lib/mutant/transform.rb +511 -0
- data/lib/mutant/variable.rb +282 -0
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/warnings.rb +113 -0
- data/meta/case.rb +1 -0
- data/meta/class.rb +0 -9
- data/meta/def.rb +1 -26
- data/meta/regexp.rb +10 -20
- data/meta/send.rb +14 -46
- data/mutant-minitest.gemspec +1 -1
- data/mutant-rspec.gemspec +2 -2
- data/mutant.gemspec +15 -16
- data/mutant.yml +6 -0
- data/spec/integration/mutant/isolation/fork_spec.rb +22 -5
- data/spec/integration/mutant/minitest_spec.rb +3 -2
- data/spec/integration/mutant/rspec_spec.rb +4 -3
- data/spec/integrations.yml +16 -13
- data/spec/shared/base_behavior.rb +45 -0
- data/spec/shared/framework_integration_behavior.rb +43 -14
- data/spec/spec_helper.rb +21 -17
- data/spec/support/corpus.rb +56 -95
- data/spec/support/shared_context.rb +37 -14
- data/spec/support/xspec.rb +7 -3
- data/spec/unit/mutant/bootstrap_spec.rb +216 -0
- data/spec/unit/mutant/cli_spec.rb +173 -117
- data/spec/unit/mutant/config_spec.rb +126 -0
- data/spec/unit/mutant/either_spec.rb +247 -0
- data/spec/unit/mutant/env_spec.rb +162 -40
- data/spec/unit/mutant/expression/method_spec.rb +16 -0
- data/spec/unit/mutant/expression/parser_spec.rb +29 -33
- data/spec/unit/mutant/expression_spec.rb +5 -7
- data/spec/unit/mutant/integration_spec.rb +100 -9
- data/spec/unit/mutant/isolation/fork_spec.rb +125 -67
- data/spec/unit/mutant/isolation/result_spec.rb +33 -1
- data/spec/unit/mutant/license_spec.rb +257 -0
- data/spec/unit/mutant/loader_spec.rb +50 -11
- data/spec/unit/mutant/matcher/compiler_spec.rb +0 -78
- data/spec/unit/mutant/matcher/method/instance_spec.rb +55 -11
- data/spec/unit/mutant/matcher/method/singleton_spec.rb +12 -2
- data/spec/unit/mutant/matcher_spec.rb +102 -0
- data/spec/unit/mutant/maybe_spec.rb +60 -0
- data/spec/unit/mutant/meta/example/dsl_spec.rb +1 -17
- data/spec/unit/mutant/mutation_spec.rb +13 -6
- data/spec/unit/mutant/parallel/driver_spec.rb +112 -14
- data/spec/unit/mutant/parallel/source/array_spec.rb +25 -17
- data/spec/unit/mutant/parallel/worker_spec.rb +182 -44
- data/spec/unit/mutant/parallel_spec.rb +105 -8
- data/spec/unit/mutant/range_spec.rb +141 -0
- data/spec/unit/mutant/reporter/cli/printer/config_spec.rb +7 -21
- data/spec/unit/mutant/reporter/cli/printer/env_progress_spec.rb +15 -6
- data/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb +10 -2
- data/spec/unit/mutant/reporter/cli/printer/isolation_result_spec.rb +12 -4
- data/spec/unit/mutant/reporter/cli/printer/mutation_result_spec.rb +31 -2
- data/spec/unit/mutant/reporter/cli/printer/status_progressive_spec.rb +4 -4
- data/spec/unit/mutant/reporter/cli/printer/subject_result_spec.rb +5 -0
- data/spec/unit/mutant/reporter/cli_spec.rb +46 -123
- data/spec/unit/mutant/repository/diff/ranges_spec.rb +180 -0
- data/spec/unit/mutant/repository/diff_spec.rb +84 -71
- data/spec/unit/mutant/require_highjack_spec.rb +1 -1
- data/spec/unit/mutant/result/env_spec.rb +39 -9
- data/spec/unit/mutant/result/test_spec.rb +14 -0
- data/spec/unit/mutant/runner_spec.rb +88 -41
- data/spec/unit/mutant/selector/expression_spec.rb +11 -10
- data/spec/unit/mutant/selector/null_spec.rb +17 -0
- data/spec/unit/mutant/subject/method/instance_spec.rb +44 -5
- data/spec/unit/mutant/subject/method/singleton_spec.rb +9 -2
- data/spec/unit/mutant/subject_spec.rb +9 -1
- data/spec/unit/mutant/transform/array_spec.rb +92 -0
- data/spec/unit/mutant/transform/bool_spec.rb +63 -0
- data/spec/unit/mutant/transform/error_spec.rb +132 -0
- data/spec/unit/mutant/transform/exception_spec.rb +44 -0
- data/spec/unit/mutant/transform/hash_spec.rb +236 -0
- data/spec/unit/mutant/transform/index_spec.rb +92 -0
- data/spec/unit/mutant/transform/named_spec.rb +49 -0
- data/spec/unit/mutant/transform/primitive_spec.rb +56 -0
- data/spec/unit/mutant/transform/sequence_spec.rb +98 -0
- data/spec/unit/mutant/variable_spec.rb +618 -0
- data/spec/unit/mutant/warnings_spec.rb +89 -0
- data/spec/unit/mutant/world_spec.rb +63 -0
- data/test_app/Gemfile.minitest +0 -2
- metadata +79 -113
- data/.gitattributes +0 -1
- data/.ruby-gemset +0 -1
- data/config/triage.yml +0 -2
- data/lib/mutant/actor.rb +0 -57
- data/lib/mutant/actor/env.rb +0 -31
- data/lib/mutant/actor/mailbox.rb +0 -34
- data/lib/mutant/actor/receiver.rb +0 -42
- data/lib/mutant/actor/sender.rb +0 -26
- data/lib/mutant/ast/meta/restarg.rb +0 -19
- data/lib/mutant/ast/regexp.rb +0 -42
- data/lib/mutant/ast/regexp/transformer.rb +0 -187
- data/lib/mutant/ast/regexp/transformer/direct.rb +0 -123
- data/lib/mutant/ast/regexp/transformer/named_group.rb +0 -59
- data/lib/mutant/ast/regexp/transformer/options_group.rb +0 -83
- data/lib/mutant/ast/regexp/transformer/quantifier.rb +0 -114
- data/lib/mutant/ast/regexp/transformer/recursive.rb +0 -58
- data/lib/mutant/ast/regexp/transformer/root.rb +0 -31
- data/lib/mutant/ast/regexp/transformer/text.rb +0 -60
- data/lib/mutant/env/bootstrap.rb +0 -160
- data/lib/mutant/matcher/compiler.rb +0 -60
- data/lib/mutant/mutator/node/regexp.rb +0 -35
- data/lib/mutant/mutator/node/regexp/alternation_meta.rb +0 -23
- data/lib/mutant/mutator/node/regexp/capture_group.rb +0 -28
- data/lib/mutant/mutator/node/regexp/character_type.rb +0 -32
- data/lib/mutant/mutator/node/regexp/end_of_line_anchor.rb +0 -23
- data/lib/mutant/mutator/node/regexp/end_of_string_or_before_end_of_line_anchor.rb +0 -23
- data/lib/mutant/mutator/node/regexp/greedy_zero_or_more.rb +0 -27
- data/lib/mutant/parallel/master.rb +0 -181
- data/lib/mutant/reporter/cli/printer/status.rb +0 -53
- data/lib/mutant/reporter/cli/tput.rb +0 -46
- data/lib/mutant/warning_filter.rb +0 -61
- data/meta/regexp/character_types.rb +0 -23
- data/meta/regexp/regexp_alternation_meta.rb +0 -13
- data/meta/regexp/regexp_bol_anchor.rb +0 -10
- data/meta/regexp/regexp_bos_anchor.rb +0 -18
- data/meta/regexp/regexp_capture_group.rb +0 -19
- data/meta/regexp/regexp_eol_anchor.rb +0 -10
- data/meta/regexp/regexp_eos_anchor.rb +0 -8
- data/meta/regexp/regexp_eos_ob_eol_anchor.rb +0 -10
- data/meta/regexp/regexp_greedy_zero_or_more.rb +0 -12
- data/meta/regexp/regexp_root_expression.rb +0 -10
- data/meta/restarg.rb +0 -10
- data/spec/support/fake_actor.rb +0 -111
- data/spec/support/warning.rb +0 -66
- data/spec/unit/mutant/actor/binding_spec.rb +0 -34
- data/spec/unit/mutant/actor/env_spec.rb +0 -31
- data/spec/unit/mutant/actor/mailbox_spec.rb +0 -28
- data/spec/unit/mutant/actor/message_spec.rb +0 -25
- data/spec/unit/mutant/actor/receiver_spec.rb +0 -58
- data/spec/unit/mutant/actor/sender_spec.rb +0 -24
- data/spec/unit/mutant/ast/regexp/parse_spec.rb +0 -19
- data/spec/unit/mutant/ast/regexp/transformer/lookup_table/table_spec.rb +0 -21
- data/spec/unit/mutant/ast/regexp/transformer/lookup_table_spec.rb +0 -35
- data/spec/unit/mutant/ast/regexp/transformer_spec.rb +0 -21
- data/spec/unit/mutant/ast/regexp_spec.rb +0 -704
- data/spec/unit/mutant/env/bootstrap_spec.rb +0 -188
- data/spec/unit/mutant/matcher/compiler/subject_prefix_spec.rb +0 -26
- data/spec/unit/mutant/parallel/master_spec.rb +0 -338
- data/spec/unit/mutant/reporter/cli/printer/status_spec.rb +0 -121
- data/spec/unit/mutant/reporter/cli/tput_spec.rb +0 -50
- data/spec/unit/mutant/warning_filter_spec.rb +0 -106
- data/spec/unit/mutant_spec.rb +0 -17
- data/test_app/Gemfile.rspec3.7 +0 -7
data/spec/spec_helper.rb
CHANGED
@@ -16,10 +16,6 @@ if ENV['COVERAGE'] == 'true'
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
# Require warning support first in order to catch any warnings emitted during boot
|
20
|
-
require_relative './support/warning'
|
21
|
-
$stderr = MutantSpec::Warning::EXTRACTOR
|
22
|
-
|
23
19
|
require 'tempfile'
|
24
20
|
require 'concord'
|
25
21
|
require 'anima'
|
@@ -34,8 +30,12 @@ $LOAD_PATH << File.join(TestApp.root, 'lib')
|
|
34
30
|
require 'test_app'
|
35
31
|
|
36
32
|
module Fixtures
|
37
|
-
TEST_CONFIG = Mutant::Config::DEFAULT
|
38
|
-
|
33
|
+
TEST_CONFIG = Mutant::Config::DEFAULT
|
34
|
+
.with(reporter: Mutant::Reporter::Null.new)
|
35
|
+
|
36
|
+
TEST_ENV = Mutant::Bootstrap
|
37
|
+
.apply(Mutant::WORLD, TEST_CONFIG).from_right
|
38
|
+
|
39
39
|
end # Fixtures
|
40
40
|
|
41
41
|
module ParserHelper
|
@@ -48,24 +48,28 @@ module ParserHelper
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def parse_expression(string)
|
51
|
-
Mutant::Config::DEFAULT.expression_parser.(string)
|
51
|
+
Mutant::Config::DEFAULT.expression_parser.apply(string).from_right
|
52
52
|
end
|
53
53
|
end # ParserHelper
|
54
54
|
|
55
|
-
module
|
56
|
-
def
|
57
|
-
|
55
|
+
module XSpecHelper
|
56
|
+
def verify_events
|
57
|
+
expectations = raw_expectations
|
58
|
+
.map(&XSpec::MessageExpectation.method(:parse))
|
59
|
+
|
60
|
+
XSpec::ExpectationVerifier.verify(self, expectations) do
|
61
|
+
yield
|
62
|
+
end
|
58
63
|
end
|
59
|
-
|
64
|
+
|
65
|
+
def undefined
|
66
|
+
double('undefined')
|
67
|
+
end
|
68
|
+
end # XSpecHelper
|
60
69
|
|
61
70
|
RSpec.configure do |config|
|
62
71
|
config.extend(SharedContext)
|
63
|
-
config.include(MessageHelper)
|
64
72
|
config.include(ParserHelper)
|
65
73
|
config.include(Mutant::AST::Sexp)
|
66
|
-
|
67
|
-
config.after(:suite) do
|
68
|
-
$stderr = STDERR
|
69
|
-
MutantSpec::Warning.assert_no_warnings
|
70
|
-
end
|
74
|
+
config.include(XSpecHelper)
|
71
75
|
end
|
data/spec/support/corpus.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'anima'
|
4
|
-
require 'morpher'
|
5
4
|
require 'mutant'
|
6
5
|
require 'parallel'
|
7
6
|
|
@@ -30,11 +29,11 @@ module MutantSpec
|
|
30
29
|
MUTATION_GENERATION_MESSAGE = 'Total Mutations/Time/Parse-Errors: %s/%0.2fs - %0.2f/s'
|
31
30
|
START_MESSAGE = 'Starting - %s'
|
32
31
|
FINISH_MESSAGE = 'Mutations - %4i - %s'
|
32
|
+
RUBY_GLOB_PATTERN = '**/*.rb'
|
33
33
|
|
34
34
|
DEFAULT_MUTATION_COUNT = 0
|
35
35
|
|
36
36
|
include Adamantium, Anima.new(
|
37
|
-
:expected_errors,
|
38
37
|
:mutation_coverage,
|
39
38
|
:mutation_generation,
|
40
39
|
:integration,
|
@@ -42,7 +41,6 @@ module MutantSpec
|
|
42
41
|
:namespace,
|
43
42
|
:repo_uri,
|
44
43
|
:repo_ref,
|
45
|
-
:ruby_glob_pattern,
|
46
44
|
:exclude
|
47
45
|
)
|
48
46
|
|
@@ -64,12 +62,23 @@ module MutantSpec
|
|
64
62
|
--include lib
|
65
63
|
--require #{name}
|
66
64
|
#{namespace}*
|
67
|
-
]
|
65
|
+
] + concurrency_limits
|
68
66
|
)
|
69
67
|
end
|
70
68
|
end
|
71
69
|
end
|
72
70
|
|
71
|
+
# The concurrency limits, if any
|
72
|
+
#
|
73
|
+
# @return [Array<String>]
|
74
|
+
def concurrency_limits
|
75
|
+
if ENV.key?('MUTANT_JOBS')
|
76
|
+
%W[--jobs #{ENV.fetch('MUTANT_JOBS')}]
|
77
|
+
else
|
78
|
+
[]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
73
82
|
# Verify mutation generation
|
74
83
|
#
|
75
84
|
# @return [self]
|
@@ -130,8 +139,6 @@ module MutantSpec
|
|
130
139
|
#
|
131
140
|
# @return [Integer] mutations generated
|
132
141
|
def check_generation(path)
|
133
|
-
relative_path = path.relative_path_from(repo_path)
|
134
|
-
|
135
142
|
node = Parser::CurrentRuby.parse(path.read)
|
136
143
|
fail "Cannot parse: #{path}" unless node
|
137
144
|
|
@@ -141,13 +148,7 @@ module MutantSpec
|
|
141
148
|
check_generation_invariants(node, mutation)
|
142
149
|
end
|
143
150
|
|
144
|
-
expected_errors.assert_success(relative_path)
|
145
|
-
|
146
151
|
mutations.length
|
147
|
-
rescue Exception => exception # rubocop:disable Lint/RescueException
|
148
|
-
expected_errors.assert_error(relative_path, exception)
|
149
|
-
|
150
|
-
DEFAULT_MUTATION_COUNT
|
151
152
|
end
|
152
153
|
|
153
154
|
# Check generation invariants
|
@@ -196,7 +197,7 @@ module MutantSpec
|
|
196
197
|
# @return [Array<Pathname>]
|
197
198
|
def effective_ruby_paths
|
198
199
|
Pathname
|
199
|
-
.glob(repo_path.join(
|
200
|
+
.glob(repo_path.join(RUBY_GLOB_PATTERN))
|
200
201
|
.sort_by(&:size)
|
201
202
|
.reverse
|
202
203
|
.reject { |path| exclude.include?(path.relative_path_from(repo_path).to_s) }
|
@@ -269,89 +270,49 @@ module MutantSpec
|
|
269
270
|
end
|
270
271
|
end
|
271
272
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
raise exception unless map.fetch(original_error.inspect, []).include?(path)
|
299
|
-
end
|
300
|
-
|
301
|
-
# Assert that we expect to not encounter an error for the specified path
|
302
|
-
#
|
303
|
-
# @param path [Pathname]
|
304
|
-
#
|
305
|
-
# @raise [UnnecessaryExpectation] if we are expecting an exception for this path
|
306
|
-
#
|
307
|
-
# @return [undefined]
|
308
|
-
def assert_success(path)
|
309
|
-
map.each do |error, paths|
|
310
|
-
fail UnnecessaryExpectation.new(error, path) if paths.include?(path)
|
311
|
-
end
|
312
|
-
end
|
313
|
-
|
314
|
-
# Return representation as hash
|
315
|
-
#
|
316
|
-
# @note this method is necessary for morpher loader to be invertible
|
317
|
-
#
|
318
|
-
# @return [Hash{Pathname => String}]
|
319
|
-
def to_h
|
320
|
-
map
|
321
|
-
end
|
322
|
-
end # ErrorWhitelist
|
323
|
-
|
324
|
-
LOADER = Morpher.build do
|
325
|
-
s(:block,
|
326
|
-
s(:guard, s(:primitive, Array)),
|
327
|
-
s(:map,
|
328
|
-
s(:block,
|
329
|
-
s(:guard, s(:primitive, Hash)),
|
330
|
-
s(:hash_transform,
|
331
|
-
s(:key_symbolize, :repo_uri, s(:guard, s(:primitive, String))),
|
332
|
-
s(:key_symbolize, :repo_ref, s(:guard, s(:primitive, String))),
|
333
|
-
s(:key_symbolize, :ruby_glob_pattern, s(:guard, s(:primitive, String))),
|
334
|
-
s(:key_symbolize, :name, s(:guard, s(:primitive, String))),
|
335
|
-
s(:key_symbolize, :namespace, s(:guard, s(:primitive, String))),
|
336
|
-
s(:key_symbolize, :integration, s(:guard, s(:primitive, String))),
|
337
|
-
s(:key_symbolize, :mutation_coverage,
|
338
|
-
s(:guard, s(:or, s(:primitive, TrueClass), s(:primitive, FalseClass)))),
|
339
|
-
s(:key_symbolize, :mutation_generation,
|
340
|
-
s(:guard, s(:or, s(:primitive, TrueClass), s(:primitive, FalseClass)))),
|
341
|
-
s(:key_symbolize, :expected_errors,
|
342
|
-
s(:block,
|
343
|
-
s(:guard, s(:primitive, Hash)),
|
344
|
-
s(:custom,
|
345
|
-
[
|
346
|
-
->(hash) { hash.map { |key, values| [key, values.map(&Pathname.method(:new))] }.to_h },
|
347
|
-
->(hash) { hash.map { |key, values| [key, values.map(&:to_s)] }.to_h }
|
348
|
-
]),
|
349
|
-
s(:load_attribute_hash, s(:param, ErrorWhitelist)))),
|
350
|
-
s(:key_symbolize, :exclude, s(:map, s(:guard, s(:primitive, String))))),
|
351
|
-
s(:anima_load, Project))))
|
352
|
-
end
|
273
|
+
Transform = Mutant::Transform
|
274
|
+
|
275
|
+
boolean = Transform::Boolean.new
|
276
|
+
string = Transform::Primitive.new(String)
|
277
|
+
string_array = Transform::Array.new(string)
|
278
|
+
|
279
|
+
integration = Transform::Sequence.new(
|
280
|
+
[
|
281
|
+
Transform::Hash.new(
|
282
|
+
optional: [],
|
283
|
+
required: [
|
284
|
+
Transform::Hash::Key.new('exclude', string_array),
|
285
|
+
Transform::Hash::Key.new('integration', string),
|
286
|
+
Transform::Hash::Key.new('mutation_coverage', boolean),
|
287
|
+
Transform::Hash::Key.new('mutation_generation', boolean),
|
288
|
+
Transform::Hash::Key.new('name', string),
|
289
|
+
Transform::Hash::Key.new('namespace', string),
|
290
|
+
Transform::Hash::Key.new('repo_ref', string),
|
291
|
+
Transform::Hash::Key.new('repo_uri', string)
|
292
|
+
]
|
293
|
+
),
|
294
|
+
Transform::Hash::Symbolize.new,
|
295
|
+
Transform::Exception.new(RuntimeError, Project.method(:new))
|
296
|
+
]
|
297
|
+
)
|
353
298
|
|
354
|
-
|
299
|
+
transform =
|
300
|
+
Transform::Sequence.new(
|
301
|
+
[
|
302
|
+
Transform::Exception.new(SystemCallError, :read.to_proc),
|
303
|
+
Transform::Exception.new(YAML::SyntaxError, YAML.method(:safe_load)),
|
304
|
+
Transform::Array.new(integration)
|
305
|
+
]
|
306
|
+
)
|
307
|
+
|
308
|
+
path = ROOT.join('spec', 'integrations.yml')
|
309
|
+
|
310
|
+
ALL = Transform::Named
|
311
|
+
.new(path, transform)
|
312
|
+
.apply(path)
|
313
|
+
.lmap(&:compact_message)
|
314
|
+
.lmap(&method(:fail))
|
315
|
+
.from_right
|
355
316
|
end # Project
|
356
317
|
end # Corpus
|
357
318
|
end # MutantSpec
|
@@ -17,14 +17,6 @@ module SharedContext
|
|
17
17
|
prepend(new_definition)
|
18
18
|
end
|
19
19
|
|
20
|
-
def messages(&block)
|
21
|
-
let(:message_sequence) do
|
22
|
-
FakeActor::MessageSequence.new.tap do |sequence|
|
23
|
-
sequence.instance_eval(&block)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
20
|
def it_reports(expected_content)
|
29
21
|
it 'writes expected report to output' do
|
30
22
|
described_class.call(output, reportable)
|
@@ -36,8 +28,6 @@ module SharedContext
|
|
36
28
|
# rubocop:disable MethodLength
|
37
29
|
# rubocop:disable AbcSize
|
38
30
|
def setup_shared_context
|
39
|
-
let(:job_a) { Mutant::Parallel::Job.new(index: 0, payload: mutation_a) }
|
40
|
-
let(:job_b) { Mutant::Parallel::Job.new(index: 1, payload: mutation_b) }
|
41
31
|
let(:mutation_a) { Mutant::Mutation::Evil.new(subject_a, mutation_a_node) }
|
42
32
|
let(:mutation_a_node) { s(:false) }
|
43
33
|
let(:mutation_b) { Mutant::Mutation::Evil.new(subject_a, mutation_b_node) }
|
@@ -46,14 +36,47 @@ module SharedContext
|
|
46
36
|
let(:output) { StringIO.new }
|
47
37
|
let(:subject_a_node) { s(:true) }
|
48
38
|
let(:test_a) { instance_double(Mutant::Test, identification: 'test-a') }
|
39
|
+
let(:subjects) { [subject_a] }
|
40
|
+
|
41
|
+
let(:job_a) do
|
42
|
+
Mutant::Parallel::Source::Job.new(
|
43
|
+
index: 0,
|
44
|
+
payload: mutation_a
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
let(:job_b) do
|
49
|
+
Mutant::Parallel::Source::Job.new(
|
50
|
+
index: 1,
|
51
|
+
payload: mutation_b
|
52
|
+
)
|
53
|
+
end
|
49
54
|
|
50
55
|
let(:env) do
|
51
56
|
instance_double(
|
52
57
|
Mutant::Env,
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
58
|
+
amount_mutations: mutations.length,
|
59
|
+
amount_selected_tests: selections.values.flatten.to_set.length,
|
60
|
+
amount_subjects: subjects.length,
|
61
|
+
amount_total_tests: integration.all_tests.length,
|
62
|
+
config: config,
|
63
|
+
integration: integration,
|
64
|
+
mutations: mutations,
|
65
|
+
selected_tests: [test_a].to_set,
|
66
|
+
selections: selections,
|
67
|
+
subjects: subjects,
|
68
|
+
test_subject_ratio: Rational(1)
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
let(:selections) do
|
73
|
+
{ subject_a => [test_a] }
|
74
|
+
end
|
75
|
+
|
76
|
+
let(:integration) do
|
77
|
+
instance_double(
|
78
|
+
Mutant::Integration,
|
79
|
+
all_tests: [test_a]
|
57
80
|
)
|
58
81
|
end
|
59
82
|
|
data/spec/support/xspec.rb
CHANGED
@@ -5,7 +5,7 @@ module XSpec
|
|
5
5
|
include Concord.new(:event_list)
|
6
6
|
|
7
7
|
TERMINATE_EVENTS = IceNine.deep_freeze(%i[return exception].to_set)
|
8
|
-
VALID_EVENTS = IceNine.deep_freeze(%i[return exception yields].to_set)
|
8
|
+
VALID_EVENTS = IceNine.deep_freeze(%i[return execute exception yields].to_set)
|
9
9
|
|
10
10
|
private_constant(*constants(false))
|
11
11
|
|
@@ -32,6 +32,10 @@ module XSpec
|
|
32
32
|
value
|
33
33
|
end
|
34
34
|
|
35
|
+
def execute(_event, block)
|
36
|
+
block.call
|
37
|
+
end
|
38
|
+
|
35
39
|
def exception(_event, exception)
|
36
40
|
fail exception
|
37
41
|
end
|
@@ -140,12 +144,12 @@ module XSpec
|
|
140
144
|
include Concord.new(:expectations)
|
141
145
|
|
142
146
|
def call(observation)
|
143
|
-
expectation = expectations.shift or fail "No expected message but observed #{observation}"
|
147
|
+
expectation = expectations.shift or fail "No expected message but observed #{observation.inspect}"
|
144
148
|
expectation.call(observation)
|
145
149
|
end
|
146
150
|
|
147
151
|
def assert_done
|
148
|
-
expectations.empty? or fail "unconsumed expectations:\n#{expectations.map(&:inspect).join}"
|
152
|
+
expectations.empty? or fail "unconsumed expectations:\n#{expectations.map(&:inspect).join("\n")}"
|
149
153
|
end
|
150
154
|
|
151
155
|
# rubocop:disable MethodLength
|
@@ -0,0 +1,216 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Mutant::Bootstrap do
|
4
|
+
let(:integration) { instance_double(Mutant::Integration) }
|
5
|
+
let(:integration_name) { instance_double(String) }
|
6
|
+
let(:integration_result) { Mutant::Either::Right.new(integration) }
|
7
|
+
let(:kernel) { instance_double(Object, 'kernel') }
|
8
|
+
let(:load_path) { %w[original] }
|
9
|
+
let(:matcher_config) { Mutant::Matcher::Config::DEFAULT }
|
10
|
+
let(:object_space) { class_double(ObjectSpace) }
|
11
|
+
let(:object_space_modules) { [] }
|
12
|
+
let(:warnings) { instance_double(Mutant::Warnings) }
|
13
|
+
|
14
|
+
let(:world) do
|
15
|
+
instance_double(
|
16
|
+
Mutant::World,
|
17
|
+
kernel: kernel,
|
18
|
+
load_path: load_path,
|
19
|
+
object_space: object_space,
|
20
|
+
pathname: Pathname,
|
21
|
+
warnings: warnings
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:config) do
|
26
|
+
Mutant::Config::DEFAULT.with(
|
27
|
+
includes: [],
|
28
|
+
integration: integration,
|
29
|
+
jobs: 1,
|
30
|
+
matcher: matcher_config,
|
31
|
+
reporter: instance_double(Mutant::Reporter),
|
32
|
+
requires: []
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
let(:expected_env) do
|
37
|
+
env_with_scopes.with(
|
38
|
+
integration: integration,
|
39
|
+
selector: Mutant::Selector::Expression.new(integration)
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
let(:env_empty) do
|
44
|
+
Mutant::Env.empty(world, config)
|
45
|
+
end
|
46
|
+
|
47
|
+
let(:env_with_scopes) { env_empty }
|
48
|
+
|
49
|
+
shared_examples 'expected warning' do
|
50
|
+
let(:warns) { [] }
|
51
|
+
|
52
|
+
before do
|
53
|
+
allow(config.reporter).to receive(:warn, &warns.method(:<<))
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'warns with expected warning' do
|
57
|
+
expect { apply }.to change(warns, :to_a).from([]).to([expected_warning])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
shared_examples_for 'bootstrap call' do
|
62
|
+
it 'returns expected env' do
|
63
|
+
expect(apply).to eql(Mutant::Either::Right.new(expected_env))
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'performs IO in expected sequence' do
|
67
|
+
apply
|
68
|
+
|
69
|
+
expect(object_space)
|
70
|
+
.to have_received(:each_object)
|
71
|
+
.ordered
|
72
|
+
|
73
|
+
expect(Mutant::Integration)
|
74
|
+
.to have_received(:setup)
|
75
|
+
.with(env_with_scopes)
|
76
|
+
.ordered
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
before do
|
81
|
+
allow(Mutant::Integration).to receive_messages(setup: integration_result)
|
82
|
+
|
83
|
+
allow(object_space).to receive(:each_object) do |argument|
|
84
|
+
expect(argument).to be(Module)
|
85
|
+
object_space_modules.each
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe '.apply' do
|
90
|
+
def apply
|
91
|
+
described_class.apply(world, config)
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'when Module#name calls result in exceptions' do
|
95
|
+
let(:object_space_modules) { [invalid_class] }
|
96
|
+
|
97
|
+
let(:expected_warning) do
|
98
|
+
"Object#name from: #{invalid_class} raised an error: " \
|
99
|
+
"RuntimeError. #{Mutant::Env::SEMANTICS_MESSAGE}"
|
100
|
+
end
|
101
|
+
|
102
|
+
# Not really a class, but does not leak a "wrong" class
|
103
|
+
# into later specs.
|
104
|
+
let(:invalid_class) do
|
105
|
+
Object.new.tap do |object|
|
106
|
+
def object.name
|
107
|
+
fail
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
include_examples 'expected warning'
|
113
|
+
include_examples 'bootstrap call'
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'when Module#name calls return nil' do
|
117
|
+
let(:anonymous_class) { Class.new }
|
118
|
+
let(:object_space_modules) { [anonymous_class] }
|
119
|
+
|
120
|
+
include_examples 'bootstrap call'
|
121
|
+
end
|
122
|
+
|
123
|
+
context 'when requires are configured' do
|
124
|
+
let(:config) { super().with(requires: %w[foo bar]) }
|
125
|
+
let(:requires) { [] }
|
126
|
+
|
127
|
+
before do
|
128
|
+
allow(kernel).to receive(:require, &requires.method(:<<))
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'executes requires' do
|
132
|
+
expect { apply }.to change(requires, :to_a).from([]).to(%w[foo bar])
|
133
|
+
end
|
134
|
+
|
135
|
+
include_examples 'bootstrap call'
|
136
|
+
end
|
137
|
+
|
138
|
+
context 'when includes are configured' do
|
139
|
+
let(:config) { super().with(includes: %w[foo bar]) }
|
140
|
+
|
141
|
+
it 'appends to load path' do
|
142
|
+
expect { apply }
|
143
|
+
.to change(load_path, :to_a)
|
144
|
+
.from(load_path.dup)
|
145
|
+
.to(%w[original foo bar])
|
146
|
+
end
|
147
|
+
|
148
|
+
include_examples 'bootstrap call'
|
149
|
+
end
|
150
|
+
|
151
|
+
context 'when Module#name does not return a String or nil' do
|
152
|
+
let(:object_space_modules) { [invalid_class] }
|
153
|
+
|
154
|
+
let(:invalid_class) do
|
155
|
+
# intentionally an object to not actually pollute object space
|
156
|
+
Object.new.tap do |object|
|
157
|
+
def object.name
|
158
|
+
Object
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
let(:expected_warning) do
|
164
|
+
"Object#name from: #{invalid_class} " \
|
165
|
+
"returned Object. #{Mutant::Env::SEMANTICS_MESSAGE}"
|
166
|
+
end
|
167
|
+
|
168
|
+
include_examples 'expected warning'
|
169
|
+
include_examples 'bootstrap call'
|
170
|
+
end
|
171
|
+
|
172
|
+
context 'when object name cannot be parsed as expression' do
|
173
|
+
let(:object_space_modules) { [invalid_class] }
|
174
|
+
|
175
|
+
let(:invalid_class) do
|
176
|
+
# intentionally an object to not actually pollute object space
|
177
|
+
Object.new.tap do |object|
|
178
|
+
def object.name
|
179
|
+
'invalid expression'
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
include_examples 'bootstrap call'
|
185
|
+
end
|
186
|
+
|
187
|
+
context 'when scope matches expression' do
|
188
|
+
let(:object_space_modules) { [TestApp::Literal, TestApp::Empty] }
|
189
|
+
let(:match_expressions) { object_space_modules.map(&:name).map(&method(:parse_expression)) }
|
190
|
+
|
191
|
+
let(:matcher_config) do
|
192
|
+
super().with(match_expressions: match_expressions)
|
193
|
+
end
|
194
|
+
|
195
|
+
let(:env_with_scopes) do
|
196
|
+
env_empty.with(
|
197
|
+
matchable_scopes: [
|
198
|
+
Mutant::Scope.new(TestApp::Empty, match_expressions.last),
|
199
|
+
Mutant::Scope.new(TestApp::Literal, match_expressions.first)
|
200
|
+
]
|
201
|
+
)
|
202
|
+
end
|
203
|
+
|
204
|
+
let(:expected_env) do
|
205
|
+
subjects = Mutant::Matcher::Scope.new(TestApp::Literal).call(env_empty)
|
206
|
+
|
207
|
+
super().with(
|
208
|
+
mutations: subjects.flat_map(&:mutations),
|
209
|
+
subjects: subjects
|
210
|
+
)
|
211
|
+
end
|
212
|
+
|
213
|
+
include_examples 'bootstrap call'
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|