mutant 0.7.1 → 0.7.2

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -1
  3. data/Changelog.md +5 -0
  4. data/TODO +0 -8
  5. data/config/flay.yml +1 -1
  6. data/config/reek.yml +4 -1
  7. data/lib/mutant.rb +1 -1
  8. data/lib/mutant/actor/env.rb +8 -8
  9. data/lib/mutant/actor/mailbox.rb +16 -31
  10. data/lib/mutant/actor/receiver.rb +7 -9
  11. data/lib/mutant/actor/sender.rb +3 -3
  12. data/lib/mutant/line_trace.rb +34 -0
  13. data/lib/mutant/reporter/cli.rb +17 -0
  14. data/lib/mutant/reporter/cli/format.rb +16 -17
  15. data/lib/mutant/reporter/cli/printer.rb +64 -17
  16. data/lib/mutant/reporter/trace.rb +12 -0
  17. data/lib/mutant/result.rb +3 -3
  18. data/lib/mutant/runner.rb +2 -5
  19. data/lib/mutant/runner/worker.rb +4 -6
  20. data/lib/mutant/subject.rb +23 -11
  21. data/lib/mutant/subject/method/instance.rb +3 -38
  22. data/lib/mutant/subject/method/singleton.rb +1 -1
  23. data/lib/mutant/version.rb +1 -1
  24. data/mutant.gemspec +1 -1
  25. data/spec/spec_helper.rb +2 -0
  26. data/spec/support/fake_actor.rb +17 -11
  27. data/spec/support/shared_context.rb +0 -2
  28. data/spec/unit/mutant/actor/env_spec.rb +5 -25
  29. data/spec/unit/mutant/actor/mailbox_spec.rb +29 -0
  30. data/spec/unit/mutant/actor/receiver_spec.rb +24 -28
  31. data/spec/unit/mutant/actor/sender_spec.rb +9 -9
  32. data/spec/unit/mutant/line_trace_spec.rb +38 -0
  33. data/spec/unit/mutant/reporter/cli_spec.rb +154 -157
  34. data/spec/unit/mutant/runner/master_spec.rb +11 -11
  35. data/spec/unit/mutant/runner/worker_spec.rb +2 -3
  36. data/spec/unit/mutant/runner_spec.rb +13 -10
  37. data/spec/unit/mutant/subject_spec.rb +17 -2
  38. metadata +51 -50
  39. data/lib/mutant/actor/actor.rb +0 -50
  40. data/spec/unit/mutant/actor/actor_spec.rb +0 -35
  41. data/spec/unit/mutant/mailbox_spec.rb +0 -33
@@ -21,6 +21,18 @@ module Mutant
21
21
  end
22
22
  end
23
23
 
24
+ REPORT_DELAY = 0.0
25
+
26
+ # Return report delay
27
+ #
28
+ # @return [Float]
29
+ #
30
+ # @api private
31
+ #
32
+ def delay
33
+ REPORT_DELAY
34
+ end
35
+
24
36
  end # Tracker
25
37
  end # reporter
26
38
  end # Mutant
data/lib/mutant/result.rb CHANGED
@@ -28,7 +28,7 @@ module Mutant
28
28
  def self.included(host)
29
29
  super
30
30
 
31
- host.memoize :coverage
31
+ host.memoize(:coverage)
32
32
  end
33
33
 
34
34
  end # Coverage
@@ -52,7 +52,7 @@ module Mutant
52
52
  define_method(name) do
53
53
  public_send(collection).map(&name).reduce(0, :+)
54
54
  end
55
- memoize name
55
+ memoize(name)
56
56
  end
57
57
  end # ClassMethods
58
58
 
@@ -248,7 +248,7 @@ module Mutant
248
248
 
249
249
  # Mutation result
250
250
  class Mutation
251
- include Result, Anima.new(:mutation, :test_result, :index)
251
+ include Result, Anima.new(:mutation, :test_result)
252
252
 
253
253
  # Return runtime
254
254
  #
data/lib/mutant/runner.rb CHANGED
@@ -22,9 +22,6 @@ module Mutant
22
22
  include Adamantium::Flat, Anima.new(:job, :result)
23
23
  end
24
24
 
25
- REPORT_FREQUENCY = 20.0
26
- REPORT_DELAY = 1 / REPORT_FREQUENCY
27
-
28
25
  # Initialize object
29
26
  #
30
27
  # @return [undefined]
@@ -37,7 +34,7 @@ module Mutant
37
34
  reporter.start(env)
38
35
  config.integration.setup
39
36
 
40
- @master = config.actor_env.current.bind(Master.call(env))
37
+ @master = config.actor_env.new_mailbox.bind(Master.call(env))
41
38
 
42
39
  status = nil
43
40
 
@@ -45,7 +42,7 @@ module Mutant
45
42
  status = current_status
46
43
  break if status.done
47
44
  reporter.progress(status)
48
- Kernel.sleep(REPORT_DELAY)
45
+ Kernel.sleep(reporter.delay)
49
46
  end
50
47
 
51
48
  reporter.progress(status)
@@ -17,7 +17,7 @@ module Mutant
17
17
  def self.run(attributes)
18
18
  attributes.fetch(:config).actor_env.spawn do |actor|
19
19
  worker = new(attributes)
20
- worker.send(:run, actor)
20
+ worker.__send__(:run, actor)
21
21
  end
22
22
  end
23
23
 
@@ -67,22 +67,20 @@ module Mutant
67
67
  # @api private
68
68
  #
69
69
  def handle_job(job)
70
- parent.call(Actor::Message.new(:result, JobResult.new(job: job, result: run_mutation(job))))
70
+ parent.call(Actor::Message.new(:result, JobResult.new(job: job, result: run_mutation(job.mutation))))
71
71
  end
72
72
 
73
73
  # Run mutation
74
74
  #
75
75
  # @param [Mutation] mutation
76
76
  #
77
- # @return [Report::Mutation]
77
+ # @return [Result::Mutation]
78
78
  #
79
79
  # @api private
80
80
  #
81
- def run_mutation(job)
82
- mutation = job.mutation
81
+ def run_mutation(mutation)
83
82
  test_result = mutation.kill(config.isolation, config.integration)
84
83
  Result::Mutation.new(
85
- index: job.index,
86
84
  mutation: mutation,
87
85
  test_result: test_result
88
86
  )
@@ -13,7 +13,9 @@ module Mutant
13
13
  #
14
14
  def mutations
15
15
  mutations = [neutral_mutation]
16
- generate_mutations(mutations)
16
+ Mutator.each(node) do |mutant|
17
+ mutations << Mutation::Evil.new(self, wrap_node(mutant))
18
+ end
17
19
  mutations
18
20
  end
19
21
  memoize :mutations
@@ -56,6 +58,18 @@ module Mutant
56
58
  self
57
59
  end
58
60
 
61
+ # Return source lines
62
+ #
63
+ # @return [Range<Fixnum>]
64
+ #
65
+ # @api private
66
+ #
67
+ def source_lines
68
+ expression = node.location.expression
69
+ expression.line..expression.source_buffer.decompose_position(expression.end_pos).first
70
+ end
71
+ memoize :source_lines
72
+
59
73
  # Return source line
60
74
  #
61
75
  # @return [Fixnum]
@@ -63,7 +77,7 @@ module Mutant
63
77
  # @api private
64
78
  #
65
79
  def source_line
66
- node.location.expression.line
80
+ source_lines.begin
67
81
  end
68
82
 
69
83
  # Return subject identification
@@ -84,7 +98,7 @@ module Mutant
84
98
  # @api private
85
99
  #
86
100
  def source
87
- Unparser.unparse(node)
101
+ Unparser.unparse(wrap_node(node))
88
102
  end
89
103
  memoize :source
90
104
 
@@ -113,21 +127,19 @@ module Mutant
113
127
  # @api private
114
128
  #
115
129
  def neutral_mutation
116
- Mutation::Neutral.new(self, node)
130
+ Mutation::Neutral.new(self, wrap_node(node))
117
131
  end
118
132
 
119
- # Generate mutations
133
+ # Wrap node into subject specific container
120
134
  #
121
- # @param [#<<] emitter
135
+ # @param [Parser::AST::Node] node
122
136
  #
123
- # @return [undefined]
137
+ # @return [Parser::AST::Node]
124
138
  #
125
139
  # @api private
126
140
  #
127
- def generate_mutations(emitter)
128
- Mutator.each(node) do |mutant|
129
- emitter << Mutation::Evil.new(self, mutant)
130
- end
141
+ def wrap_node(node)
142
+ node
131
143
  end
132
144
 
133
145
  end # Subject
@@ -25,7 +25,7 @@ module Mutant
25
25
  # @api private
26
26
  #
27
27
  def prepare
28
- scope.send(:undef_method, name)
28
+ scope.__send__(:undef_method, name)
29
29
  self
30
30
  end
31
31
 
@@ -33,17 +33,6 @@ module Mutant
33
33
  class Memoized < self
34
34
  include AST::Sexp
35
35
 
36
- # Return source
37
- #
38
- # @return [String]
39
- #
40
- # @api private
41
- #
42
- def source
43
- Unparser.unparse(memoizer_node(node))
44
- end
45
- memoize :source
46
-
47
36
  # Prepare subject for mutation insertion
48
37
  #
49
38
  # @return [self]
@@ -51,37 +40,13 @@ module Mutant
51
40
  # @api private
52
41
  #
53
42
  def prepare
54
- scope.send(:memoized_methods).instance_variable_get(:@memory).delete(name)
43
+ scope.__send__(:memoized_methods).instance_variable_get(:@memory).delete(name)
55
44
  super
56
45
  self
57
46
  end
58
47
 
59
48
  private
60
49
 
61
- # Return mutations
62
- #
63
- # @param [#<<] emitter
64
- #
65
- # @return [undefined]
66
- #
67
- # @api private
68
- #
69
- def generate_mutations(emitter)
70
- Mutator.each(node) do |mutant|
71
- emitter << Mutation::Evil.new(self, memoizer_node(mutant))
72
- end
73
- end
74
-
75
- # Return neutral mutation
76
- #
77
- # @return [Mutation::Neutral]
78
- #
79
- # @api private
80
- #
81
- def neutral_mutation
82
- Mutation::Neutral.new(self, memoizer_node(node))
83
- end
84
-
85
50
  # Return memoizer node for mutant
86
51
  #
87
52
  # @param [Parser::AST::Node] mutant
@@ -90,7 +55,7 @@ module Mutant
90
55
  #
91
56
  # @api private
92
57
  #
93
- def memoizer_node(mutant)
58
+ def wrap_node(mutant)
94
59
  s(:begin, mutant, s(:send, nil, :memoize, s(:args, s(:sym, name))))
95
60
  end
96
61
 
@@ -25,7 +25,7 @@ module Mutant
25
25
  # @api private
26
26
  #
27
27
  def prepare
28
- scope.singleton_class.send(:undef_method, name)
28
+ scope.singleton_class.__send__(:undef_method, name)
29
29
  self
30
30
  end
31
31
 
@@ -1,4 +1,4 @@
1
1
  module Mutant
2
2
  # The current mutant version
3
- VERSION = '0.7.1'.freeze
3
+ VERSION = '0.7.2'.freeze
4
4
  end # Mutant
data/mutant.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |gem|
21
21
  gem.extra_rdoc_files = %w[TODO LICENSE]
22
22
  gem.executables = %w[mutant]
23
23
 
24
- gem.required_ruby_version = '>= 1.9.3'
24
+ gem.required_ruby_version = '>= 2.0.0'
25
25
 
26
26
  gem.add_runtime_dependency('parser', '~> 2.2.pre.7')
27
27
  gem.add_runtime_dependency('ast', '~> 2.0')
data/spec/spec_helper.rb CHANGED
@@ -11,6 +11,8 @@ if ENV['COVERAGE'] == 'true'
11
11
  add_filter 'lib/mutant/meta/*'
12
12
  add_filter 'lib/mutant/zombifier'
13
13
  add_filter 'lib/mutant/zombifier/*'
14
+ # Trace points shadow each other under 2.0 (fixed in 2.1)
15
+ add_filter 'lib/mutant/line_trace.rb' if RUBY_VERSION.eql?('2.0.0')
14
16
 
15
17
  minimum_coverage 100
16
18
  end
@@ -40,26 +40,32 @@ module FakeActor
40
40
  end
41
41
 
42
42
  class Env
43
- include Concord.new(:messages, :actor_names)
43
+ include Concord.new(:messages, :mailbox_names)
44
44
 
45
45
  def spawn
46
- name = @actor_names.shift
47
- raise 'Tried to spawn actor when no name available' unless name
48
- actor = actor(name)
49
- yield actor if block_given?
50
- actor.sender
46
+ mailbox = mailbox(next_name)
47
+ yield mailbox if block_given?
48
+ mailbox.sender
51
49
  end
52
50
 
53
- def current
54
- actor(:current)
51
+ def mailbox(name)
52
+ Mailbox.new(name, @messages)
55
53
  end
56
54
 
57
- def actor(name)
58
- Actor.new(name, @messages)
55
+ def new_mailbox
56
+ mailbox(:current)
57
+ end
58
+
59
+ private
60
+
61
+ def next_name
62
+ @mailbox_names.shift.tap do |name|
63
+ name or fail 'Tried to spawn actor when no name available'
64
+ end
59
65
  end
60
66
  end # Env
61
67
 
62
- class Actor
68
+ class Mailbox
63
69
  include Concord.new(:name, :messages)
64
70
 
65
71
  def receiver
@@ -85,7 +85,6 @@ module SharedContext
85
85
 
86
86
  let(:mutation_a_result) do
87
87
  Mutant::Result::Mutation.new(
88
- index: 1,
89
88
  mutation: mutation_a,
90
89
  test_result: mutation_a_test_result
91
90
  )
@@ -93,7 +92,6 @@ module SharedContext
93
92
 
94
93
  let(:mutation_b_result) do
95
94
  Mutant::Result::Mutation.new(
96
- index: 1,
97
95
  mutation: mutation_a,
98
96
  test_result: mutation_b_test_result
99
97
  )
@@ -1,32 +1,13 @@
1
1
  RSpec.describe Mutant::Actor::Env do
2
- let(:mutex) { double('Mutex') }
3
- let(:thread) { double('Thread') }
4
- let(:thread_root) { double('Thread Root') }
5
- let(:actor) { Mutant::Actor::Actor.new(thread, mailbox) }
2
+ let(:thread) { double('Thread') }
3
+ let(:thread_root) { double('Thread Root') }
6
4
 
7
5
  let(:object) { described_class.new(thread_root) }
8
6
 
9
- before do
10
- expect(Mutex).to receive(:new).and_return(mutex)
11
- end
12
-
13
- describe '#current' do
14
- subject { object.current }
15
-
16
- let!(:mailbox) { Mutant::Actor::Mailbox.new }
17
-
18
- before do
19
- expect(Mutant::Actor::Mailbox).to receive(:new).and_return(mailbox).ordered
20
- expect(thread_root).to receive(:current).and_return(thread)
21
- end
22
-
23
- it { should eql(actor) }
24
- end
25
-
26
7
  describe '#spawn' do
27
8
  subject { object.spawn(&block) }
28
9
 
29
- let!(:mailbox) { Mutant::Actor::Mailbox.new }
10
+ let!(:mailbox) { Mutant::Actor::Mailbox.new }
30
11
 
31
12
  let(:yields) { [] }
32
13
 
@@ -35,15 +16,14 @@ RSpec.describe Mutant::Actor::Env do
35
16
  before do
36
17
  expect(Mutant::Actor::Mailbox).to receive(:new).and_return(mailbox).ordered
37
18
  expect(thread_root).to receive(:new).and_yield.and_return(thread).ordered
38
- expect(thread_root).to receive(:current).and_return(thread).ordered
39
19
  end
40
20
 
41
21
  it 'returns sender' do
42
- should eql(actor.sender)
22
+ should be(mailbox.sender)
43
23
  end
44
24
 
45
25
  it 'yields actor' do
46
- expect { subject }.to change { yields }.from([]).to([actor])
26
+ expect { subject }.to change { yields }.from([]).to([mailbox])
47
27
  end
48
28
  end
49
29
  end
@@ -0,0 +1,29 @@
1
+ RSpec.describe Mutant::Actor::Mailbox do
2
+ let(:mutex) { double('Mutex') }
3
+ let(:condition_variable) { double('Mutex') }
4
+
5
+ before do
6
+ allow(Mutex).to receive(:new).and_return(mutex)
7
+ allow(ConditionVariable).to receive(:new).and_return(condition_variable)
8
+ end
9
+
10
+ describe '.new' do
11
+ subject { described_class.new }
12
+
13
+ let(:object) { described_class.new }
14
+ let(:thread) { double('Thread') }
15
+
16
+ its(:sender) { should eql(Mutant::Actor::Sender.new(condition_variable, mutex, [])) }
17
+ its(:receiver) { should eql(Mutant::Actor::Receiver.new(condition_variable, mutex, [])) }
18
+
19
+ end
20
+
21
+ describe '#bind' do
22
+ let(:object) { described_class.new }
23
+ let(:other) { double('Sender') }
24
+
25
+ subject { object.bind(other) }
26
+
27
+ it { should eql(Mutant::Actor::Binding.new(object, other)) }
28
+ end
29
+ end
@@ -1,36 +1,34 @@
1
1
  RSpec.describe Mutant::Actor::Receiver do
2
- let(:mailbox) { double('Mailbox') }
3
- let(:mutex) { double('Mutex') }
4
- let(:message) { double('Message') }
2
+ let(:messages) { double('Messages') }
3
+ let(:mutex) { double('Mutex') }
4
+ let(:condition_variable) { double('Condition Variable') }
5
+ let(:message) { double('Message') }
5
6
 
6
- let(:object) { described_class.new(mutex, mailbox) }
7
+ let(:object) { described_class.new(condition_variable, mutex, messages) }
7
8
 
8
9
  describe '#call' do
9
10
  subject { object.call }
10
11
 
11
- context 'when mailbox contains a message' do
12
+ context 'when messages contains a message' do
12
13
  before do
13
- expect(mutex).to receive(:lock).ordered
14
- expect(mailbox).to receive(:empty?).and_return(false).ordered
15
- expect(mailbox).to receive(:shift).and_return(message).ordered
16
- expect(mutex).to receive(:unlock).ordered
14
+ expect(mutex).to receive(:synchronize).and_yield.ordered
15
+ expect(messages).to receive(:empty?).and_return(false).ordered
16
+ expect(messages).to receive(:shift).and_return(message).ordered
17
17
  end
18
18
 
19
19
  it { should be(message) }
20
20
  end
21
21
 
22
- context 'when mailbox initially contains no message' do
22
+ context 'when messages initially contains no message' do
23
23
  before do
24
24
  # 1rst failing try
25
- expect(mutex).to receive(:lock).ordered
26
- expect(mailbox).to receive(:empty?).and_return(true).ordered
27
- expect(mutex).to receive(:unlock).ordered
28
- expect(Thread).to receive(:stop).ordered
25
+ expect(mutex).to receive(:synchronize).and_yield.ordered
26
+ expect(messages).to receive(:empty?).and_return(true).ordered
27
+ expect(condition_variable).to receive(:wait).with(mutex).ordered
29
28
  # 2nd successful try
30
- expect(mutex).to receive(:lock).ordered
31
- expect(mailbox).to receive(:empty?).and_return(false).ordered
32
- expect(mailbox).to receive(:shift).and_return(message).ordered
33
- expect(mutex).to receive(:unlock).ordered
29
+ expect(mutex).to receive(:synchronize).and_yield.ordered
30
+ expect(messages).to receive(:empty?).and_return(false).ordered
31
+ expect(messages).to receive(:shift).and_return(message).ordered
34
32
  end
35
33
 
36
34
  it 'waits for message' do
@@ -38,21 +36,19 @@ RSpec.describe Mutant::Actor::Receiver do
38
36
  end
39
37
  end
40
38
 
41
- context 'when mailbox contains no message but thread gets waken without message arrived' do
39
+ context 'when messages contains no message but thread gets waken without message arrived' do
42
40
  before do
43
41
  # 1rst failing try
44
- expect(mutex).to receive(:lock).ordered
45
- expect(mailbox).to receive(:empty?).and_return(true).ordered
46
- expect(mutex).to receive(:unlock).ordered
47
- expect(Thread).to receive(:stop).ordered
42
+ expect(mutex).to receive(:synchronize).and_yield.ordered
43
+ expect(messages).to receive(:empty?).and_return(true).ordered
44
+ expect(condition_variable).to receive(:wait).with(mutex).ordered
48
45
  # 2nd failing try
49
- expect(mutex).to receive(:lock).ordered
50
- expect(mailbox).to receive(:empty?).and_return(true).ordered
51
- expect(mutex).to receive(:unlock).ordered
52
- expect(Thread).to receive(:stop).ordered
46
+ expect(mutex).to receive(:synchronize).and_yield.ordered
47
+ expect(messages).to receive(:empty?).and_return(true).ordered
48
+ expect(condition_variable).to receive(:wait).with(mutex).ordered
53
49
  end
54
50
 
55
- it 'waits for message' do
51
+ it 'fails with error' do
56
52
  expect { subject }.to raise_error(Mutant::Actor::ProtocolError)
57
53
  end
58
54
  end