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.
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