lib/rubyqc/modifier.rb in rubyqc-0.0.2 vs lib/rubyqc/modifier.rb in rubyqc-0.0.3
- old
+ new
@@ -1,21 +1,65 @@
+require 'rubyqc/error'
+
module RubyQC
- class Modifier
+ class Modifier < Struct.new(:args, :errors, :cases, :threads)
def initialize args, &block
- @args = args
- @times = 10
+ super(args, [], RubyQC.default_times, RubyQC.default_parallel)
run(&block)
end
def times t, &block
- @times = t
+ raise ArgumentError.new(
+ "Must run at least once, but got: #{t}") if t <= 0
+ self.cases = t
run(&block)
end
- def run
- @times.times{
- yield(*@args.map(&:rubyqc))
- } if block_given?
+ def parallel t, &block
+ raise ArgumentError.new(
+ "Must have at least 1 thread, but got: #{t}") if t <= 0
+ self.threads = t
+ run(&block)
+ end
+
+ private
+ def mutex
+ @mutex ||= Mutex.new
+ end
+
+ def run &block
+ if !block_given?
+ # waiting for block to be given
+ elsif threads == 1
+ run_thread(cases, &block)
+ else
+ divided = cases / threads
+ mod = cases % threads
+
+ ts = (threads - 1).times.map{
+ Thread.new{ run_thread(divided, &block) }
+ } + [Thread.new{ run_thread(divided + mod, &block) }]
+ ts.each(&:join)
+
+ raise Error.new(cases, errors) unless errors.empty?
+ end
+
+ self
+ end
+
+ def run_thread t
+ t.times do
+ if Thread.current == Thread.main
+ # we raise errors immediately if we're not running in parallels
+ yield(*args.map(&:rubyqc))
+ else
+ begin
+ yield(*args.map(&:rubyqc))
+ rescue Exception => e
+ mutex.synchronize{ errors << e }
+ end
+ end
+ end
end
end
end