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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 607569850361ac4088176c89f75ac7f7d732d8f0
4
- data.tar.gz: 3247e38171198bf00c53341447881cb76fa44a3f
3
+ metadata.gz: 90851c1c4c4d7c0f23438bae72fe95f70a680514
4
+ data.tar.gz: 7ac31c28d9329ec976bfac638a48a8fe515e3604
5
5
  SHA512:
6
- metadata.gz: 888a9f29aa202635ece333e17c514ca52c7a28660e0ab8cd7f4397f4f108be3a8d91477812ad48ae970f2808b1516de084f44ff02f743b21174ad2504fb4cdae
7
- data.tar.gz: 5e3faa4096046383aa51274b6f201fb3f1eb416d7f8ca8049505fcf823fd994f051c34dd159256a6926e56401f5270d859b0bad0f13b645101442bfff5e80ef6
6
+ metadata.gz: ab96c53e2d7ded947c58742c06bb5934b1b0f17437fefbe9e510d9ce25a7aea422d115aa420fc0538bd23260b2fd85c20d4aa25cb84e8f82362f08d8156f28c5
7
+ data.tar.gz: db2ace76cbabbb97d18a0a32cfc75f7ad3a014424bf4b3ae52b566f38bf47fe3ff8929edcaf31776390fc52bc9c5a2d42323c6be26fc8c617067179977edeb9a
data/.travis.yml CHANGED
@@ -3,7 +3,6 @@ script: "bundle exec rake ci"
3
3
  env:
4
4
  - TRAVIS=true
5
5
  rvm:
6
- - 1.9.3
7
6
  - 2.0.0
8
7
  - 2.1.4
9
8
  - rbx-2
data/Changelog.md CHANGED
@@ -1,3 +1,8 @@
1
+ # v0.7.2 2014-12-08
2
+
3
+ * Fix synthetic race conditon in actor implementation
4
+ * Fix progressive reporter slowdown
5
+
1
6
  # v0.7.1 2014-12-04
2
7
 
3
8
  * Fix invalid dependencies on rspec for mutant-rspec
data/TODO CHANGED
@@ -1,23 +1,15 @@
1
1
  Code:
2
2
  * Test mutant with dynamically created zombie.
3
- * Log all warnings through reporter, so remove random $stderr.puts calls
4
3
 
5
4
  Mutations:
6
5
  * Add true masgn mutations
7
- * Add binary operator specific mutations (YAY, finally reached this point)
8
6
  * Add some kind of a "do not touch me object" that raises on all messages.
9
7
  It can be used to make sure each literal value is touched.
10
8
  * Replace nil or add "do not touch me object" to literal mutations.
11
9
  * Mutate options on Regexp literals
12
10
  * Add mutations for dynamic regexp symbol and string literals
13
- * Mutate Block catch "def foo(&block)" and block pass "foo(&block)"
14
- * Binary operator mutations
15
11
  * Add timeout to terminate infinite loops
16
12
 
17
- Example of a negative mutation:
18
- Mutations on local variables and arguments prefixed with an underscore would be emitted as
19
- negative mutations that must be alive.
20
-
21
13
  Loader:
22
14
  * Make sure loader does not change visibility of injected mutants
23
15
 
data/config/flay.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 18
3
- total_score: 1141
3
+ total_score: 1200
data/config/reek.yml CHANGED
@@ -37,6 +37,7 @@ FeatureEnvy:
37
37
  - Mutant::Runner::Master#stop_worker
38
38
  - Mutant::Runner::Worker#run_mutation
39
39
  - Mutant::Runner::Worker#handle
40
+ - Mutant::Subject#source_lines
40
41
  IrresponsibleModule:
41
42
  enabled: true
42
43
  exclude: []
@@ -136,9 +137,11 @@ UnusedParameters:
136
137
  UtilityFunction:
137
138
  enabled: true
138
139
  exclude:
140
+ - Mutant::Actor::Env#new_mailbox
139
141
  - Mutant::AST::Sexp#s
140
142
  - Mutant::CLI#reporter
141
143
  - Mutant::Integration::Rspec#parse_example
142
- - Mutant::Meta::Example::Verification#format_mutation # False positive, its a utility
144
+ - Mutant::Meta::Example::Verification#format_mutation
143
145
  - Mutant::Reporter::CLI::Format::Progressive#new_buffer
146
+ - Mutant::Reporter::CLI::Printer::StatusProgressive#object # False positive calls super
144
147
  max_helper_calls: 0
data/lib/mutant.rb CHANGED
@@ -104,7 +104,6 @@ require 'mutant/actor'
104
104
  require 'mutant/actor/receiver'
105
105
  require 'mutant/actor/sender'
106
106
  require 'mutant/actor/mailbox'
107
- require 'mutant/actor/actor'
108
107
  require 'mutant/actor/env'
109
108
  require 'mutant/cache'
110
109
  require 'mutant/delegator'
@@ -212,6 +211,7 @@ require 'mutant/reporter/cli'
212
211
  require 'mutant/reporter/cli/printer'
213
212
  require 'mutant/reporter/cli/tput'
214
213
  require 'mutant/reporter/cli/format'
214
+ require 'mutant/line_trace'
215
215
  require 'mutant/zombifier'
216
216
  require 'mutant/zombifier/file'
217
217
 
@@ -11,23 +11,23 @@ module Mutant
11
11
  # @api private
12
12
  #
13
13
  def spawn
14
- mailbox = Mailbox.new
14
+ mailbox = new_mailbox
15
15
 
16
- thread = thread_root.new do
17
- yield mailbox.actor(thread_root.current)
16
+ thread_root.new do
17
+ yield mailbox
18
18
  end
19
19
 
20
- mailbox.sender(thread)
20
+ mailbox.sender
21
21
  end
22
22
 
23
- # Return an private actor for current thread
23
+ # Return new unbound mailbox
24
24
  #
25
- # @return [Actor::Private]
25
+ # @return [Mailbox]
26
26
  #
27
27
  # @api private
28
28
  #
29
- def current
30
- Mailbox.new.actor(thread_root.current)
29
+ def new_mailbox
30
+ Mailbox.new
31
31
  end
32
32
 
33
33
  end # Env
@@ -2,50 +2,35 @@ module Mutant
2
2
  module Actor
3
3
  # Unbound mailbox
4
4
  class Mailbox
5
+ include Adamantium::Flat, Concord::Public.new(:receiver, :sender)
5
6
 
6
- # Initialize new unbound mailbox
7
+ # Return new mailbox
7
8
  #
8
- # @return [undefined]
9
+ # @return [Mailbox]
9
10
  #
10
11
  # @api private
11
12
  #
12
- def initialize
13
- @mutex = Mutex.new
14
- @messages = []
15
- @receiver = Receiver.new(@mutex, @messages)
16
- freeze
17
- end
18
-
19
- # Return receiver
20
- #
21
- # @return [Receiver]
22
- #
23
- # @api private
24
- #
25
- attr_reader :receiver
13
+ def self.new
14
+ mutex = Mutex.new
15
+ condition_variable = ConditionVariable.new
16
+ messages = []
26
17
 
27
- # Return actor that is able to read mailbox
28
- #
29
- # @param [Thread] thread
30
- #
31
- # @return [Actor]
32
- #
33
- # @api private
34
- #
35
- def actor(thread)
36
- Actor.new(thread, self)
18
+ super(
19
+ Receiver.new(condition_variable, mutex, messages),
20
+ Sender.new(condition_variable, mutex, messages)
21
+ )
37
22
  end
38
23
 
39
- # Return sender to mailbox
24
+ # Return binding for RPC to other actors
40
25
  #
41
- # @param [Thread] thread
26
+ # @param [Actor::Sender] other
42
27
  #
43
- # @return [Sender]
28
+ # @return [Binding]
44
29
  #
45
30
  # @api private
46
31
  #
47
- def sender(thread)
48
- Sender.new(thread, @mutex, @messages)
32
+ def bind(other)
33
+ Binding.new(self, other)
49
34
  end
50
35
 
51
36
  end # Mailbox
@@ -2,7 +2,7 @@ module Mutant
2
2
  module Actor
3
3
  # Receiver side of an actor
4
4
  class Receiver
5
- include Concord.new(:mutex, :mailbox)
5
+ include Adamantium::Flat, Concord.new(:condition_variable, :mutex, :messages)
6
6
 
7
7
  # Receives a message, blocking
8
8
  #
@@ -31,14 +31,12 @@ module Mutant
31
31
  # @api private
32
32
  #
33
33
  def try_blocking_receive
34
- mutex.lock
35
- if mailbox.empty?
36
- mutex.unlock
37
- Thread.stop
38
- Undefined
39
- else
40
- mailbox.shift.tap do
41
- mutex.unlock
34
+ mutex.synchronize do
35
+ if messages.empty?
36
+ condition_variable.wait(mutex)
37
+ Undefined
38
+ else
39
+ messages.shift
42
40
  end
43
41
  end
44
42
  end
@@ -3,7 +3,7 @@ module Mutant
3
3
 
4
4
  # Sender for messages to acting thread
5
5
  class Sender
6
- include Concord.new(:thread, :mutex, :mailbox)
6
+ include Adamantium::Flat, Concord.new(:condition_variable, :mutex, :messages)
7
7
 
8
8
  # Send a message to actor
9
9
  #
@@ -15,8 +15,8 @@ module Mutant
15
15
  #
16
16
  def call(message)
17
17
  mutex.synchronize do
18
- mailbox << message
19
- thread.run
18
+ messages << message
19
+ condition_variable.signal
20
20
  end
21
21
 
22
22
  self
@@ -0,0 +1,34 @@
1
+ module Mutant
2
+ # Line tracer
3
+ class LineTrace
4
+ include Adamantium::Flat, Concord.new(:contents)
5
+
6
+ private_class_method :new
7
+
8
+ # Test if trace coveres file at lineno
9
+ #
10
+ # @param [String] file
11
+ # @param [Fixnum] lineno
12
+ #
13
+ # @return [Boolean]
14
+ #
15
+ def cover?(file, lineno)
16
+ contents.fetch(file) { return false }.include?(lineno)
17
+ end
18
+
19
+ # Run block
20
+ #
21
+ # @return [Traces]
22
+ #
23
+ # @api private
24
+ #
25
+ def self.call(&block)
26
+ traces = Hash.new { |hash, file| hash[file] = Set.new }
27
+ TracePoint.trace(:return, :line) do |point|
28
+ traces[point.path] << point.lineno
29
+ end.tap(&block).disable
30
+ new(IceNine.deep_freeze(traces))
31
+ end
32
+
33
+ end # LineTrace
34
+ end # Mutant
@@ -51,6 +51,23 @@ module Mutant
51
51
  self
52
52
  end
53
53
 
54
+ # Return report delay in seconds
55
+ #
56
+ # TODO: Move this to a callback registration
57
+ #
58
+ # Reporters other than CLI that might exist in futures
59
+ # may only the final report. So providing a noop callback
60
+ # registration makes more sense than. As only CLI reporters
61
+ # exist currently I do not really care right now.
62
+ #
63
+ # @return [Float]
64
+ #
65
+ # @api private
66
+ #
67
+ def delay
68
+ format.delay
69
+ end
70
+
54
71
  # Report warning
55
72
  #
56
73
  # @param [String] message
@@ -25,6 +25,16 @@ module Mutant
25
25
  #
26
26
  abstract_method :progress
27
27
 
28
+ # Return report delay in seconds
29
+ #
30
+ # @return [Float]
31
+ #
32
+ # @api private
33
+ #
34
+ def delay
35
+ self.class::REPORT_DELAY
36
+ end
37
+
28
38
  # Output abstraction to decouple tty? from buffer
29
39
  class Output
30
40
  include Concord.new(:tty, :buffer)
@@ -67,17 +77,8 @@ module Mutant
67
77
  # Format for progressive non rewindable output
68
78
  class Progressive < self
69
79
 
70
- # Initialize object
71
- #
72
- # @return [undefined]
73
- #
74
- # @api private
75
- #
76
- def initialize(*)
77
- @seen = Set.new
78
-
79
- super
80
- end
80
+ REPORT_FREQUENCY = 1.0
81
+ REPORT_DELAY = 1 / REPORT_FREQUENCY
81
82
 
82
83
  # Return start representation
83
84
  #
@@ -96,12 +97,7 @@ module Mutant
96
97
  # @api private
97
98
  #
98
99
  def progress(status)
99
- current = status.env_result.subject_results.flat_map(&:mutation_results)
100
- new = current.reject(&@seen.method(:include?))
101
- @seen = current.to_set
102
- new.map do |mutation_result|
103
- format(Printer::MutationProgressResult, mutation_result)
104
- end.join(EMPTY_STRING)
100
+ format(Printer::StatusProgressive, status)
105
101
  end
106
102
 
107
103
  private
@@ -124,6 +120,9 @@ module Mutant
124
120
 
125
121
  BUFFER_FLAGS = 'a+'.freeze
126
122
 
123
+ REPORT_FREQUENCY = 20.0
124
+ REPORT_DELAY = 1 / REPORT_FREQUENCY
125
+
127
126
  # Format start
128
127
  #
129
128
  # @param [Env] env
@@ -147,7 +147,7 @@ module Mutant
147
147
  #
148
148
  def run
149
149
  visit(EnvProgress, object.env_result)
150
- info('Active subjects: %d', active_subject_results.length)
150
+ info('Active subjects: %d', active_subject_results.length)
151
151
  visit_collection(SubjectProgress, active_subject_results)
152
152
  job_status
153
153
  self
@@ -198,12 +198,12 @@ module Mutant
198
198
  #
199
199
  def run
200
200
  info 'Mutant configuration:'
201
- info 'Matcher: %s', object.matcher_config.inspect
202
- info 'Integration: %s', object.integration.name
203
- info 'Expect Coverage: %0.2f%%', object.expected_coverage.inspect
204
- info 'Jobs: %d', object.jobs
205
- info 'Includes: %s', object.includes.inspect
206
- info 'Requires: %s', object.requires.inspect
201
+ info 'Matcher: %s', object.matcher_config.inspect
202
+ info 'Integration: %s', object.integration.name
203
+ info 'Expect Coverage: %0.2f%%', object.expected_coverage.inspect
204
+ info 'Jobs: %d', object.jobs
205
+ info 'Includes: %s', object.includes.inspect
206
+ info 'Requires: %s', object.requires.inspect
207
207
  self
208
208
  end
209
209
 
@@ -232,16 +232,15 @@ module Mutant
232
232
  #
233
233
  def run
234
234
  visit(Config, env.config)
235
- info 'Available Subjects: %s', amount_subjects
236
- info 'Subjects: %s', amount_subjects
237
- info 'Mutations: %s', amount_mutations
238
- info 'Kills: %s', amount_mutations_killed
239
- info 'Alive: %s', amount_mutations_alive
240
- info 'Runtime: %0.2fs', runtime
241
- info 'Killtime: %0.2fs', killtime
242
- info 'Overhead: %0.2f%%', overhead_percent
243
- status 'Coverage: %0.2f%%', coverage_percent
244
- status 'Expected: %0.2f%%', env.config.expected_coverage
235
+ info 'Subjects: %s', amount_subjects
236
+ info 'Mutations: %s', amount_mutations
237
+ info 'Kills: %s', amount_mutations_killed
238
+ info 'Alive: %s', amount_mutations_alive
239
+ info 'Runtime: %0.2fs', runtime
240
+ info 'Killtime: %0.2fs', killtime
241
+ info 'Overhead: %0.2f%%', overhead_percent
242
+ status 'Coverage: %0.2f%%', coverage_percent
243
+ status 'Expected: %0.2f%%', env.config.expected_coverage
245
244
  self
246
245
  end
247
246
 
@@ -342,6 +341,54 @@ module Mutant
342
341
 
343
342
  end # MutationProgressResult
344
343
 
344
+ # Reporter for progressive output format on scheduler Status objects
345
+ class StatusProgressive < self
346
+
347
+ FORMAT = '(%02d/%02d) %3d%% - killtime: %0.02fs runtime: %0.02fs overhead: %0.02fs'.freeze
348
+
349
+ delegate(
350
+ :coverage,
351
+ :runtime,
352
+ :amount_mutations_killed,
353
+ :amount_mutations,
354
+ :amount_mutation_results,
355
+ :killtime,
356
+ :overhead
357
+ )
358
+
359
+ # Run printer
360
+ #
361
+ # @return [self]
362
+ #
363
+ # @api private
364
+ #
365
+ def run
366
+ status(
367
+ FORMAT,
368
+ amount_mutations_killed,
369
+ amount_mutations,
370
+ coverage * 100,
371
+ killtime,
372
+ runtime,
373
+ overhead
374
+ )
375
+
376
+ self
377
+ end
378
+
379
+ private
380
+
381
+ # Return object being printed
382
+ #
383
+ # @return [Result::Env]
384
+ #
385
+ # @api private
386
+ #
387
+ def object
388
+ super().env_result
389
+ end
390
+ end
391
+
345
392
  # Reporter for subject progress
346
393
  class SubjectProgress < self
347
394