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