mutant 0.9.8 → 0.9.9
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/.github/workflows/ci.yml +2 -2
- data/Changelog.md +6 -0
- data/README.md +63 -23
- data/config/reek.yml +1 -0
- data/lib/mutant.rb +4 -0
- data/lib/mutant/ast.rb +0 -9
- data/lib/mutant/ast/find_metaclass_containing.rb +48 -0
- data/lib/mutant/ast/meta/send.rb +0 -6
- data/lib/mutant/bootstrap.rb +0 -36
- data/lib/mutant/cli.rb +5 -49
- data/lib/mutant/color.rb +0 -3
- data/lib/mutant/config.rb +0 -8
- data/lib/mutant/context.rb +0 -3
- data/lib/mutant/diff.rb +0 -17
- data/lib/mutant/env.rb +0 -6
- data/lib/mutant/expression/method.rb +6 -6
- data/lib/mutant/expression/methods.rb +6 -6
- data/lib/mutant/expression/parser.rb +0 -6
- data/lib/mutant/integration.rb +0 -18
- data/lib/mutant/isolation/fork.rb +0 -22
- data/lib/mutant/license.rb +11 -0
- data/lib/mutant/matcher.rb +0 -14
- data/lib/mutant/matcher/config.rb +0 -11
- data/lib/mutant/matcher/method.rb +0 -31
- data/lib/mutant/matcher/method/instance.rb +0 -8
- data/lib/mutant/matcher/method/metaclass.rb +86 -0
- data/lib/mutant/matcher/method/singleton.rb +0 -25
- data/lib/mutant/matcher/methods.rb +17 -28
- data/lib/mutant/matcher/namespace.rb +0 -10
- data/lib/mutant/matcher/scope.rb +2 -4
- data/lib/mutant/meta/example/dsl.rb +0 -21
- data/lib/mutant/meta/example/verification.rb +0 -20
- data/lib/mutant/mutation.rb +0 -3
- data/lib/mutant/mutator.rb +1 -29
- data/lib/mutant/mutator/node.rb +1 -66
- data/lib/mutant/mutator/node/and_asgn.rb +0 -3
- data/lib/mutant/mutator/node/argument.rb +0 -15
- data/lib/mutant/mutator/node/arguments.rb +0 -20
- data/lib/mutant/mutator/node/begin.rb +0 -3
- data/lib/mutant/mutator/node/binary.rb +0 -23
- data/lib/mutant/mutator/node/block.rb +0 -15
- data/lib/mutant/mutator/node/break.rb +0 -3
- data/lib/mutant/mutator/node/case.rb +0 -9
- data/lib/mutant/mutator/node/class.rb +0 -3
- data/lib/mutant/mutator/node/conditional_loop.rb +0 -3
- data/lib/mutant/mutator/node/const.rb +0 -3
- data/lib/mutant/mutator/node/define.rb +0 -11
- data/lib/mutant/mutator/node/defined.rb +0 -3
- data/lib/mutant/mutator/node/dstr.rb +0 -3
- data/lib/mutant/mutator/node/dsym.rb +0 -3
- data/lib/mutant/mutator/node/generic.rb +0 -3
- data/lib/mutant/mutator/node/if.rb +0 -12
- data/lib/mutant/mutator/node/index.rb +0 -27
- data/lib/mutant/mutator/node/kwbegin.rb +0 -3
- data/lib/mutant/mutator/node/literal.rb +0 -3
- data/lib/mutant/mutator/node/literal/array.rb +0 -6
- data/lib/mutant/mutator/node/literal/boolean.rb +0 -4
- data/lib/mutant/mutator/node/literal/float.rb +0 -9
- data/lib/mutant/mutator/node/literal/hash.rb +0 -9
- data/lib/mutant/mutator/node/literal/integer.rb +0 -9
- data/lib/mutant/mutator/node/literal/nil.rb +0 -3
- data/lib/mutant/mutator/node/literal/range.rb +0 -6
- data/lib/mutant/mutator/node/literal/regex.rb +0 -6
- data/lib/mutant/mutator/node/literal/string.rb +0 -3
- data/lib/mutant/mutator/node/literal/symbol.rb +0 -3
- data/lib/mutant/mutator/node/masgn.rb +0 -3
- data/lib/mutant/mutator/node/match_current_line.rb +0 -3
- data/lib/mutant/mutator/node/mlhs.rb +0 -3
- data/lib/mutant/mutator/node/named_value/access.rb +2 -14
- data/lib/mutant/mutator/node/named_value/constant_assignment.rb +0 -9
- data/lib/mutant/mutator/node/named_value/variable_assignment.rb +0 -6
- data/lib/mutant/mutator/node/next.rb +0 -3
- data/lib/mutant/mutator/node/noop.rb +0 -3
- data/lib/mutant/mutator/node/nthref.rb +0 -3
- data/lib/mutant/mutator/node/op_asgn.rb +0 -3
- data/lib/mutant/mutator/node/or_asgn.rb +0 -3
- data/lib/mutant/mutator/node/procarg_zero.rb +0 -3
- data/lib/mutant/mutator/node/regopt.rb +0 -6
- data/lib/mutant/mutator/node/resbody.rb +0 -6
- data/lib/mutant/mutator/node/rescue.rb +2 -19
- data/lib/mutant/mutator/node/return.rb +0 -3
- data/lib/mutant/mutator/node/sclass.rb +20 -0
- data/lib/mutant/mutator/node/send.rb +2 -61
- data/lib/mutant/mutator/node/send/attribute_assignment.rb +0 -9
- data/lib/mutant/mutator/node/send/binary.rb +0 -11
- data/lib/mutant/mutator/node/send/conditional.rb +0 -3
- data/lib/mutant/mutator/node/splat.rb +0 -3
- data/lib/mutant/mutator/node/super.rb +0 -3
- data/lib/mutant/mutator/node/when.rb +0 -19
- data/lib/mutant/mutator/node/yield.rb +0 -3
- data/lib/mutant/mutator/node/zsuper.rb +0 -3
- data/lib/mutant/mutator/util/array.rb +0 -6
- data/lib/mutant/mutator/util/symbol.rb +0 -3
- data/lib/mutant/parallel.rb +0 -13
- data/lib/mutant/parallel/driver.rb +0 -10
- data/lib/mutant/parallel/worker.rb +0 -22
- data/lib/mutant/reporter/cli.rb +0 -5
- data/lib/mutant/reporter/cli/format.rb +0 -9
- data/lib/mutant/reporter/cli/printer.rb +0 -40
- data/lib/mutant/reporter/cli/printer/env_progress.rb +0 -15
- data/lib/mutant/reporter/cli/printer/isolation_result.rb +0 -18
- data/lib/mutant/reporter/cli/printer/mutation_progress_result.rb +0 -5
- data/lib/mutant/reporter/cli/printer/mutation_result.rb +0 -21
- data/lib/mutant/reporter/cli/printer/status_progressive.rb +0 -8
- data/lib/mutant/reporter/cli/printer/subject_progress.rb +0 -9
- data/lib/mutant/repository/diff.rb +1 -13
- data/lib/mutant/repository/diff/ranges.rb +0 -11
- data/lib/mutant/result.rb +0 -3
- data/lib/mutant/runner.rb +0 -18
- data/lib/mutant/runner/sink.rb +0 -5
- data/lib/mutant/subject.rb +0 -8
- data/lib/mutant/subject/method.rb +0 -3
- data/lib/mutant/subject/method/instance.rb +0 -5
- data/lib/mutant/subject/method/metaclass.rb +30 -0
- data/lib/mutant/transform.rb +0 -92
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/warnings.rb +0 -6
- data/lib/mutant/zombifier.rb +2 -34
- data/meta/and.rb +0 -2
- data/meta/array.rb +0 -3
- data/meta/begin.rb +0 -3
- data/meta/block.rb +0 -3
- data/meta/break.rb +0 -1
- data/meta/case.rb +0 -6
- data/meta/casgn.rb +0 -3
- data/meta/cvasgn.rb +0 -1
- data/meta/def.rb +0 -7
- data/meta/ensure.rb +0 -1
- data/meta/false.rb +0 -1
- data/meta/gvasgn.rb +0 -1
- data/meta/hash.rb +0 -4
- data/meta/if.rb +0 -5
- data/meta/ivasgn.rb +0 -1
- data/meta/kwbegin.rb +0 -1
- data/meta/lvasgn.rb +0 -1
- data/meta/match_current_line.rb +0 -1
- data/meta/next.rb +0 -1
- data/meta/or.rb +0 -2
- data/meta/regexp.rb +0 -1
- data/meta/rescue.rb +0 -6
- data/meta/sclass.rb +12 -0
- data/meta/send.rb +0 -4
- data/meta/true.rb +0 -1
- data/meta/until.rb +0 -1
- data/meta/while.rb +0 -2
- data/meta/yield.rb +0 -1
- data/mutant.sh +12 -0
- data/spec/unit/mutant/ast/find_metaclass_containing_spec.rb +64 -0
- data/spec/unit/mutant/expression/methods_spec.rb +7 -2
- data/spec/unit/mutant/license_spec.rb +15 -3
- data/spec/unit/mutant/matcher/method/metaclass_spec.rb +108 -0
- data/spec/unit/mutant/matcher/methods/metaclass_spec.rb +62 -0
- data/spec/unit/mutant/matcher/namespace_spec.rb +3 -1
- data/spec/unit/mutant/matcher/scope_spec.rb +11 -1
- data/spec/unit/mutant/meta/example_spec.rb +3 -3
- data/spec/unit/mutant/mutator/node_spec.rb +1 -6
- data/spec/unit/mutant/subject/method/metaclass_spec.rb +63 -0
- data/test_app/lib/test_app.rb +1 -0
- data/test_app/lib/test_app/metaclasses.rb +108 -0
- metadata +17 -2
data/meta/true.rb
CHANGED
data/meta/until.rb
CHANGED
|
@@ -8,7 +8,6 @@ Mutant::Meta::Example.add :until do
|
|
|
8
8
|
mutation 'until true; foo; end'
|
|
9
9
|
mutation 'until true; end'
|
|
10
10
|
mutation 'until false; foo; bar; end'
|
|
11
|
-
mutation 'until nil; foo; bar; end'
|
|
12
11
|
mutation 'until true; foo; nil; end'
|
|
13
12
|
mutation 'until true; foo; self; end'
|
|
14
13
|
mutation 'until true; nil; bar; end'
|
data/meta/while.rb
CHANGED
|
@@ -10,7 +10,6 @@ Mutant::Meta::Example.add :while do
|
|
|
10
10
|
mutation 'while true; foo; end'
|
|
11
11
|
mutation 'while true; end'
|
|
12
12
|
mutation 'while false; foo; bar; end'
|
|
13
|
-
mutation 'while nil; foo; bar; end'
|
|
14
13
|
mutation 'while true; foo; nil; end'
|
|
15
14
|
mutation 'while true; nil; bar; end'
|
|
16
15
|
mutation 'while true; raise; end'
|
|
@@ -22,5 +21,4 @@ Mutant::Meta::Example.add :while do
|
|
|
22
21
|
singleton_mutations
|
|
23
22
|
mutation 'while true; raise; end'
|
|
24
23
|
mutation 'while false; end'
|
|
25
|
-
mutation 'while nil; end'
|
|
26
24
|
end
|
data/meta/yield.rb
CHANGED
data/mutant.sh
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#/usr/bin/bash -ex
|
|
2
|
+
|
|
3
|
+
bundle exec mutant \
|
|
4
|
+
--since HEAD~1 \
|
|
5
|
+
--zombie \
|
|
6
|
+
--ignore-subject Mutant::CLI#add_debug_options \
|
|
7
|
+
--ignore-subject Mutant::Mutator::Node::Argument#skip? \
|
|
8
|
+
--ignore-subject Mutant::Mutator::Node::ProcargZero#dispatch \
|
|
9
|
+
--ignore-subject Mutant::Mutator::Node::When#mutate_conditions \
|
|
10
|
+
--ignore-subject Mutant::Zombifier#call \
|
|
11
|
+
-- \
|
|
12
|
+
'Mutant*'
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Mutant::AST::FindMetaclassContaining do
|
|
4
|
+
describe '#call' do
|
|
5
|
+
subject { described_class.call(ast, node) }
|
|
6
|
+
|
|
7
|
+
let(:metaclass_node) { s(:sclass, s(:self), method_node) }
|
|
8
|
+
let(:method_node) { s(:def, 'test', s(:nil)) }
|
|
9
|
+
|
|
10
|
+
let(:ast) { metaclass_node }
|
|
11
|
+
let(:node) { method_node }
|
|
12
|
+
|
|
13
|
+
context 'when called without ast' do
|
|
14
|
+
let(:ast) { nil }
|
|
15
|
+
|
|
16
|
+
it { expect { subject }.to raise_error }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
context 'when called without node' do
|
|
20
|
+
let(:node) { nil }
|
|
21
|
+
|
|
22
|
+
it { is_expected.to be nil }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context 'when called with ast which contains a duplicate of the node' do
|
|
26
|
+
let(:node) { s(:def, 'test', s(:nil)) }
|
|
27
|
+
|
|
28
|
+
shared_examples_for 'unfooled by the duplicate node' do
|
|
29
|
+
it { is_expected.to be nil }
|
|
30
|
+
|
|
31
|
+
# if we changed method_node or node without altering the other to match,
|
|
32
|
+
# the above example would provide a false positive. by ensuring node and
|
|
33
|
+
# method_node are eq but not equal, we ensure the usefulness of the
|
|
34
|
+
# above example.
|
|
35
|
+
it { expect(node).to eq(method_node) }
|
|
36
|
+
it { expect(node).not_to be method_node }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it_behaves_like 'unfooled by the duplicate node'
|
|
40
|
+
|
|
41
|
+
context 'when the node is in a begin block' do
|
|
42
|
+
let(:metaclass_node) { s(:sclass, s(:self), s(:begin, method_node)) }
|
|
43
|
+
|
|
44
|
+
it_behaves_like 'unfooled by the duplicate node'
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
context 'when called with ast containing the node' do
|
|
49
|
+
it { is_expected.to be metaclass_node }
|
|
50
|
+
|
|
51
|
+
context 'inside a begin block' do
|
|
52
|
+
let(:metaclass_node) { s(:sclass, s(:self), s(:begin, method_node)) }
|
|
53
|
+
|
|
54
|
+
it { is_expected.to be metaclass_node }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
context 'inside a different class' do
|
|
58
|
+
let(:metaclass_node) { s(:sclass, s(:self), s(:class, :MyClass, nil, method_node)) }
|
|
59
|
+
|
|
60
|
+
it { is_expected.to be nil }
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -49,13 +49,18 @@ RSpec.describe Mutant::Expression::Methods do
|
|
|
49
49
|
context 'with an instance method' do
|
|
50
50
|
let(:attributes) { { scope_name: 'TestApp::Literal', scope_symbol: '#' } }
|
|
51
51
|
|
|
52
|
-
it { should eql(Mutant::Matcher::Methods::Instance.new(TestApp::Literal)) }
|
|
52
|
+
it { should eql(Mutant::Matcher::Chain.new([Mutant::Matcher::Methods::Instance.new(TestApp::Literal)])) }
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
context 'with a singleton method' do
|
|
56
56
|
let(:attributes) { { scope_name: 'TestApp::Literal', scope_symbol: '.' } }
|
|
57
57
|
|
|
58
|
-
it
|
|
58
|
+
it do
|
|
59
|
+
should eql(Mutant::Matcher::Chain.new([
|
|
60
|
+
Mutant::Matcher::Methods::Singleton.new(TestApp::Literal),
|
|
61
|
+
Mutant::Matcher::Methods::Metaclass.new(TestApp::Literal)
|
|
62
|
+
]))
|
|
63
|
+
end
|
|
59
64
|
end
|
|
60
65
|
end
|
|
61
66
|
end
|
|
@@ -283,11 +283,23 @@ RSpec.describe Mutant::License do
|
|
|
283
283
|
context 'when mutant-license gem cannot be loaded' do
|
|
284
284
|
let(:load_json) { false }
|
|
285
285
|
|
|
286
|
-
|
|
287
|
-
|
|
286
|
+
def self.setup_error(message)
|
|
287
|
+
before do
|
|
288
|
+
allow(gem_method).to receive(:call).and_raise(Gem::LoadError, message)
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
context 'while the mutant license gem from rubygems is present' do
|
|
293
|
+
setup_error %{can't activate mutant-license (~> 0.1.0), already activated mutant-license-0.0.0.}
|
|
294
|
+
|
|
295
|
+
it_fails_with_message '[Mutant-License-Error]: mutant-license gem from rubygems.org is a dummy'
|
|
288
296
|
end
|
|
289
297
|
|
|
290
|
-
|
|
298
|
+
context 'with other error message' do
|
|
299
|
+
setup_error 'test-error'
|
|
300
|
+
|
|
301
|
+
it_fails_with_message '[Mutant-License-Error]: test-error'
|
|
302
|
+
end
|
|
291
303
|
end
|
|
292
304
|
end
|
|
293
305
|
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Mutant::Matcher::Method::Metaclass, '#call' do
|
|
4
|
+
SOURCE_PATH = 'test_app/lib/test_app/metaclasses.rb'
|
|
5
|
+
subject { object.call(env) }
|
|
6
|
+
|
|
7
|
+
let(:object) { described_class.new(scope, method) }
|
|
8
|
+
let(:method) { scope.method(method_name) }
|
|
9
|
+
let(:type) { :def }
|
|
10
|
+
let(:method_name) { :foo }
|
|
11
|
+
let(:method_arity) { 0 }
|
|
12
|
+
let(:base) { TestApp::MetaclassMethodTests }
|
|
13
|
+
let(:source_path) { MutantSpec::ROOT.join(SOURCE_PATH) }
|
|
14
|
+
let(:warnings) { instance_double(Mutant::Warnings) }
|
|
15
|
+
|
|
16
|
+
let(:world) do
|
|
17
|
+
instance_double(
|
|
18
|
+
Mutant::World,
|
|
19
|
+
pathname: Pathname,
|
|
20
|
+
warnings: warnings
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
let(:env) do
|
|
25
|
+
instance_double(
|
|
26
|
+
Mutant::Env,
|
|
27
|
+
config: Mutant::Config::DEFAULT,
|
|
28
|
+
parser: Fixtures::TEST_ENV.parser,
|
|
29
|
+
world: world
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def name
|
|
34
|
+
node.children.fetch(0)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def arguments
|
|
38
|
+
node.children.fetch(1)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
context 'when also defined on lvar' do
|
|
42
|
+
let(:scope) { base::DefinedOnLvar }
|
|
43
|
+
let(:expected_warnings) do
|
|
44
|
+
[
|
|
45
|
+
'Can only match :def inside :sclass on :self or :const, got :sclass on :lvar unable to match'
|
|
46
|
+
]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
include_examples 'skipped candidate'
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
context 'when defined on self' do
|
|
53
|
+
let(:scope) { base::DefinedOnSelf }
|
|
54
|
+
let(:method_line) { 7 }
|
|
55
|
+
|
|
56
|
+
it_should_behave_like 'a method matcher'
|
|
57
|
+
|
|
58
|
+
context 'when scope is a metaclass' do
|
|
59
|
+
let(:scope) { base::DefinedOnSelf::InsideMetaclass.metaclass }
|
|
60
|
+
let(:method_line) { 28 }
|
|
61
|
+
|
|
62
|
+
it_should_behave_like 'a method matcher'
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
context 'when defined on constant' do
|
|
67
|
+
context 'inside namespace' do
|
|
68
|
+
let(:scope) { base::DefinedOnConstant::InsideNamespace }
|
|
69
|
+
let(:method_line) { 44 }
|
|
70
|
+
|
|
71
|
+
it_should_behave_like 'a method matcher'
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
context 'outside namespace' do
|
|
75
|
+
let(:scope) { base::DefinedOnConstant::OutsideNamespace }
|
|
76
|
+
let(:method_line) { 52 }
|
|
77
|
+
|
|
78
|
+
it_should_behave_like 'a method matcher'
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
context 'when defined multiple times in the same line' do
|
|
83
|
+
context 'with method on different scope' do
|
|
84
|
+
let(:scope) { base::DefinedMultipleTimes::SameLine::DifferentScope }
|
|
85
|
+
let(:method_line) { 76 }
|
|
86
|
+
let(:method_arity) { 1 }
|
|
87
|
+
|
|
88
|
+
it_should_behave_like 'a method matcher'
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
context 'with different name' do
|
|
92
|
+
let(:scope) { base::DefinedMultipleTimes::SameLine::DifferentName }
|
|
93
|
+
let(:method_line) { 80 }
|
|
94
|
+
|
|
95
|
+
it_should_behave_like 'a method matcher'
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# tests that the evaluator correctly returns nil when the metaclass doesn't
|
|
100
|
+
# directly contain the method
|
|
101
|
+
context 'when defined inside a class in a metaclass' do
|
|
102
|
+
let(:scope) { base::NotActuallyInAMetaclass }
|
|
103
|
+
let(:method) { scope.metaclass::SomeClass.new.method(:foo) }
|
|
104
|
+
let(:method_line) { 93 }
|
|
105
|
+
|
|
106
|
+
it { is_expected.to be_empty }
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Mutant::Matcher::Methods::Metaclass, '#call' do
|
|
4
|
+
let(:object) { described_class.new(class_under_test) }
|
|
5
|
+
let(:env) { Fixtures::TEST_ENV }
|
|
6
|
+
|
|
7
|
+
let(:class_under_test) do
|
|
8
|
+
parent = Module.new do
|
|
9
|
+
class << self
|
|
10
|
+
def method_d; end
|
|
11
|
+
|
|
12
|
+
protected
|
|
13
|
+
|
|
14
|
+
def method_e; end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def method_f; end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
Class.new do
|
|
23
|
+
extend parent
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
def method_a; end
|
|
27
|
+
|
|
28
|
+
protected
|
|
29
|
+
|
|
30
|
+
def method_b; end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def method_c; end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
let(:subject_a) { instance_double(Mutant::Subject, 'A') }
|
|
40
|
+
let(:subject_b) { instance_double(Mutant::Subject, 'B') }
|
|
41
|
+
let(:subject_c) { instance_double(Mutant::Subject, 'C') }
|
|
42
|
+
|
|
43
|
+
let(:subjects) { [subject_a, subject_b, subject_c] }
|
|
44
|
+
|
|
45
|
+
before do
|
|
46
|
+
matcher = Mutant::Matcher::Method::Metaclass
|
|
47
|
+
|
|
48
|
+
{
|
|
49
|
+
method_a: subject_a,
|
|
50
|
+
method_b: subject_b,
|
|
51
|
+
method_c: subject_c
|
|
52
|
+
}.each do |method, subject|
|
|
53
|
+
allow(matcher).to receive(:new)
|
|
54
|
+
.with(class_under_test, class_under_test.method(method))
|
|
55
|
+
.and_return(Mutant::Matcher::Static.new([subject]))
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'returns expected subjects' do
|
|
60
|
+
expect(object.call(env)).to eql(subjects)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -13,8 +13,10 @@ RSpec.describe Mutant::Matcher::Namespace, '#call' do
|
|
|
13
13
|
[
|
|
14
14
|
[Mutant::Matcher::Methods::Singleton, raw_scope_b, [subject_b]],
|
|
15
15
|
[Mutant::Matcher::Methods::Instance, raw_scope_b, []],
|
|
16
|
+
[Mutant::Matcher::Methods::Metaclass, raw_scope_b, []],
|
|
16
17
|
[Mutant::Matcher::Methods::Singleton, raw_scope_a, [subject_a]],
|
|
17
|
-
[Mutant::Matcher::Methods::Instance, raw_scope_a, []]
|
|
18
|
+
[Mutant::Matcher::Methods::Instance, raw_scope_a, []],
|
|
19
|
+
[Mutant::Matcher::Methods::Metaclass, raw_scope_a, []]
|
|
18
20
|
].each do |klass, scope, subjects|
|
|
19
21
|
matcher = instance_double(Mutant::Matcher)
|
|
20
22
|
expect(matcher).to receive(:call).with(env).and_return(subjects)
|
|
@@ -6,8 +6,10 @@ RSpec.describe Mutant::Matcher::Scope, '#call' do
|
|
|
6
6
|
let(:env) { instance_double(Mutant::Env) }
|
|
7
7
|
let(:matcher_a) { instance_double(Mutant::Matcher) }
|
|
8
8
|
let(:matcher_b) { instance_double(Mutant::Matcher) }
|
|
9
|
+
let(:matcher_c) { instance_double(Mutant::Matcher) }
|
|
9
10
|
let(:subject_a) { instance_double(Mutant::Subject) }
|
|
10
11
|
let(:subject_b) { instance_double(Mutant::Subject) }
|
|
12
|
+
let(:subject_c) { instance_double(Mutant::Subject) }
|
|
11
13
|
|
|
12
14
|
subject { object.call(env) }
|
|
13
15
|
|
|
@@ -20,6 +22,10 @@ RSpec.describe Mutant::Matcher::Scope, '#call' do
|
|
|
20
22
|
.with(scope)
|
|
21
23
|
.and_return(matcher_b)
|
|
22
24
|
|
|
25
|
+
expect(Mutant::Matcher::Methods::Metaclass).to receive(:new)
|
|
26
|
+
.with(scope)
|
|
27
|
+
.and_return(matcher_c)
|
|
28
|
+
|
|
23
29
|
expect(matcher_a).to receive(:call)
|
|
24
30
|
.with(env)
|
|
25
31
|
.and_return([subject_a])
|
|
@@ -27,9 +33,13 @@ RSpec.describe Mutant::Matcher::Scope, '#call' do
|
|
|
27
33
|
expect(matcher_b).to receive(:call)
|
|
28
34
|
.with(env)
|
|
29
35
|
.and_return([subject_b])
|
|
36
|
+
|
|
37
|
+
expect(matcher_c).to receive(:call)
|
|
38
|
+
.with(env)
|
|
39
|
+
.and_return([subject_c])
|
|
30
40
|
end
|
|
31
41
|
|
|
32
42
|
it 'concatenates subjects from matched singleton and instance methods' do
|
|
33
|
-
should eql([subject_a, subject_b])
|
|
43
|
+
should eql([subject_a, subject_b, subject_c])
|
|
34
44
|
end
|
|
35
45
|
end
|
|
@@ -10,9 +10,9 @@ RSpec.describe Mutant::Meta::Example do
|
|
|
10
10
|
)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
let(:file) { 'foo/bar.rb'
|
|
14
|
-
let(:node) { s(:true)
|
|
15
|
-
let(:mutation_nodes) { [s(:
|
|
13
|
+
let(:file) { 'foo/bar.rb' }
|
|
14
|
+
let(:node) { s(:true) }
|
|
15
|
+
let(:mutation_nodes) { [s(:false)] }
|
|
16
16
|
|
|
17
17
|
let(:mutations) do
|
|
18
18
|
mutation_nodes.map do |node|
|
|
@@ -41,12 +41,7 @@ RSpec.describe Mutant::Mutator::Node do
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
specify do
|
|
44
|
-
expect(apply).to eql(
|
|
45
|
-
[
|
|
46
|
-
s(:and, s(:nil), s(:true)),
|
|
47
|
-
s(:and, s(:true), s(:nil))
|
|
48
|
-
].to_set
|
|
49
|
-
)
|
|
44
|
+
expect(apply).to eql([s(:and, s(:nil), s(:true))].to_set)
|
|
50
45
|
end
|
|
51
46
|
end
|
|
52
47
|
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe Mutant::Subject::Method::Metaclass do
|
|
4
|
+
let(:object) do
|
|
5
|
+
described_class.new(
|
|
6
|
+
context: context,
|
|
7
|
+
node: node,
|
|
8
|
+
warnings: warnings
|
|
9
|
+
)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
let(:node) { s(:def, :foo, s(:args)) }
|
|
13
|
+
let(:warnings) { instance_double(Mutant::Warnings) }
|
|
14
|
+
|
|
15
|
+
let(:context) do
|
|
16
|
+
Mutant::Context.new(scope, instance_double(Pathname))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
let(:scope) do
|
|
20
|
+
Class.new do
|
|
21
|
+
class << self
|
|
22
|
+
def foo; end
|
|
23
|
+
|
|
24
|
+
def name
|
|
25
|
+
'Test'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe '#expression' do
|
|
32
|
+
subject { object.expression }
|
|
33
|
+
|
|
34
|
+
it { should eql(parse_expression('Test.foo')) }
|
|
35
|
+
|
|
36
|
+
it_should_behave_like 'an idempotent method'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe '#match_expression' do
|
|
40
|
+
subject { object.match_expressions }
|
|
41
|
+
|
|
42
|
+
it { should eql(%w[Test.foo Test*].map(&method(:parse_expression))) }
|
|
43
|
+
|
|
44
|
+
it_should_behave_like 'an idempotent method'
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe '#prepare' do
|
|
48
|
+
|
|
49
|
+
subject { object.prepare }
|
|
50
|
+
|
|
51
|
+
it 'undefines method on scope' do
|
|
52
|
+
expect { subject }.to change { scope.methods.include?(:foo) }.from(true).to(false)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it_should_behave_like 'a command method'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe '#source' do
|
|
59
|
+
subject { object.source }
|
|
60
|
+
|
|
61
|
+
it { should eql("class << self\n def foo\n end\nend") }
|
|
62
|
+
end
|
|
63
|
+
end
|