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
@@ -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 }