mutant 0.8.7 → 0.8.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +5 -0
  3. data/README.md +64 -3
  4. data/config/flay.yml +1 -1
  5. data/lib/mutant.rb +2 -0
  6. data/lib/mutant/cli.rb +1 -1
  7. data/lib/mutant/env/bootstrap.rb +36 -8
  8. data/lib/mutant/expression/method.rb +3 -5
  9. data/lib/mutant/expression/methods.rb +2 -4
  10. data/lib/mutant/expression/namespace.rb +4 -8
  11. data/lib/mutant/matcher.rb +3 -18
  12. data/lib/mutant/matcher/chain.rb +7 -13
  13. data/lib/mutant/matcher/compiler.rb +2 -13
  14. data/lib/mutant/matcher/filter.rb +6 -19
  15. data/lib/mutant/matcher/method.rb +124 -104
  16. data/lib/mutant/matcher/method/instance.rb +40 -34
  17. data/lib/mutant/matcher/method/singleton.rb +80 -61
  18. data/lib/mutant/matcher/methods.rb +19 -29
  19. data/lib/mutant/matcher/namespace.rb +22 -16
  20. data/lib/mutant/matcher/null.rb +4 -7
  21. data/lib/mutant/matcher/scope.rb +23 -13
  22. data/lib/mutant/matcher/static.rb +17 -0
  23. data/lib/mutant/mutation.rb +0 -5
  24. data/lib/mutant/reporter/cli/format.rb +2 -3
  25. data/lib/mutant/reporter/cli/printer/env_progress.rb +37 -11
  26. data/lib/mutant/reporter/cli/printer/status_progressive.rb +1 -1
  27. data/lib/mutant/scope.rb +6 -0
  28. data/lib/mutant/subject/method.rb +0 -7
  29. data/lib/mutant/subject/method/instance.rb +0 -10
  30. data/lib/mutant/subject/method/singleton.rb +0 -10
  31. data/lib/mutant/version.rb +1 -1
  32. data/lib/mutant/zombifier.rb +2 -1
  33. data/mutant-rspec.gemspec +1 -1
  34. data/spec/integration/mutant/rspec_spec.rb +1 -1
  35. data/spec/shared/method_matcher_behavior.rb +21 -14
  36. data/spec/spec_helper.rb +6 -0
  37. data/spec/unit/mutant/env/boostrap_spec.rb +88 -26
  38. data/spec/unit/mutant/env_spec.rb +0 -1
  39. data/spec/unit/mutant/expression/method_spec.rb +3 -3
  40. data/spec/unit/mutant/expression/methods_spec.rb +3 -4
  41. data/spec/unit/mutant/expression/namespace/flat_spec.rb +2 -3
  42. data/spec/unit/mutant/expression/namespace/recursive_spec.rb +2 -4
  43. data/spec/unit/mutant/matcher/chain_spec.rb +21 -29
  44. data/spec/unit/mutant/matcher/compiler/subject_prefix_spec.rb +16 -13
  45. data/spec/unit/mutant/matcher/compiler_spec.rb +49 -60
  46. data/spec/unit/mutant/matcher/filter_spec.rb +15 -31
  47. data/spec/unit/mutant/matcher/method/instance_spec.rb +84 -128
  48. data/spec/unit/mutant/matcher/method/singleton_spec.rb +48 -52
  49. data/spec/unit/mutant/matcher/methods/instance_spec.rb +21 -24
  50. data/spec/unit/mutant/matcher/methods/singleton_spec.rb +18 -21
  51. data/spec/unit/mutant/matcher/namespace_spec.rb +30 -38
  52. data/spec/unit/mutant/matcher/null_spec.rb +5 -20
  53. data/spec/unit/mutant/matcher/scope_spec.rb +33 -0
  54. data/spec/unit/mutant/matcher/static_spec.rb +11 -0
  55. data/spec/unit/mutant/mutation_spec.rb +30 -10
  56. data/spec/unit/mutant/reporter/cli/printer/env_progress_spec.rb +6 -0
  57. data/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb +2 -0
  58. data/spec/unit/mutant/reporter/cli/printer/status_spec.rb +10 -0
  59. data/spec/unit/mutant/reporter/cli_spec.rb +4 -0
  60. data/spec/unit/mutant/subject/method/instance_spec.rb +0 -28
  61. data/spec/unit/mutant/subject/method/singleton_spec.rb +0 -28
  62. data/test_app/Gemfile.rspec3.4 +7 -0
  63. data/test_app/lib/test_app.rb +16 -12
  64. data/test_app/lib/test_app/literal.rb +3 -0
  65. metadata +9 -2
@@ -55,7 +55,6 @@ RSpec.describe Mutant::Env do
55
55
 
56
56
  before do
57
57
  expect(isolation).to receive(:call).and_yield.and_return(test_result)
58
- expect(mutation_subject).to receive(:public?).and_return(true).ordered
59
58
  expect(mutation_subject).to receive(:prepare).and_return(mutation_subject).ordered
60
59
  expect(context).to receive(:root).with(s(:nil)).and_return(wrapped_node).ordered
61
60
  expect(Mutant::Loader::Eval).to receive(:call).with(wrapped_node, mutation_subject).and_return(nil).ordered
@@ -23,13 +23,13 @@ RSpec.describe Mutant::Expression::Method do
23
23
  end
24
24
 
25
25
  describe '#matcher' do
26
- subject { object.matcher(env) }
26
+ subject { object.matcher }
27
27
 
28
28
  context 'with an instance method' do
29
29
  let(:input) { instance_method }
30
30
 
31
31
  it 'returns correct matcher' do
32
- expect(subject.map(&:expression)).to eql([object])
32
+ expect(subject.call(env).map(&:expression)).to eql([object])
33
33
  end
34
34
  end
35
35
 
@@ -37,7 +37,7 @@ RSpec.describe Mutant::Expression::Method do
37
37
  let(:input) { singleton_method }
38
38
 
39
39
  it 'returns correct matcher' do
40
- expect(subject.map(&:expression)).to eql([object])
40
+ expect(subject.call(env).map(&:expression)).to eql([object])
41
41
  end
42
42
  end
43
43
  end
@@ -1,5 +1,4 @@
1
1
  RSpec.describe Mutant::Expression::Methods do
2
- let(:env) { Fixtures::TEST_ENV }
3
2
  let(:object) { described_class.new(attributes) }
4
3
 
5
4
  describe '#match_length' do
@@ -43,18 +42,18 @@ RSpec.describe Mutant::Expression::Methods do
43
42
  end
44
43
 
45
44
  describe '#matcher' do
46
- subject { object.matcher(env) }
45
+ subject { object.matcher }
47
46
 
48
47
  context 'with an instance method' do
49
48
  let(:attributes) { { scope_name: 'TestApp::Literal', scope_symbol: '#' } }
50
49
 
51
- it { should eql(Mutant::Matcher::Methods::Instance.new(env, TestApp::Literal)) }
50
+ it { should eql(Mutant::Matcher::Methods::Instance.new(TestApp::Literal)) }
52
51
  end
53
52
 
54
53
  context 'with a singleton method' do
55
54
  let(:attributes) { { scope_name: 'TestApp::Literal', scope_symbol: '.' } }
56
55
 
57
- it { should eql(Mutant::Matcher::Methods::Singleton.new(env, TestApp::Literal)) }
56
+ it { should eql(Mutant::Matcher::Methods::Singleton.new(TestApp::Literal)) }
58
57
  end
59
58
  end
60
59
  end
@@ -1,12 +1,11 @@
1
1
  RSpec.describe Mutant::Expression::Namespace::Exact do
2
2
  let(:object) { parse_expression(input) }
3
- let(:env) { Fixtures::TEST_ENV }
4
3
  let(:input) { 'TestApp::Literal' }
5
4
 
6
5
  describe '#matcher' do
7
- subject { object.matcher(env) }
6
+ subject { object.matcher }
8
7
 
9
- it { should eql(Mutant::Matcher::Scope.new(env, TestApp::Literal, object)) }
8
+ it { should eql(Mutant::Matcher::Scope.new(TestApp::Literal)) }
10
9
  end
11
10
 
12
11
  describe '#match_length' do
@@ -1,13 +1,11 @@
1
1
  RSpec.describe Mutant::Expression::Namespace::Recursive do
2
-
3
2
  let(:object) { parse_expression(input) }
4
3
  let(:input) { 'TestApp::Literal*' }
5
- let(:env) { Fixtures::TEST_ENV }
6
4
 
7
5
  describe '#matcher' do
8
- subject { object.matcher(env) }
6
+ subject { object.matcher }
9
7
 
10
- it { should eql(Mutant::Matcher::Namespace.new(env, object)) }
8
+ it { should eql(Mutant::Matcher::Namespace.new(object)) }
11
9
  end
12
10
 
13
11
  describe '#syntax' do
@@ -1,32 +1,24 @@
1
- RSpec.describe Mutant::Matcher::Chain do
2
-
3
- let(:object) { described_class.new(matchers) }
4
-
5
- describe '#each' do
6
- let(:yields) { [] }
7
- subject { object.each { |entry| yields << entry } }
8
-
9
- let(:matchers) { [matcher_a, matcher_b] }
10
-
11
- let(:matcher_a) { [subject_a] }
12
- let(:matcher_b) { [subject_b] }
13
-
14
- let(:subject_a) { double('Subject A') }
15
- let(:subject_b) { double('Subject B') }
16
-
17
- # it_should_behave_like 'an #each method'
18
- context 'with no block' do
19
- subject { object.each }
20
-
21
- it { should be_instance_of(to_enum.class) }
22
-
23
- it 'yields the expected values' do
24
- expect(subject.to_a).to eql(object.to_a)
25
- end
26
- end
1
+ RSpec.describe Mutant::Matcher::Chain, '#call' do
2
+ subject { object.call(env) }
3
+
4
+ let(:object) { described_class.new([matcher_a, matcher_b]) }
5
+ let(:env) { instance_double(Mutant::Env) }
6
+ let(:matcher_a) { instance_double(Mutant::Matcher) }
7
+ let(:matcher_b) { instance_double(Mutant::Matcher) }
8
+ let(:subject_a) { instance_double(Mutant::Subject) }
9
+ let(:subject_b) { instance_double(Mutant::Subject) }
10
+
11
+ before do
12
+ expect(matcher_a).to receive(:call)
13
+ .with(env)
14
+ .and_return([subject_a])
15
+
16
+ expect(matcher_b).to receive(:call)
17
+ .with(env)
18
+ .and_return([subject_b])
19
+ end
27
20
 
28
- it 'should yield subjects' do
29
- expect { subject }.to change { yields }.from([]).to([subject_a, subject_b])
30
- end
21
+ it 'returns concatenated matches' do
22
+ should eql([subject_a, subject_b])
31
23
  end
32
24
  end
@@ -1,21 +1,24 @@
1
- RSpec.describe Mutant::Matcher::Compiler::SubjectPrefix do
2
- let(:object) { described_class.new(parse_expression('Foo*')) }
1
+ RSpec.describe Mutant::Matcher::Compiler::SubjectPrefix, '#call' do
2
+ let(:object) { described_class.new(parse_expression('Foo*')) }
3
3
 
4
- let(:_subject) { double('Subject', expression: parse_expression(subject_expression)) }
4
+ let(:_subject) do
5
+ instance_double(
6
+ Mutant::Subject,
7
+ expression: parse_expression(subject_expression)
8
+ )
9
+ end
5
10
 
6
- describe '#call' do
7
- subject { object.call(_subject) }
11
+ subject { object.call(_subject) }
8
12
 
9
- context 'when subject expression is prefixed by expression' do
10
- let(:subject_expression) { 'Foo::Bar' }
13
+ context 'when subject expression is prefixed by expression' do
14
+ let(:subject_expression) { 'Foo::Bar' }
11
15
 
12
- it { should be(true) }
13
- end
16
+ it { should be(true) }
17
+ end
14
18
 
15
- context 'when subject expression is NOT prefixed by expression' do
16
- let(:subject_expression) { 'Bar' }
19
+ context 'when subject expression is NOT prefixed by expression' do
20
+ let(:subject_expression) { 'Bar' }
17
21
 
18
- it { should be(false) }
19
- end
22
+ it { should be(false) }
20
23
  end
21
24
  end
@@ -1,89 +1,78 @@
1
- RSpec.describe Mutant::Matcher::Compiler do
2
- let(:object) { described_class }
3
-
4
- let(:env) { Fixtures::TEST_ENV }
5
-
6
- let(:expression_a) { parse_expression('Foo*') }
7
- let(:expression_b) { parse_expression('Bar*') }
8
-
9
- let(:matcher_a) { expression_a.matcher(env) }
10
- let(:matcher_b) { expression_b.matcher(env) }
1
+ RSpec.describe Mutant::Matcher::Compiler, '#call' do
2
+ let(:object) { described_class }
3
+ let(:env) { Fixtures::TEST_ENV }
4
+ let(:matcher_config) { Mutant::Matcher::Config::DEFAULT }
5
+ let(:expression_a) { parse_expression('Foo*') }
6
+ let(:expression_b) { parse_expression('Bar*') }
7
+ let(:matcher_a) { expression_a.matcher }
8
+ let(:matcher_b) { expression_b.matcher }
11
9
 
12
10
  let(:expected_matcher) do
13
- Mutant::Matcher::Filter.new(expected_positive_matcher, expected_predicate)
11
+ Mutant::Matcher::Filter.new(
12
+ expected_positive_matcher,
13
+ expected_predicate
14
+ )
14
15
  end
15
16
 
16
17
  let(:expected_predicate) do
17
18
  Morpher.compile(s(:and, s(:negate, s(:or)), s(:and)))
18
19
  end
19
20
 
20
- describe '.call' do
21
- subject { object.call(env, matcher_config.with(attributes)) }
21
+ subject { object.call(matcher_config.with(attributes)) }
22
22
 
23
- let(:matcher_config) { Mutant::Matcher::Config::DEFAULT }
23
+ context 'on empty config' do
24
+ let(:attributes) { {} }
24
25
 
25
- context 'on empty config' do
26
- let(:attributes) { {} }
26
+ let(:expected_positive_matcher) { Mutant::Matcher::Chain.new([]) }
27
27
 
28
- let(:expected_positive_matcher) { Mutant::Matcher::Chain.new([]) }
28
+ it { should eql(expected_matcher) }
29
+ end
29
30
 
30
- it { should eql(expected_matcher) }
31
+ context 'on config with match expression' do
32
+ let(:expected_predicate) do
33
+ Morpher::Evaluator::Predicate::Boolean::And.new(
34
+ [
35
+ Morpher::Evaluator::Predicate::Negation.new(
36
+ Morpher::Evaluator::Predicate::Boolean::Or.new(ignore_expression_predicates)
37
+ ),
38
+ Morpher::Evaluator::Predicate::Boolean::And.new(subject_filter_predicates)
39
+ ]
40
+ )
31
41
  end
32
42
 
33
- context 'on config with match expression' do
34
- let(:expected_predicate) do
35
- Morpher::Evaluator::Predicate::Boolean::And.new(
36
- [
37
- Morpher::Evaluator::Predicate::Negation.new(
38
- Morpher::Evaluator::Predicate::Boolean::Or.new(ignore_expression_predicates)
39
- ),
40
- Morpher::Evaluator::Predicate::Boolean::And.new(subject_filter_predicates)
41
- ]
42
- )
43
- end
43
+ let(:expected_positive_matcher) { Mutant::Matcher::Chain.new([matcher_a]) }
44
+ let(:attributes) { { match_expressions: [expression_a] } }
45
+ let(:ignore_expression_predicates) { [] }
46
+ let(:subject_filter_predicates) { [] }
44
47
 
45
- let(:expected_positive_matcher) { Mutant::Matcher::Chain.new([matcher_a]) }
46
- let(:attributes) { { match_expressions: [expression_a] } }
47
- let(:ignore_expression_predicates) { [] }
48
- let(:subject_filter_predicates) { [] }
48
+ context 'and no other constraints' do
49
+ it { should eql(expected_matcher) }
50
+ end
49
51
 
50
- context 'and no other constraints' do
51
- it { should eql(expected_matcher) }
52
+ context 'and ignore expressions' do
53
+ let(:attributes) do
54
+ super().merge(ignore_expressions: [expression_b])
52
55
  end
53
56
 
54
- context 'and ignore expressions' do
55
- let(:attributes) do
56
- super().merge(ignore_expressions: [expression_b])
57
- end
58
-
59
- let(:ignore_expression_predicates) do
60
- [Mutant::Matcher::Compiler::SubjectPrefix.new(expression_b)]
61
- end
62
-
63
- it { should eql(expected_matcher) }
57
+ let(:ignore_expression_predicates) do
58
+ [Mutant::Matcher::Compiler::SubjectPrefix.new(expression_b)]
64
59
  end
65
60
 
66
- context 'and subject filters' do
67
- let(:filter) { double('filter') }
68
-
69
- let(:attributes) do
70
- super().merge(subject_filters: [filter])
71
- end
61
+ it { should eql(expected_matcher) }
62
+ end
72
63
 
73
- let(:subject_filter_predicates) do
74
- [filter]
75
- end
64
+ context 'and subject filters' do
65
+ let(:filter) { double('filter') }
76
66
 
77
- it { should eql(expected_matcher) }
67
+ let(:attributes) do
68
+ super().merge(subject_filters: [filter])
78
69
  end
79
- end
80
70
 
81
- context 'on config with multiple match expressions' do
82
- let(:attributes) do
83
- { match_expressions: [expression_a, expression_b] }
71
+ let(:subject_filter_predicates) do
72
+ [filter]
84
73
  end
85
74
 
86
- let(:expected_positive_matcher) { Mutant::Matcher::Chain.new([matcher_a, matcher_b]) }
75
+ it { should eql(expected_matcher) }
87
76
  end
88
77
  end
89
78
  end
@@ -1,36 +1,20 @@
1
- RSpec.describe Mutant::Matcher::Filter do
2
- let(:object) { described_class.new(matcher, predicate) }
3
- let(:matcher) { [subject_a, subject_b] }
4
- let(:subject_a) { double('Subject A') }
5
- let(:subject_b) { double('Subject B') }
6
-
7
- describe '#each' do
8
- let(:yields) { [] }
9
- subject { object.each { |entry| yields << entry } }
10
-
11
- let(:predicate) { ->(node) { node.eql?(subject_a) } }
12
-
13
- # it_should_behave_like 'an #each method'
14
- context 'with no block' do
15
- subject { object.each }
16
-
17
- it { should be_instance_of(to_enum.class) }
1
+ RSpec.describe Mutant::Matcher::Filter, '#call' do
2
+ subject { object.call(env) }
18
3
 
19
- it 'yields the expected values' do
20
- expect(subject.to_a).to eql(object.to_a)
21
- end
22
- end
23
-
24
- it 'should yield subjects' do
25
- expect { subject }.to change { yields }.from([]).to([subject_a])
26
- end
4
+ let(:object) { described_class.new(matcher, predicate) }
5
+ let(:matcher) { instance_double(Mutant::Matcher) }
6
+ let(:subject_a) { instance_double(Mutant::Subject) }
7
+ let(:subject_b) { instance_double(Mutant::Subject) }
8
+ let(:env) { instance_double(Mutant::Env) }
9
+ let(:predicate) { ->(node) { node.eql?(subject_a) } }
10
+
11
+ before do
12
+ expect(matcher).to receive(:call)
13
+ .with(env)
14
+ .and_return([subject_a, subject_b])
27
15
  end
28
16
 
29
- describe '.build' do
30
- subject { described_class.build(matcher, &predicate) }
31
-
32
- let(:predicate) { ->(_subject) { false } }
33
-
34
- its(:to_a) { should eql([]) }
17
+ it 'returns subjects after filtering' do
18
+ should eql([subject_a])
35
19
  end
36
20
  end
@@ -1,157 +1,113 @@
1
- RSpec.describe Mutant::Matcher::Method::Instance do
2
-
3
- let(:env) { Fixtures::TEST_ENV }
4
- let(:reporter) { Fixtures::TEST_CONFIG.reporter }
1
+ RSpec.describe Mutant::Matcher::Method::Instance, '#call' do
2
+ subject { object.call(env) }
3
+
4
+ let(:object) { described_class.new(scope, method) }
5
+ let(:env) { test_env }
6
+ let(:method_name) { :foo }
7
+ let(:source_path) { MutantSpec::ROOT.join('test_app/lib/test_app.rb') }
8
+ let(:method) { scope.instance_method(method_name) }
9
+ let(:namespace) { self.class }
10
+ let(:type) { :def }
11
+ let(:method_arity) { 0 }
12
+ let(:base) { TestApp::InstanceMethodTests }
13
+
14
+ def name
15
+ node.children.fetch(0)
16
+ end
5
17
 
6
- describe '#each' do
7
- subject { object.each(&yields.method(:<<)) }
18
+ def arguments
19
+ node.children.fetch(1)
20
+ end
8
21
 
9
- let(:object) { described_class.build(env, scope, method) }
10
- let(:method) { scope.instance_method(method_name) }
11
- let(:yields) { [] }
12
- let(:namespace) { self.class }
13
- let(:type) { :def }
14
- let(:method_name) { :foo }
15
- let(:method_arity) { 0 }
16
- let(:base) { TestApp::InstanceMethodTests }
22
+ context 'when method is defined inside eval' do
23
+ let(:scope) { base::WithMemoizer }
24
+ let(:method) { scope.instance_method(:boz) }
25
+ let(:method_name) { :boz }
17
26
 
18
- def name
19
- node.children[0]
27
+ let(:expected_warnings) do
28
+ [
29
+ "#{method} does not have a valid source location, unable to emit subject"
30
+ ]
20
31
  end
21
32
 
22
- def arguments
23
- node.children[1]
24
- end
33
+ include_examples 'skipped candidate'
34
+ end
25
35
 
26
- context 'when method is defined without source location' do
27
- let(:scope) { Module }
28
- let(:method) { scope.instance_method(:object_id) }
29
-
30
- it 'does not emit matcher' do
31
- subject
32
- expect(yields.length).to be(0)
33
- end
34
-
35
- it 'does warn' do
36
- subject
37
- expect(reporter.warn_calls.last).to(
38
- eql("#{method.inspect} does not have valid source location unable to emit subject")
39
- )
40
- end
36
+ context 'when method is defined without source location' do
37
+ let(:scope) { Module }
38
+ let(:method) { scope.instance_method(:object_id) }
39
+
40
+ let(:expected_warnings) do
41
+ [
42
+ "#{method} does not have a valid source location, unable to emit subject"
43
+ ]
41
44
  end
42
45
 
43
- context 'when method is defined once' do
44
- let(:scope) { base::DefinedOnce }
45
- let(:source_path) { MutantSpec::ROOT.join('test_app/lib/test_app.rb') }
46
- let(:method_line) { 10 }
46
+ include_examples 'skipped candidate'
47
+ end
47
48
 
48
- it_should_behave_like 'a method matcher'
49
+ context 'in module eval' do
50
+ let(:scope) { base::InModuleEval }
51
+
52
+ let(:expected_warnings) do
53
+ [
54
+ "#{method} is dynamically defined in a closure, unable to emit subject"
55
+ ]
49
56
  end
50
57
 
51
- context 'when method is defined once with a memoizer' do
52
- let(:scope) { base::WithMemoizer }
53
- let(:source_path) { MutantSpec::ROOT.join('test_app/lib/test_app.rb') }
54
- let(:method_line) { 15 }
58
+ include_examples 'skipped candidate'
59
+ end
60
+
61
+ context 'in class eval' do
62
+ let(:scope) { base::InClassEval }
55
63
 
56
- it_should_behave_like 'a method matcher'
64
+ let(:expected_warnings) do
65
+ [
66
+ "#{method} is dynamically defined in a closure, unable to emit subject"
67
+ ]
57
68
  end
58
69
 
59
- context 'when method is defined multiple times' do
60
- context 'on different lines' do
61
- let(:scope) { base::DefinedMultipleTimes::DifferentLines }
62
- let(:source_path) { MutantSpec::ROOT.join('test_app/lib/test_app.rb') }
63
- let(:method_line) { 24 }
64
- let(:method_arity) { 1 }
65
-
66
- it_should_behave_like 'a method matcher'
67
- end
68
-
69
- context 'on the same line' do
70
- let(:scope) { base::DefinedMultipleTimes::SameLineSameScope }
71
- let(:source_path) { MutantSpec::ROOT.join('test_app/lib/test_app.rb') }
72
- let(:method_line) { 29 }
73
- let(:method_arity) { 1 }
74
-
75
- it_should_behave_like 'a method matcher'
76
- end
77
-
78
- context 'on the same line with different scope' do
79
- let(:scope) { base::DefinedMultipleTimes::SameLineDifferentScope }
80
- let(:source_path) { MutantSpec::ROOT.join('test_app/lib/test_app.rb') }
81
- let(:method_line) { 33 }
82
- let(:method_arity) { 1 }
83
-
84
- it_should_behave_like 'a method matcher'
85
- end
86
-
87
- context 'in module eval' do
88
- let(:scope) { base::InModuleEval }
89
-
90
- it 'does not emit matcher' do
91
- subject
92
- expect(yields.length).to be(0)
93
- end
94
-
95
- it 'does warn' do
96
- subject
97
- expect(reporter.warn_calls.last).to(
98
- eql("#{method.inspect} is defined from a 3rd party lib unable to emit subject")
99
- )
100
- end
101
- end
102
-
103
- context 'in class eval' do
104
- let(:scope) { base::InClassEval }
105
-
106
- it 'does not emit matcher' do
107
- subject
108
- expect(yields.length).to be(0)
109
- end
110
-
111
- it 'does warn' do
112
- subject
113
- expect(reporter.warn_calls.last).to(
114
- eql("#{method.inspect} is defined from a 3rd party lib unable to emit subject")
115
- )
116
- end
117
- end
118
- end
70
+ include_examples 'skipped candidate'
119
71
  end
120
72
 
121
- describe '.build' do
122
- let(:object) { described_class }
73
+ context 'when method is defined once' do
74
+ let(:method_name) { :bar }
75
+ let(:scope) { base::WithMemoizer }
76
+ let(:method_line) { 13 }
123
77
 
124
- subject { object.build(env, scope, method) }
78
+ it_should_behave_like 'a method matcher'
79
+ end
125
80
 
126
- let(:scope) do
127
- Class.new do
128
- include Adamantium
81
+ context 'when method is defined once with a memoizer' do
82
+ let(:scope) { base::WithMemoizer }
83
+ let(:method_line) { 15 }
129
84
 
130
- def foo
131
- end
132
- memoize :foo
85
+ it_should_behave_like 'a method matcher'
86
+ end
133
87
 
134
- def bar
135
- end
136
- end
137
- end
88
+ context 'when method is defined multiple times' do
89
+ context 'on different lines' do
90
+ let(:scope) { base::DefinedMultipleTimes::DifferentLines }
91
+ let(:method_line) { 24 }
92
+ let(:method_arity) { 1 }
138
93
 
139
- let(:method) do
140
- scope.instance_method(method_name)
94
+ it_should_behave_like 'a method matcher'
141
95
  end
142
96
 
143
- context 'with adamantium infected scope' do
144
- context 'with unmemoized method' do
145
- let(:method_name) { :bar }
97
+ context 'on the same line' do
98
+ let(:scope) { base::DefinedMultipleTimes::SameLineSameScope }
99
+ let(:method_line) { 29 }
100
+ let(:method_arity) { 1 }
146
101
 
147
- it { should eql(described_class.new(env, scope, method)) }
148
- end
102
+ it_should_behave_like 'a method matcher'
103
+ end
149
104
 
150
- context 'with memoized method' do
151
- let(:method_name) { :foo }
105
+ context 'on the same line with different scope' do
106
+ let(:scope) { base::DefinedMultipleTimes::SameLineDifferentScope }
107
+ let(:method_line) { 33 }
108
+ let(:method_arity) { 1 }
152
109
 
153
- it { should eql(described_class::Memoized.new(env, scope, method)) }
154
- end
110
+ it_should_behave_like 'a method matcher'
155
111
  end
156
112
  end
157
113
  end