mutant 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
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