mutant 0.8.9 → 0.8.10

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +4 -0
  3. data/config/devtools.yml +1 -1
  4. data/config/flay.yml +1 -1
  5. data/lib/mutant.rb +1 -2
  6. data/lib/mutant/ast/types.rb +3 -2
  7. data/lib/mutant/context.rb +78 -6
  8. data/lib/mutant/diff.rb +7 -13
  9. data/lib/mutant/matcher/method.rb +2 -2
  10. data/lib/mutant/mutation.rb +1 -1
  11. data/lib/mutant/mutator/node/generic.rb +1 -1
  12. data/lib/mutant/reporter/cli/printer.rb +25 -1
  13. data/lib/mutant/reporter/sequence.rb +22 -0
  14. data/lib/mutant/result.rb +4 -9
  15. data/lib/mutant/version.rb +1 -1
  16. data/meta/dsym.rb +7 -7
  17. data/mutant.gemspec +5 -5
  18. data/spec/spec_helper.rb +0 -1
  19. data/spec/unit/mutant/ast/named_children_spec.rb +12 -2
  20. data/spec/unit/mutant/context_spec.rb +85 -1
  21. data/spec/unit/mutant/diff_spec.rb +9 -0
  22. data/spec/unit/mutant/mutation_spec.rb +55 -4
  23. data/spec/unit/mutant/reporter/cli/printer/mutation_result_spec.rb +3 -3
  24. data/spec/unit/mutant/reporter/cli/printer_spec.rb +27 -6
  25. data/spec/unit/mutant/reporter/null_spec.rb +5 -15
  26. data/spec/unit/mutant/reporter/sequence_spec.rb +29 -0
  27. data/spec/unit/mutant/result/class_methods_spec.rb +49 -0
  28. data/spec/unit/mutant/result/mutation_spec.rb +49 -0
  29. data/spec/unit/mutant/result/subject_spec.rb +84 -19
  30. data/spec/unit/mutant/result_spec.rb +18 -10
  31. data/spec/unit/mutant/subject/method/instance_spec.rb +3 -3
  32. data/spec/unit/mutant/subject/method/singleton_spec.rb +1 -1
  33. metadata +19 -22
  34. data/lib/mutant/context/scope.rb +0 -94
  35. data/lib/mutant/delegator.rb +0 -43
  36. data/spec/unit/mutant/context/root_spec.rb +0 -11
  37. data/spec/unit/mutant/context/scope/root_spec.rb +0 -32
  38. data/spec/unit/mutant/context/scope/unqualified_name_spec.rb +0 -25
  39. data/spec/unit/mutant/context/scope_spec.rb +0 -11
@@ -34,6 +34,15 @@ RSpec.describe Mutant::Diff do
34
34
 
35
35
  it_should_behave_like 'an idempotent method'
36
36
  end
37
+
38
+ context 'when there is no diff' do
39
+ let(:old) { '' }
40
+ let(:new) { '' }
41
+
42
+ it { should be(nil) }
43
+
44
+ it_should_behave_like 'an idempotent method'
45
+ end
37
46
  end
38
47
 
39
48
  describe '#diff' do
@@ -1,10 +1,16 @@
1
1
  RSpec.describe Mutant::Mutation do
2
- class TestMutation < Mutant::Mutation
3
- SYMBOL = 'test'.freeze
2
+ let(:mutation_class) do
3
+ Class.new(Mutant::Mutation) do
4
+ const_set(:SYMBOL, 'test'.freeze)
5
+ const_set(:TEST_PASS_SUCCESS, true)
6
+ end
4
7
  end
5
8
 
6
- let(:object) { TestMutation.new(mutation_subject, Mutant::AST::Nodes::N_NIL) }
7
- let(:context) { instance_double(Mutant::Context) }
9
+ let(:context) { instance_double(Mutant::Context) }
10
+
11
+ let(:object) do
12
+ mutation_class.new(mutation_subject, Mutant::AST::Nodes::N_NIL)
13
+ end
8
14
 
9
15
  let(:mutation_subject) do
10
16
  instance_double(
@@ -71,6 +77,51 @@ RSpec.describe Mutant::Mutation do
71
77
  it_should_behave_like 'an idempotent method'
72
78
  end
73
79
 
80
+ describe '.success?' do
81
+ subject { mutation_class.success?(test_result) }
82
+
83
+ let(:test_result) do
84
+ instance_double(
85
+ Mutant::Result::Test,
86
+ passed: passed
87
+ )
88
+ end
89
+
90
+ context 'on mutation with positive pass expectation' do
91
+ context 'when Result::Test#passed equals expectation' do
92
+ let(:passed) { true }
93
+
94
+ it { should be(true) }
95
+ end
96
+
97
+ context 'when Result::Test#passed NOT equals expectation' do
98
+ let(:passed) { false }
99
+
100
+ it { should be(false) }
101
+ end
102
+ end
103
+
104
+ context 'on mutation with negative pass expectation' do
105
+ let(:mutation_class) do
106
+ Class.new(super()) do
107
+ const_set(:TEST_PASS_SUCCESS, false)
108
+ end
109
+ end
110
+
111
+ context 'when Result::Test#passed equals expectation' do
112
+ let(:passed) { true }
113
+
114
+ it { should be(false) }
115
+ end
116
+
117
+ context 'when Result::Test#passed NOT equals expectation' do
118
+ let(:passed) { false }
119
+
120
+ it { should be(true) }
121
+ end
122
+ end
123
+ end
124
+
74
125
  describe '#identification' do
75
126
 
76
127
  subject { object.identification }
@@ -50,11 +50,11 @@ RSpec.describe Mutant::Reporter::CLI::Printer::MutationResult do
50
50
  Original unparsed source:
51
51
  super
52
52
  Original AST:
53
- (lvar :super)
53
+ s(:lvar, :super)
54
54
  Mutated unparsed source:
55
55
  super
56
56
  Mutated AST:
57
- (zsuper)
57
+ s(:zsuper)
58
58
  -----------------------
59
59
  REPORT
60
60
  end
@@ -73,7 +73,7 @@ RSpec.describe Mutant::Reporter::CLI::Printer::MutationResult do
73
73
  Original code was inserted unmutated. And the test did NOT PASS.
74
74
  Your tests do not pass initially or you found a bug in mutant / unparser.
75
75
  Subject AST:
76
- (true)
76
+ s(:true)
77
77
  Unparsed Source:
78
78
  true
79
79
  Test Result:
@@ -16,7 +16,7 @@ RSpec.describe Mutant::Reporter::CLI::Printer do
16
16
  let(:tty?) { true }
17
17
  let(:success?) { true }
18
18
 
19
- context '.call' do
19
+ describe '.call' do
20
20
  let(:class_under_test) do
21
21
  Class.new(described_class) do
22
22
  def run
@@ -30,7 +30,28 @@ RSpec.describe Mutant::Reporter::CLI::Printer do
30
30
  it_reports "foo\n"
31
31
  end
32
32
 
33
- context '#status' do
33
+ describe '.delegate' do
34
+ let(:reportable) { double(foo: :bar, baz: :boz) }
35
+
36
+ let(:class_under_test) do
37
+ Class.new(described_class) do
38
+ delegate :foo, :baz
39
+
40
+ def run
41
+ puts(foo)
42
+ puts(baz)
43
+ end
44
+ end
45
+ end
46
+
47
+ it_reports "bar\nboz\n"
48
+
49
+ it 'sets delegation methods to private visibility' do
50
+ expect(class_under_test.private_instance_methods).to include(:foo, :baz)
51
+ end
52
+ end
53
+
54
+ describe '#status' do
34
55
  let(:class_under_test) do
35
56
  Class.new(described_class) do
36
57
  def run
@@ -65,7 +86,7 @@ RSpec.describe Mutant::Reporter::CLI::Printer do
65
86
  end
66
87
  end
67
88
 
68
- context '#visit_collection' do
89
+ describe '#visit_collection' do
69
90
  let(:class_under_test) do
70
91
  reporter = nested_reporter
71
92
  Class.new(described_class) do
@@ -86,7 +107,7 @@ RSpec.describe Mutant::Reporter::CLI::Printer do
86
107
  it_reports "foo\nbar\n"
87
108
  end
88
109
 
89
- context '#visit' do
110
+ describe '#visit' do
90
111
  let(:class_under_test) do
91
112
  reporter = nested_reporter
92
113
  Class.new(described_class) do
@@ -107,7 +128,7 @@ RSpec.describe Mutant::Reporter::CLI::Printer do
107
128
  it_reports "foo\n"
108
129
  end
109
130
 
110
- context '#info' do
131
+ describe '#info' do
111
132
  let(:class_under_test) do
112
133
  Class.new(described_class) do
113
134
  def run
@@ -119,7 +140,7 @@ RSpec.describe Mutant::Reporter::CLI::Printer do
119
140
  it_reports "foo - bar\n"
120
141
  end
121
142
 
122
- context '#colorize' do
143
+ describe '#colorize' do
123
144
  let(:class_under_test) do
124
145
  Class.new(described_class) do
125
146
  def run
@@ -2,21 +2,11 @@ RSpec.describe Mutant::Reporter::Null do
2
2
  let(:object) { described_class.new }
3
3
  let(:value) { instance_double(Object) }
4
4
 
5
- describe '#report' do
6
- subject { object.report(value) }
5
+ %i[progress report start warn].each do |name|
6
+ describe "##{name}" do
7
+ subject { object.public_send(name, value) }
7
8
 
8
- it_should_behave_like 'a command method'
9
- end
10
-
11
- describe '#warn' do
12
- subject { object.warn(value) }
13
-
14
- it_should_behave_like 'a command method'
15
- end
16
-
17
- describe '#progress' do
18
- subject { object.progress(value) }
19
-
20
- it_should_behave_like 'a command method'
9
+ it_should_behave_like 'a command method'
10
+ end
21
11
  end
22
12
  end
@@ -0,0 +1,29 @@
1
+ RSpec.describe Mutant::Reporter::Sequence do
2
+ let(:object) { described_class.new([reporter_a, reporter_b]) }
3
+ let(:value) { instance_double(Object) }
4
+ let(:reporter_a) { instance_double(Mutant::Reporter, delay: 1.0) }
5
+ let(:reporter_b) { instance_double(Mutant::Reporter, delay: 2.0) }
6
+
7
+ %i[report progress warn start].each do |name|
8
+ describe "##{name}" do
9
+ subject { object.public_send(name, value) }
10
+
11
+ before do
12
+ [reporter_a, reporter_b].each do |receiver|
13
+ expect(receiver).to receive(name)
14
+ .ordered
15
+ .with(value)
16
+ .and_return(receiver)
17
+ end
18
+ end
19
+
20
+ it_should_behave_like 'a command method'
21
+ end
22
+ end
23
+
24
+ describe '#delay' do
25
+ it 'returns the lowest value' do
26
+ expect(object.delay).to eql(1.0)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,49 @@
1
+ # The effect of this private module is done at boot time.
2
+ # Hence there is no way to kill the mutations at runtime
3
+ # so we have to directly hook into the private moduel via
4
+ # reflection to redo a runtime observable interaction with
5
+ # it.
6
+ #
7
+ # Until mutant gets boot time mutation support there is no
8
+ # way to to avoid this.
9
+ RSpec.describe 'Mutant::Result::ClassMethods' do
10
+ let(:infected_class) do
11
+ Class.new do
12
+ include Adamantium::Flat, Concord::Public.new(:collection)
13
+ extend Mutant::Result.const_get(:ClassMethods)
14
+
15
+ sum :length, :collection
16
+ end
17
+ end
18
+
19
+ describe '#sum' do
20
+ let(:object) { infected_class.new(collection) }
21
+
22
+ def apply
23
+ object.length
24
+ end
25
+
26
+ subject { apply }
27
+
28
+ before do
29
+ # memoization behavior
30
+ expect(collection).to receive(:map)
31
+ .once
32
+ .and_call_original
33
+
34
+ apply
35
+ end
36
+
37
+ context 'empty collection' do
38
+ let(:collection) { [] }
39
+
40
+ it { should be(0) }
41
+ end
42
+
43
+ context 'non-emtpy collection' do
44
+ let(:collection) { [[1], [2, 3]] }
45
+
46
+ it { should be(3) }
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ RSpec.describe Mutant::Result::Mutation do
2
+ let(:object) do
3
+ described_class.new(
4
+ mutation: mutation,
5
+ test_result: test_result
6
+ )
7
+ end
8
+
9
+ let(:mutation) do
10
+ instance_double(
11
+ Mutant::Mutation,
12
+ frozen?: true,
13
+ class: class_double(Mutant::Mutation)
14
+ )
15
+ end
16
+
17
+ let(:test_result) do
18
+ instance_double(
19
+ Mutant::Result::Test,
20
+ runtime: 1.0
21
+ )
22
+ end
23
+
24
+ let(:mutation_subject) do
25
+ instance_double(
26
+ Mutant::Subject
27
+ )
28
+ end
29
+
30
+ describe '#runtime' do
31
+ subject { object.runtime }
32
+
33
+ it { should eql(1.0) }
34
+ end
35
+
36
+ describe '#success?' do
37
+ subject { object.success? }
38
+
39
+ let(:result) { double('result boolean') }
40
+
41
+ before do
42
+ expect(mutation.class).to receive(:success?)
43
+ .with(test_result)
44
+ .and_return(result)
45
+ end
46
+
47
+ it { should be(result) }
48
+ end
49
+ end
@@ -7,38 +7,103 @@ RSpec.describe Mutant::Result::Subject do
7
7
  )
8
8
  end
9
9
 
10
- let(:mutation_subject) { instance_double(Mutant::Subject) }
10
+ let(:mutation_subject) do
11
+ instance_double(
12
+ Mutant::Subject,
13
+ mutations: mutation_results.map { instance_double(Mutant::Mutation) }
14
+ )
15
+ end
11
16
 
12
- describe '#continue?' do
13
- subject { object.continue? }
17
+ shared_context 'full coverage' do
18
+ let(:mutation_results) do
19
+ [
20
+ instance_double(Mutant::Result::Mutation, success?: true)
21
+ ]
22
+ end
23
+ end
14
24
 
15
- context 'when mutation results are empty' do
16
- let(:mutation_results) { [] }
25
+ shared_context 'partial coverage' do
26
+ let(:mutation_results) do
27
+ [
28
+ instance_double(Mutant::Result::Mutation, success?: false),
29
+ instance_double(Mutant::Result::Mutation, success?: true)
30
+ ]
31
+ end
32
+ end
17
33
 
18
- it { should be(true) }
34
+ shared_context 'no coverage' do
35
+ let(:mutation_results) do
36
+ [
37
+ instance_double(Mutant::Result::Mutation, success?: false)
38
+ ]
19
39
  end
40
+ end
41
+
42
+ shared_context 'no results' do
43
+ let(:mutation_results) { [] }
44
+ end
20
45
 
21
- context 'with failing mutation result' do
22
- let(:mutation_results) { [instance_double(Mutant::Result::Mutation, success?: false)] }
46
+ describe '#coverage' do
47
+ subject { object.coverage }
23
48
 
24
- it { should be(false) }
49
+ {
50
+ 'full coverage' => 1r,
51
+ 'partial coverage' => 0.5r,
52
+ 'no coverage' => 0r,
53
+ 'no results' => 1r
54
+ }.each do |name, expected|
55
+ context(name) do
56
+ include_context(name)
57
+ it { should eql(expected) }
58
+ end
25
59
  end
60
+ end
26
61
 
27
- context 'with successful mutation result' do
28
- let(:mutation_results) { [instance_double(Mutant::Result::Mutation, success?: true)] }
62
+ describe '#amount_mutations' do
63
+ subject { object.amount_mutations }
29
64
 
30
- it { should be(true) }
65
+ {
66
+ 'full coverage' => 1,
67
+ 'partial coverage' => 2,
68
+ 'no coverage' => 1,
69
+ 'no results' => 0
70
+ }.each do |name, expected|
71
+ context(name) do
72
+ include_context(name)
73
+ it { should be(expected) }
74
+ end
31
75
  end
76
+ end
77
+
78
+ describe '#amount_mutations_alive' do
79
+ subject { object.amount_mutations_alive }
32
80
 
33
- context 'with failed and successful mutation result' do
34
- let(:mutation_results) do
35
- [
36
- instance_double(Mutant::Result::Mutation, success?: true),
37
- instance_double(Mutant::Result::Mutation, success?: false)
38
- ]
81
+ {
82
+ 'full coverage' => 0,
83
+ 'partial coverage' => 1,
84
+ 'no coverage' => 1,
85
+ 'no results' => 0
86
+ }.each do |name, expected|
87
+ context(name) do
88
+ include_context(name)
89
+ it { should be(expected) }
39
90
  end
91
+ end
92
+ end
40
93
 
41
- it { should be(false) }
94
+ describe '#success?' do
95
+ subject { object.success? }
96
+
97
+ {
98
+ 'full coverage' => true,
99
+ 'partial coverage' => false,
100
+ 'no coverage' => false,
101
+ 'no results' => true
102
+ }.each do |name, expected|
103
+ context(name) do
104
+ include_context(name)
105
+ it { should be(expected) }
106
+ end
42
107
  end
43
108
  end
44
109
  end