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.
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
@@ -1,76 +1,72 @@
1
- RSpec.describe Mutant::Matcher::Method::Singleton, '#each' do
2
- subject { object.each(&yields.method(:<<)) }
3
-
4
- let(:object) { described_class.new(env, scope, method) }
5
- let(:method) { scope.method(method_name) }
6
- let(:env) { Fixtures::TEST_ENV }
7
- let(:yields) { [] }
8
- let(:type) { :defs }
9
- let(:method_name) { :foo }
10
- let(:method_arity) { 0 }
11
- let(:base) { TestApp::SingletonMethodTests }
1
+ RSpec.describe Mutant::Matcher::Method::Singleton, '#call' do
2
+ subject { object.call(env) }
3
+
4
+ let(:object) { described_class.new(scope, method) }
5
+ let(:env) { test_env }
6
+ let(:method) { scope.method(method_name) }
7
+ let(:type) { :defs }
8
+ let(:method_name) { :foo }
9
+ let(:method_arity) { 0 }
10
+ let(:base) { TestApp::SingletonMethodTests }
11
+ let(:source_path) { MutantSpec::ROOT.join('test_app/lib/test_app.rb') }
12
12
 
13
13
  def name
14
- node.children[1]
14
+ node.children.fetch(1)
15
15
  end
16
16
 
17
17
  def arguments
18
- node.children[2]
18
+ node.children.fetch(2)
19
19
  end
20
20
 
21
- context 'on singleton methods' do
21
+ context 'when also defined on lvar' do
22
+ let(:scope) { base::DefinedOnLvar }
23
+ let(:method_line) { 66 }
24
+ let(:expected_warnings) do
25
+ [
26
+ 'Can only match :defs on :self or :const got :lvar unable to match'
27
+ ]
28
+ end
22
29
 
23
- context 'when also defined on lvar' do
24
- let(:scope) { base::AlsoDefinedOnLvar }
25
- let(:source_path) { MutantSpec::ROOT.join('test_app/lib/test_app.rb') }
26
- let(:method_line) { 66 }
30
+ include_examples 'skipped candidate'
31
+ end
27
32
 
28
- it_should_behave_like 'a method matcher'
33
+ context 'when defined on self' do
34
+ let(:scope) { base::DefinedOnSelf }
35
+ let(:method_line) { 61 }
29
36
 
30
- it 'warns about definition on non const/self' do
31
- subject
32
- expect(env.config.reporter.warn_calls).to(
33
- include('Can only match :defs on :self or :const got :lvar unable to match')
34
- )
35
- end
36
- end
37
+ it_should_behave_like 'a method matcher'
38
+ end
37
39
 
38
- context 'when defined on self' do
39
- let(:scope) { base::DefinedOnSelf }
40
- let(:source_path) { MutantSpec::ROOT.join('test_app/lib/test_app.rb') }
41
- let(:method_line) { 61 }
40
+ context 'when defined on constant' do
41
+ context 'inside namespace' do
42
+ let(:scope) { base::DefinedOnConstant::InsideNamespace }
43
+ let(:method_line) { 71 }
42
44
 
43
45
  it_should_behave_like 'a method matcher'
44
46
  end
45
47
 
46
- context 'when defined on constant' do
48
+ context 'outside namespace' do
49
+ let(:scope) { base::DefinedOnConstant::OutsideNamespace }
50
+ let(:method_line) { 78 }
47
51
 
48
- context 'inside namespace' do
49
- let(:scope) { base::DefinedOnConstant::InsideNamespace }
50
- let(:source_path) { MutantSpec::ROOT.join('test_app/lib/test_app.rb') }
51
- let(:method_line) { 71 }
52
-
53
- it_should_behave_like 'a method matcher'
54
- end
52
+ it_should_behave_like 'a method matcher'
53
+ end
54
+ end
55
55
 
56
- context 'outside namespace' do
57
- let(:scope) { base::DefinedOnConstant::OutsideNamespace }
58
- let(:source_path) { MutantSpec::ROOT.join('test_app/lib/test_app.rb') }
59
- let(:method_line) { 78 }
56
+ context 'when defined multiple times in the same line' do
57
+ context 'with method on different scope' do
58
+ let(:scope) { base::DefinedMultipleTimes::SameLine::DifferentScope }
59
+ let(:method_line) { 97 }
60
+ let(:method_arity) { 1 }
60
61
 
61
- it_should_behave_like 'a method matcher'
62
- end
62
+ it_should_behave_like 'a method matcher'
63
63
  end
64
64
 
65
- context 'when defined multiple times in the same line' do
66
- context 'with method on different scope' do
67
- let(:scope) { base::DefinedMultipleTimes::SameLine::DifferentScope }
68
- let(:source_path) { MutantSpec::ROOT.join('test_app/lib/test_app.rb') }
69
- let(:method_line) { 97 }
70
- let(:method_arity) { 1 }
65
+ context 'with different name' do
66
+ let(:scope) { base::DefinedMultipleTimes::SameLine::DifferentName }
67
+ let(:method_line) { 101 }
71
68
 
72
- it_should_behave_like 'a method matcher'
73
- end
69
+ it_should_behave_like 'a method matcher'
74
70
  end
75
71
  end
76
72
  end
@@ -1,10 +1,6 @@
1
- RSpec.describe Mutant::Matcher::Methods::Instance, '#each' do
2
- let(:object) { described_class.new(env, class_under_test) }
3
- let(:env) { Fixtures::TEST_ENV }
4
-
5
- subject { object.each { |matcher| yields << matcher } }
6
-
7
- let(:yields) { [] }
1
+ RSpec.describe Mutant::Matcher::Methods::Instance, '#call' do
2
+ let(:object) { described_class.new(class_under_test) }
3
+ let(:env) { Fixtures::TEST_ENV }
8
4
 
9
5
  let(:class_under_test) do
10
6
  parent = Module.new do
@@ -37,26 +33,27 @@ RSpec.describe Mutant::Matcher::Methods::Instance, '#each' do
37
33
  end
38
34
  end
39
35
 
40
- let(:subject_a) { double('Subject A') }
41
- let(:subject_b) { double('Subject B') }
42
- let(:subject_c) { double('Subject C') }
43
-
44
- let(:subjects) { [subject_a, subject_b, subject_c] }
36
+ let(:subject_a) { instance_double(Mutant::Subject) }
37
+ let(:subject_b) { instance_double(Mutant::Subject) }
38
+ let(:subject_c) { instance_double(Mutant::Subject) }
39
+ let(:subjects) { [subject_a, subject_b, subject_c] }
45
40
 
46
41
  before do
47
- matcher = Mutant::Matcher::Method::Instance
48
- allow(matcher).to receive(:new)
49
- .with(env, class_under_test, class_under_test.instance_method(:method_a)).and_return([subject_a])
50
- allow(matcher).to receive(:new)
51
- .with(env, class_under_test, class_under_test.instance_method(:method_b)).and_return([subject_b])
52
- allow(matcher).to receive(:new)
53
- .with(env, class_under_test, class_under_test.instance_method(:method_c)).and_return([subject_c])
42
+ {
43
+ method_a: subject_a,
44
+ method_b: subject_b,
45
+ method_c: subject_c
46
+ }.each do |method, subject|
47
+ matcher = instance_double(Mutant::Matcher)
48
+ expect(matcher).to receive(:call).with(env).and_return([subject])
49
+
50
+ expect(Mutant::Matcher::Method::Instance).to receive(:new)
51
+ .with(class_under_test, class_under_test.instance_method(method))
52
+ .and_return(matcher)
53
+ end
54
54
  end
55
55
 
56
- it 'should yield expected subjects' do
57
- subject
58
- expect(yields).to eql(subjects)
56
+ it 'returns expected subjects' do
57
+ expect(object.call(env)).to eql(subjects)
59
58
  end
60
-
61
- it_should_behave_like 'an #each method'
62
59
  end
@@ -1,10 +1,6 @@
1
- RSpec.describe Mutant::Matcher::Methods::Singleton, '#each' do
2
- let(:object) { described_class.new(env, class_under_test) }
3
- let(:env) { Fixtures::TEST_ENV }
4
-
5
- subject { object.each { |matcher| yields << matcher } }
6
-
7
- let(:yields) { [] }
1
+ RSpec.describe Mutant::Matcher::Methods::Singleton, '#call' do
2
+ let(:object) { described_class.new(class_under_test) }
3
+ let(:env) { Fixtures::TEST_ENV }
8
4
 
9
5
  let(:class_under_test) do
10
6
  parent = Module.new do
@@ -32,26 +28,27 @@ RSpec.describe Mutant::Matcher::Methods::Singleton, '#each' do
32
28
  end
33
29
  end
34
30
 
35
- let(:subject_a) { double('Subject A') }
36
- let(:subject_b) { double('Subject B') }
37
- let(:subject_c) { double('Subject C') }
31
+ let(:subject_a) { instance_double(Mutant::Subject, 'A') }
32
+ let(:subject_b) { instance_double(Mutant::Subject, 'B') }
33
+ let(:subject_c) { instance_double(Mutant::Subject, 'C') }
38
34
 
39
35
  let(:subjects) { [subject_a, subject_b, subject_c] }
40
36
 
41
37
  before do
42
38
  matcher = Mutant::Matcher::Method::Singleton
43
- allow(matcher).to receive(:new)
44
- .with(env, class_under_test, class_under_test.method(:method_a)).and_return([subject_a])
45
- allow(matcher).to receive(:new)
46
- .with(env, class_under_test, class_under_test.method(:method_b)).and_return([subject_b])
47
- allow(matcher).to receive(:new)
48
- .with(env, class_under_test, class_under_test.method(:method_c)).and_return([subject_c])
49
- end
50
39
 
51
- it 'should yield expected subjects' do
52
- subject
53
- expect(yields).to eql(subjects)
40
+ {
41
+ method_a: subject_a,
42
+ method_b: subject_b,
43
+ method_c: subject_c
44
+ }.each do |method, subject|
45
+ allow(matcher).to receive(:new)
46
+ .with(class_under_test, class_under_test.method(method))
47
+ .and_return(Mutant::Matcher::Static.new([subject]))
48
+ end
54
49
  end
55
50
 
56
- it_should_behave_like 'an #each method'
51
+ it 'returns expected subjects' do
52
+ expect(object.call(env)).to eql(subjects)
53
+ end
57
54
  end
@@ -1,44 +1,36 @@
1
- RSpec.describe Mutant::Matcher::Namespace do
2
- let(:object) { described_class.new(env, parse_expression('TestApp*')) }
3
- let(:yields) { [] }
4
- let(:env) { double('Env') }
5
-
6
- subject { object.each { |item| yields << item } }
7
-
8
- describe '#each' do
9
-
10
- let(:singleton_a) { double('SingletonA', name: 'TestApp::Literal') }
11
- let(:singleton_b) { double('SingletonB', name: 'TestApp::Foo') }
12
- let(:singleton_c) { double('SingletonC', name: 'TestAppOther') }
13
- let(:subject_a) { double('SubjectA') }
14
- let(:subject_b) { double('SubjectB') }
15
-
16
- before do
17
- allow(Mutant::Matcher::Methods::Singleton).to receive(:new).with(env, singleton_b).and_return([subject_b])
18
- allow(Mutant::Matcher::Methods::Instance).to receive(:new).with(env, singleton_b).and_return([])
19
-
20
- allow(Mutant::Matcher::Methods::Singleton).to receive(:new).with(env, singleton_a).and_return([subject_a])
21
- allow(Mutant::Matcher::Methods::Instance).to receive(:new).with(env, singleton_a).and_return([])
22
-
23
- allow(env).to receive(:matchable_scopes).and_return(
24
- [singleton_a, singleton_b, singleton_c].map do |scope|
25
- Mutant::Matcher::Scope.new(env, scope, parse_expression(scope.name))
26
- end
27
- )
1
+ RSpec.describe Mutant::Matcher::Namespace, '#call' do
2
+ let(:object) { described_class.new(parse_expression('TestApp*')) }
3
+ let(:env) { instance_double(Mutant::Env) }
4
+ let(:raw_scope_a) { double('SingletonA', name: 'TestApp::Literal') }
5
+
6
+ let(:raw_scope_b) { double('SingletonB', name: 'TestApp::Foo') }
7
+ let(:raw_scope_c) { double('SingletonC', name: 'TestAppOther') }
8
+ let(:subject_a) { double('SubjectA') }
9
+ let(:subject_b) { double('SubjectB') }
10
+
11
+ before do
12
+ [
13
+ [Mutant::Matcher::Methods::Singleton, raw_scope_b, [subject_b]],
14
+ [Mutant::Matcher::Methods::Instance, raw_scope_b, []],
15
+ [Mutant::Matcher::Methods::Singleton, raw_scope_a, [subject_a]],
16
+ [Mutant::Matcher::Methods::Instance, raw_scope_a, []]
17
+ ].each do |klass, scope, subjects|
18
+ matcher = instance_double(Mutant::Matcher)
19
+ expect(matcher).to receive(:call).with(env).and_return(subjects)
20
+
21
+ expect(klass).to receive(:new)
22
+ .with(scope)
23
+ .and_return(matcher)
28
24
  end
29
25
 
30
- context 'with no block' do
31
- subject { object.each }
32
-
33
- it { should be_instance_of(to_enum.class) }
34
-
35
- it 'yields the expected values' do
36
- expect(subject.to_a).to eql(object.to_a)
26
+ allow(env).to receive(:matchable_scopes).and_return(
27
+ [raw_scope_a, raw_scope_b, raw_scope_c].map do |raw_scope|
28
+ Mutant::Scope.new(raw_scope, parse_expression(raw_scope.name))
37
29
  end
38
- end
30
+ )
31
+ end
39
32
 
40
- it 'should yield subjects' do
41
- expect { subject }.to change { yields }.from([]).to([subject_a, subject_b])
42
- end
33
+ it 'returns subjects' do
34
+ expect(object.call(env)).to eql([subject_a, subject_b])
43
35
  end
44
36
  end
@@ -1,24 +1,9 @@
1
- RSpec.describe Mutant::Matcher::Null do
1
+ RSpec.describe Mutant::Matcher::Null, '#call' do
2
2
  let(:object) { described_class.new }
3
+ let(:env) { instance_double(Mutant::Env) }
4
+ subject { object.call(env) }
3
5
 
4
- describe '#each' do
5
- let(:yields) { [] }
6
-
7
- subject { object.each { |entry| yields << entry } }
8
-
9
- # it_should_behave_like 'an #each method'
10
- context 'with no block' do
11
- subject { object.each }
12
-
13
- it { should be_instance_of(to_enum.class) }
14
-
15
- it 'yields the expected values' do
16
- expect(subject.to_a).to eql(object.to_a)
17
- end
18
- end
19
-
20
- it 'should yield subjects' do
21
- expect { subject }.not_to change { yields }.from([])
22
- end
6
+ it 'returns no subjects' do
7
+ should eql([])
23
8
  end
24
9
  end
@@ -0,0 +1,33 @@
1
+ RSpec.describe Mutant::Matcher::Scope, '#call' do
2
+ let(:scope) { TestApp }
3
+ let(:object) { described_class.new(scope) }
4
+ let(:env) { instance_double(Mutant::Env) }
5
+ let(:matcher_a) { instance_double(Mutant::Matcher) }
6
+ let(:matcher_b) { instance_double(Mutant::Matcher) }
7
+ let(:subject_a) { instance_double(Mutant::Subject) }
8
+ let(:subject_b) { instance_double(Mutant::Subject) }
9
+
10
+ subject { object.call(env) }
11
+
12
+ before do
13
+ expect(Mutant::Matcher::Methods::Singleton).to receive(:new)
14
+ .with(scope)
15
+ .and_return(matcher_a)
16
+
17
+ expect(Mutant::Matcher::Methods::Instance).to receive(:new)
18
+ .with(scope)
19
+ .and_return(matcher_b)
20
+
21
+ expect(matcher_a).to receive(:call)
22
+ .with(env)
23
+ .and_return([subject_a])
24
+
25
+ expect(matcher_b).to receive(:call)
26
+ .with(env)
27
+ .and_return([subject_b])
28
+ end
29
+
30
+ it 'concatenates subjects from matched singleton and instance methods' do
31
+ should eql([subject_a, subject_b])
32
+ end
33
+ end
@@ -0,0 +1,11 @@
1
+ RSpec.describe Mutant::Matcher::Static, '#call' do
2
+ let(:object) { described_class.new(subjects) }
3
+ let(:env) { instance_double(Mutant::Env) }
4
+ let(:subjects) { instance_double(Array) }
5
+
6
+ subject { object.call(env) }
7
+
8
+ it 'returns its predefined subjects' do
9
+ should be(subjects)
10
+ end
11
+ end
@@ -1,25 +1,45 @@
1
1
  RSpec.describe Mutant::Mutation do
2
-
3
2
  class TestMutation < Mutant::Mutation
4
3
  SYMBOL = 'test'.freeze
5
4
  end
6
5
 
7
6
  let(:object) { TestMutation.new(mutation_subject, Mutant::AST::Nodes::N_NIL) }
8
- let(:context) { double('Context') }
7
+ let(:context) { instance_double(Mutant::Context) }
9
8
 
10
9
  let(:mutation_subject) do
11
- double(
12
- 'Subject',
10
+ instance_double(
11
+ Mutant::Subject,
13
12
  identification: 'subject',
14
- context: context,
15
- source: 'original',
16
- tests: tests
13
+ context: context,
14
+ source: 'original'
17
15
  )
18
16
  end
19
17
 
20
- let(:test_a) { double('Test A') }
21
- let(:test_b) { double('Test B') }
22
- let(:tests) { [test_a, test_b] }
18
+ let(:test_a) { instance_double(Mutant::Test) }
19
+ let(:test_b) { instance_double(Mutant::Test) }
20
+
21
+ describe '#insert' do
22
+ subject { object.insert }
23
+
24
+ let(:root_node) { s(:foo) }
25
+
26
+ before do
27
+ expect(context).to receive(:root)
28
+ .with(object.node)
29
+ .and_return(root_node)
30
+
31
+ expect(mutation_subject).to receive(:prepare)
32
+ .ordered
33
+ .and_return(mutation_subject)
34
+
35
+ expect(Mutant::Loader::Eval).to receive(:call)
36
+ .ordered
37
+ .with(root_node, mutation_subject)
38
+ .and_return(Mutant::Loader::Eval)
39
+ end
40
+
41
+ it_should_behave_like 'a command method'
42
+ end
23
43
 
24
44
  describe '#code' do
25
45
  subject { object.code }