mutant 0.9.8 → 0.9.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (161) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -2
  3. data/Changelog.md +6 -0
  4. data/README.md +63 -23
  5. data/config/reek.yml +1 -0
  6. data/lib/mutant.rb +4 -0
  7. data/lib/mutant/ast.rb +0 -9
  8. data/lib/mutant/ast/find_metaclass_containing.rb +48 -0
  9. data/lib/mutant/ast/meta/send.rb +0 -6
  10. data/lib/mutant/bootstrap.rb +0 -36
  11. data/lib/mutant/cli.rb +5 -49
  12. data/lib/mutant/color.rb +0 -3
  13. data/lib/mutant/config.rb +0 -8
  14. data/lib/mutant/context.rb +0 -3
  15. data/lib/mutant/diff.rb +0 -17
  16. data/lib/mutant/env.rb +0 -6
  17. data/lib/mutant/expression/method.rb +6 -6
  18. data/lib/mutant/expression/methods.rb +6 -6
  19. data/lib/mutant/expression/parser.rb +0 -6
  20. data/lib/mutant/integration.rb +0 -18
  21. data/lib/mutant/isolation/fork.rb +0 -22
  22. data/lib/mutant/license.rb +11 -0
  23. data/lib/mutant/matcher.rb +0 -14
  24. data/lib/mutant/matcher/config.rb +0 -11
  25. data/lib/mutant/matcher/method.rb +0 -31
  26. data/lib/mutant/matcher/method/instance.rb +0 -8
  27. data/lib/mutant/matcher/method/metaclass.rb +86 -0
  28. data/lib/mutant/matcher/method/singleton.rb +0 -25
  29. data/lib/mutant/matcher/methods.rb +17 -28
  30. data/lib/mutant/matcher/namespace.rb +0 -10
  31. data/lib/mutant/matcher/scope.rb +2 -4
  32. data/lib/mutant/meta/example/dsl.rb +0 -21
  33. data/lib/mutant/meta/example/verification.rb +0 -20
  34. data/lib/mutant/mutation.rb +0 -3
  35. data/lib/mutant/mutator.rb +1 -29
  36. data/lib/mutant/mutator/node.rb +1 -66
  37. data/lib/mutant/mutator/node/and_asgn.rb +0 -3
  38. data/lib/mutant/mutator/node/argument.rb +0 -15
  39. data/lib/mutant/mutator/node/arguments.rb +0 -20
  40. data/lib/mutant/mutator/node/begin.rb +0 -3
  41. data/lib/mutant/mutator/node/binary.rb +0 -23
  42. data/lib/mutant/mutator/node/block.rb +0 -15
  43. data/lib/mutant/mutator/node/break.rb +0 -3
  44. data/lib/mutant/mutator/node/case.rb +0 -9
  45. data/lib/mutant/mutator/node/class.rb +0 -3
  46. data/lib/mutant/mutator/node/conditional_loop.rb +0 -3
  47. data/lib/mutant/mutator/node/const.rb +0 -3
  48. data/lib/mutant/mutator/node/define.rb +0 -11
  49. data/lib/mutant/mutator/node/defined.rb +0 -3
  50. data/lib/mutant/mutator/node/dstr.rb +0 -3
  51. data/lib/mutant/mutator/node/dsym.rb +0 -3
  52. data/lib/mutant/mutator/node/generic.rb +0 -3
  53. data/lib/mutant/mutator/node/if.rb +0 -12
  54. data/lib/mutant/mutator/node/index.rb +0 -27
  55. data/lib/mutant/mutator/node/kwbegin.rb +0 -3
  56. data/lib/mutant/mutator/node/literal.rb +0 -3
  57. data/lib/mutant/mutator/node/literal/array.rb +0 -6
  58. data/lib/mutant/mutator/node/literal/boolean.rb +0 -4
  59. data/lib/mutant/mutator/node/literal/float.rb +0 -9
  60. data/lib/mutant/mutator/node/literal/hash.rb +0 -9
  61. data/lib/mutant/mutator/node/literal/integer.rb +0 -9
  62. data/lib/mutant/mutator/node/literal/nil.rb +0 -3
  63. data/lib/mutant/mutator/node/literal/range.rb +0 -6
  64. data/lib/mutant/mutator/node/literal/regex.rb +0 -6
  65. data/lib/mutant/mutator/node/literal/string.rb +0 -3
  66. data/lib/mutant/mutator/node/literal/symbol.rb +0 -3
  67. data/lib/mutant/mutator/node/masgn.rb +0 -3
  68. data/lib/mutant/mutator/node/match_current_line.rb +0 -3
  69. data/lib/mutant/mutator/node/mlhs.rb +0 -3
  70. data/lib/mutant/mutator/node/named_value/access.rb +2 -14
  71. data/lib/mutant/mutator/node/named_value/constant_assignment.rb +0 -9
  72. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +0 -6
  73. data/lib/mutant/mutator/node/next.rb +0 -3
  74. data/lib/mutant/mutator/node/noop.rb +0 -3
  75. data/lib/mutant/mutator/node/nthref.rb +0 -3
  76. data/lib/mutant/mutator/node/op_asgn.rb +0 -3
  77. data/lib/mutant/mutator/node/or_asgn.rb +0 -3
  78. data/lib/mutant/mutator/node/procarg_zero.rb +0 -3
  79. data/lib/mutant/mutator/node/regopt.rb +0 -6
  80. data/lib/mutant/mutator/node/resbody.rb +0 -6
  81. data/lib/mutant/mutator/node/rescue.rb +2 -19
  82. data/lib/mutant/mutator/node/return.rb +0 -3
  83. data/lib/mutant/mutator/node/sclass.rb +20 -0
  84. data/lib/mutant/mutator/node/send.rb +2 -61
  85. data/lib/mutant/mutator/node/send/attribute_assignment.rb +0 -9
  86. data/lib/mutant/mutator/node/send/binary.rb +0 -11
  87. data/lib/mutant/mutator/node/send/conditional.rb +0 -3
  88. data/lib/mutant/mutator/node/splat.rb +0 -3
  89. data/lib/mutant/mutator/node/super.rb +0 -3
  90. data/lib/mutant/mutator/node/when.rb +0 -19
  91. data/lib/mutant/mutator/node/yield.rb +0 -3
  92. data/lib/mutant/mutator/node/zsuper.rb +0 -3
  93. data/lib/mutant/mutator/util/array.rb +0 -6
  94. data/lib/mutant/mutator/util/symbol.rb +0 -3
  95. data/lib/mutant/parallel.rb +0 -13
  96. data/lib/mutant/parallel/driver.rb +0 -10
  97. data/lib/mutant/parallel/worker.rb +0 -22
  98. data/lib/mutant/reporter/cli.rb +0 -5
  99. data/lib/mutant/reporter/cli/format.rb +0 -9
  100. data/lib/mutant/reporter/cli/printer.rb +0 -40
  101. data/lib/mutant/reporter/cli/printer/env_progress.rb +0 -15
  102. data/lib/mutant/reporter/cli/printer/isolation_result.rb +0 -18
  103. data/lib/mutant/reporter/cli/printer/mutation_progress_result.rb +0 -5
  104. data/lib/mutant/reporter/cli/printer/mutation_result.rb +0 -21
  105. data/lib/mutant/reporter/cli/printer/status_progressive.rb +0 -8
  106. data/lib/mutant/reporter/cli/printer/subject_progress.rb +0 -9
  107. data/lib/mutant/repository/diff.rb +1 -13
  108. data/lib/mutant/repository/diff/ranges.rb +0 -11
  109. data/lib/mutant/result.rb +0 -3
  110. data/lib/mutant/runner.rb +0 -18
  111. data/lib/mutant/runner/sink.rb +0 -5
  112. data/lib/mutant/subject.rb +0 -8
  113. data/lib/mutant/subject/method.rb +0 -3
  114. data/lib/mutant/subject/method/instance.rb +0 -5
  115. data/lib/mutant/subject/method/metaclass.rb +30 -0
  116. data/lib/mutant/transform.rb +0 -92
  117. data/lib/mutant/version.rb +1 -1
  118. data/lib/mutant/warnings.rb +0 -6
  119. data/lib/mutant/zombifier.rb +2 -34
  120. data/meta/and.rb +0 -2
  121. data/meta/array.rb +0 -3
  122. data/meta/begin.rb +0 -3
  123. data/meta/block.rb +0 -3
  124. data/meta/break.rb +0 -1
  125. data/meta/case.rb +0 -6
  126. data/meta/casgn.rb +0 -3
  127. data/meta/cvasgn.rb +0 -1
  128. data/meta/def.rb +0 -7
  129. data/meta/ensure.rb +0 -1
  130. data/meta/false.rb +0 -1
  131. data/meta/gvasgn.rb +0 -1
  132. data/meta/hash.rb +0 -4
  133. data/meta/if.rb +0 -5
  134. data/meta/ivasgn.rb +0 -1
  135. data/meta/kwbegin.rb +0 -1
  136. data/meta/lvasgn.rb +0 -1
  137. data/meta/match_current_line.rb +0 -1
  138. data/meta/next.rb +0 -1
  139. data/meta/or.rb +0 -2
  140. data/meta/regexp.rb +0 -1
  141. data/meta/rescue.rb +0 -6
  142. data/meta/sclass.rb +12 -0
  143. data/meta/send.rb +0 -4
  144. data/meta/true.rb +0 -1
  145. data/meta/until.rb +0 -1
  146. data/meta/while.rb +0 -2
  147. data/meta/yield.rb +0 -1
  148. data/mutant.sh +12 -0
  149. data/spec/unit/mutant/ast/find_metaclass_containing_spec.rb +64 -0
  150. data/spec/unit/mutant/expression/methods_spec.rb +7 -2
  151. data/spec/unit/mutant/license_spec.rb +15 -3
  152. data/spec/unit/mutant/matcher/method/metaclass_spec.rb +108 -0
  153. data/spec/unit/mutant/matcher/methods/metaclass_spec.rb +62 -0
  154. data/spec/unit/mutant/matcher/namespace_spec.rb +3 -1
  155. data/spec/unit/mutant/matcher/scope_spec.rb +11 -1
  156. data/spec/unit/mutant/meta/example_spec.rb +3 -3
  157. data/spec/unit/mutant/mutator/node_spec.rb +1 -6
  158. data/spec/unit/mutant/subject/method/metaclass_spec.rb +63 -0
  159. data/test_app/lib/test_app.rb +1 -0
  160. data/test_app/lib/test_app/metaclasses.rb +108 -0
  161. metadata +17 -2
@@ -3,6 +3,5 @@
3
3
  Mutant::Meta::Example.add :true do
4
4
  source 'true'
5
5
 
6
- mutation 'nil'
7
6
  mutation 'false'
8
7
  end
@@ -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'
@@ -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
@@ -5,6 +5,5 @@ Mutant::Meta::Example.add :yield do
5
5
 
6
6
  singleton_mutations
7
7
  mutation 'yield false'
8
- mutation 'yield nil'
9
8
  mutation 'yield'
10
9
  end
@@ -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 { should eql(Mutant::Matcher::Methods::Singleton.new(TestApp::Literal)) }
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
- before do
287
- allow(gem_method).to receive(:call).and_raise(Gem::LoadError, 'test-error')
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
- it_fails_with_message '[Mutant-License-Error]: test-error'
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(:nil), s(:false)] }
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