mutant 0.8.7 → 0.8.8
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/Changelog.md +5 -0
- data/README.md +64 -3
- data/config/flay.yml +1 -1
- data/lib/mutant.rb +2 -0
- data/lib/mutant/cli.rb +1 -1
- data/lib/mutant/env/bootstrap.rb +36 -8
- data/lib/mutant/expression/method.rb +3 -5
- data/lib/mutant/expression/methods.rb +2 -4
- data/lib/mutant/expression/namespace.rb +4 -8
- data/lib/mutant/matcher.rb +3 -18
- data/lib/mutant/matcher/chain.rb +7 -13
- data/lib/mutant/matcher/compiler.rb +2 -13
- data/lib/mutant/matcher/filter.rb +6 -19
- data/lib/mutant/matcher/method.rb +124 -104
- data/lib/mutant/matcher/method/instance.rb +40 -34
- data/lib/mutant/matcher/method/singleton.rb +80 -61
- data/lib/mutant/matcher/methods.rb +19 -29
- data/lib/mutant/matcher/namespace.rb +22 -16
- data/lib/mutant/matcher/null.rb +4 -7
- data/lib/mutant/matcher/scope.rb +23 -13
- data/lib/mutant/matcher/static.rb +17 -0
- data/lib/mutant/mutation.rb +0 -5
- data/lib/mutant/reporter/cli/format.rb +2 -3
- data/lib/mutant/reporter/cli/printer/env_progress.rb +37 -11
- data/lib/mutant/reporter/cli/printer/status_progressive.rb +1 -1
- data/lib/mutant/scope.rb +6 -0
- data/lib/mutant/subject/method.rb +0 -7
- data/lib/mutant/subject/method/instance.rb +0 -10
- data/lib/mutant/subject/method/singleton.rb +0 -10
- data/lib/mutant/version.rb +1 -1
- data/lib/mutant/zombifier.rb +2 -1
- data/mutant-rspec.gemspec +1 -1
- data/spec/integration/mutant/rspec_spec.rb +1 -1
- data/spec/shared/method_matcher_behavior.rb +21 -14
- data/spec/spec_helper.rb +6 -0
- data/spec/unit/mutant/env/boostrap_spec.rb +88 -26
- data/spec/unit/mutant/env_spec.rb +0 -1
- data/spec/unit/mutant/expression/method_spec.rb +3 -3
- data/spec/unit/mutant/expression/methods_spec.rb +3 -4
- data/spec/unit/mutant/expression/namespace/flat_spec.rb +2 -3
- data/spec/unit/mutant/expression/namespace/recursive_spec.rb +2 -4
- data/spec/unit/mutant/matcher/chain_spec.rb +21 -29
- data/spec/unit/mutant/matcher/compiler/subject_prefix_spec.rb +16 -13
- data/spec/unit/mutant/matcher/compiler_spec.rb +49 -60
- data/spec/unit/mutant/matcher/filter_spec.rb +15 -31
- data/spec/unit/mutant/matcher/method/instance_spec.rb +84 -128
- data/spec/unit/mutant/matcher/method/singleton_spec.rb +48 -52
- data/spec/unit/mutant/matcher/methods/instance_spec.rb +21 -24
- data/spec/unit/mutant/matcher/methods/singleton_spec.rb +18 -21
- data/spec/unit/mutant/matcher/namespace_spec.rb +30 -38
- data/spec/unit/mutant/matcher/null_spec.rb +5 -20
- data/spec/unit/mutant/matcher/scope_spec.rb +33 -0
- data/spec/unit/mutant/matcher/static_spec.rb +11 -0
- data/spec/unit/mutant/mutation_spec.rb +30 -10
- data/spec/unit/mutant/reporter/cli/printer/env_progress_spec.rb +6 -0
- data/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb +2 -0
- data/spec/unit/mutant/reporter/cli/printer/status_spec.rb +10 -0
- data/spec/unit/mutant/reporter/cli_spec.rb +4 -0
- data/spec/unit/mutant/subject/method/instance_spec.rb +0 -28
- data/spec/unit/mutant/subject/method/singleton_spec.rb +0 -28
- data/test_app/Gemfile.rspec3.4 +7 -0
- data/test_app/lib/test_app.rb +16 -12
- data/test_app/lib/test_app/literal.rb +3 -0
- 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 | 
| 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 | 
| 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( | 
| 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( | 
| 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 | 
| 6 | 
            +
                subject { object.matcher }
         | 
| 8 7 |  | 
| 9 | 
            -
                it { should eql(Mutant::Matcher::Scope.new( | 
| 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 | 
| 6 | 
            +
                subject { object.matcher }
         | 
| 9 7 |  | 
| 10 | 
            -
                it { should eql(Mutant::Matcher::Namespace.new( | 
| 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 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
               | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
                 | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 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 | 
            -
             | 
| 29 | 
            -
             | 
| 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) | 
| 1 | 
            +
            RSpec.describe Mutant::Matcher::Compiler::SubjectPrefix, '#call' do
         | 
| 2 | 
            +
              let(:object)   { described_class.new(parse_expression('Foo*')) }
         | 
| 3 3 |  | 
| 4 | 
            -
              let(:_subject)  | 
| 4 | 
            +
              let(:_subject) do
         | 
| 5 | 
            +
                instance_double(
         | 
| 6 | 
            +
                  Mutant::Subject,
         | 
| 7 | 
            +
                  expression: parse_expression(subject_expression)
         | 
| 8 | 
            +
                )
         | 
| 9 | 
            +
              end
         | 
| 5 10 |  | 
| 6 | 
            -
               | 
| 7 | 
            -
                subject { object.call(_subject) }
         | 
| 11 | 
            +
              subject { object.call(_subject) }
         | 
| 8 12 |  | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 13 | 
            +
              context 'when subject expression is prefixed by expression' do
         | 
| 14 | 
            +
                let(:subject_expression) { 'Foo::Bar' }
         | 
| 11 15 |  | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 16 | 
            +
                it { should be(true) }
         | 
| 17 | 
            +
              end
         | 
| 14 18 |  | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 19 | 
            +
              context 'when subject expression is NOT prefixed by expression' do
         | 
| 20 | 
            +
                let(:subject_expression) { 'Bar' }
         | 
| 17 21 |  | 
| 18 | 
            -
             | 
| 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) | 
| 3 | 
            -
             | 
| 4 | 
            -
              let(: | 
| 5 | 
            -
             | 
| 6 | 
            -
              let(: | 
| 7 | 
            -
              let(: | 
| 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( | 
| 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 | 
            -
               | 
| 21 | 
            -
                subject { object.call(env, matcher_config.with(attributes)) }
         | 
| 21 | 
            +
              subject { object.call(matcher_config.with(attributes)) }
         | 
| 22 22 |  | 
| 23 | 
            -
             | 
| 23 | 
            +
              context 'on empty config' do
         | 
| 24 | 
            +
                let(:attributes) { {} }
         | 
| 24 25 |  | 
| 25 | 
            -
                 | 
| 26 | 
            -
                  let(:attributes) { {} }
         | 
| 26 | 
            +
                let(:expected_positive_matcher) { Mutant::Matcher::Chain.new([]) }
         | 
| 27 27 |  | 
| 28 | 
            -
             | 
| 28 | 
            +
                it { should eql(expected_matcher) }
         | 
| 29 | 
            +
              end
         | 
| 29 30 |  | 
| 30 | 
            -
             | 
| 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 | 
            -
                 | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 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 | 
            -
             | 
| 46 | 
            -
                   | 
| 47 | 
            -
             | 
| 48 | 
            -
                  let(:subject_filter_predicates)    { []                                      }
         | 
| 48 | 
            +
                context 'and no other constraints' do
         | 
| 49 | 
            +
                  it { should eql(expected_matcher) }
         | 
| 50 | 
            +
                end
         | 
| 49 51 |  | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            +
                context 'and ignore expressions' do
         | 
| 53 | 
            +
                  let(:attributes) do
         | 
| 54 | 
            +
                    super().merge(ignore_expressions: [expression_b])
         | 
| 52 55 | 
             
                  end
         | 
| 53 56 |  | 
| 54 | 
            -
                   | 
| 55 | 
            -
                     | 
| 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 | 
            -
                   | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
                    let(:attributes) do
         | 
| 70 | 
            -
                      super().merge(subject_filters: [filter])
         | 
| 71 | 
            -
                    end
         | 
| 61 | 
            +
                  it { should eql(expected_matcher) }
         | 
| 62 | 
            +
                end
         | 
| 72 63 |  | 
| 73 | 
            -
             | 
| 74 | 
            -
             | 
| 75 | 
            -
                    end
         | 
| 64 | 
            +
                context 'and subject filters' do
         | 
| 65 | 
            +
                  let(:filter) { double('filter') }
         | 
| 76 66 |  | 
| 77 | 
            -
             | 
| 67 | 
            +
                  let(:attributes) do
         | 
| 68 | 
            +
                    super().merge(subject_filters: [filter])
         | 
| 78 69 | 
             
                  end
         | 
| 79 | 
            -
                end
         | 
| 80 70 |  | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 83 | 
            -
                    { match_expressions: [expression_a, expression_b] }
         | 
| 71 | 
            +
                  let(:subject_filter_predicates) do
         | 
| 72 | 
            +
                    [filter]
         | 
| 84 73 | 
             
                  end
         | 
| 85 74 |  | 
| 86 | 
            -
                   | 
| 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 | 
            -
               | 
| 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 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 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 | 
            -
               | 
| 30 | 
            -
                 | 
| 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 | 
            -
             | 
| 4 | 
            -
              let(: | 
| 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 | 
            -
               | 
| 7 | 
            -
                 | 
| 18 | 
            +
              def arguments
         | 
| 19 | 
            +
                node.children.fetch(1)
         | 
| 20 | 
            +
              end
         | 
| 8 21 |  | 
| 9 | 
            -
             | 
| 10 | 
            -
                let(: | 
| 11 | 
            -
                let(: | 
| 12 | 
            -
                let(: | 
| 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 | 
            -
                 | 
| 19 | 
            -
                   | 
| 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 | 
            -
                 | 
| 23 | 
            -
             | 
| 24 | 
            -
                end
         | 
| 33 | 
            +
                include_examples 'skipped candidate'
         | 
| 34 | 
            +
              end
         | 
| 25 35 |  | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
                     | 
| 33 | 
            -
                   | 
| 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 | 
            -
                 | 
| 44 | 
            -
             | 
| 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 | 
            -
             | 
| 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 | 
            -
                 | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 58 | 
            +
                include_examples 'skipped candidate'
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
              context 'in class eval' do
         | 
| 62 | 
            +
                let(:scope) { base::InClassEval }
         | 
| 55 63 |  | 
| 56 | 
            -
             | 
| 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 | 
            -
                 | 
| 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 | 
            -
               | 
| 122 | 
            -
                let(: | 
| 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 | 
            -
                 | 
| 78 | 
            +
                it_should_behave_like 'a method matcher'
         | 
| 79 | 
            +
              end
         | 
| 125 80 |  | 
| 126 | 
            -
             | 
| 127 | 
            -
             | 
| 128 | 
            -
             | 
| 81 | 
            +
              context 'when method is defined once with a memoizer' do
         | 
| 82 | 
            +
                let(:scope)       { base::WithMemoizer }
         | 
| 83 | 
            +
                let(:method_line) { 15                 }
         | 
| 129 84 |  | 
| 130 | 
            -
             | 
| 131 | 
            -
             | 
| 132 | 
            -
                    memoize :foo
         | 
| 85 | 
            +
                it_should_behave_like 'a method matcher'
         | 
| 86 | 
            +
              end
         | 
| 133 87 |  | 
| 134 | 
            -
             | 
| 135 | 
            -
             | 
| 136 | 
            -
                   | 
| 137 | 
            -
             | 
| 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 | 
            -
             | 
| 140 | 
            -
                  scope.instance_method(method_name)
         | 
| 94 | 
            +
                  it_should_behave_like 'a method matcher'
         | 
| 141 95 | 
             
                end
         | 
| 142 96 |  | 
| 143 | 
            -
                context ' | 
| 144 | 
            -
                   | 
| 145 | 
            -
             | 
| 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 | 
            -
             | 
| 148 | 
            -
             | 
| 102 | 
            +
                  it_should_behave_like 'a method matcher'
         | 
| 103 | 
            +
                end
         | 
| 149 104 |  | 
| 150 | 
            -
             | 
| 151 | 
            -
             | 
| 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 | 
            -
             | 
| 154 | 
            -
                  end
         | 
| 110 | 
            +
                  it_should_behave_like 'a method matcher'
         | 
| 155 111 | 
             
                end
         | 
| 156 112 | 
             
              end
         | 
| 157 113 | 
             
            end
         |