Skip to content

Commit

Permalink
Adding a client / server implementation that runs over DRB
Browse files Browse the repository at this point in the history
  • Loading branch information
tenderlove committed Mar 14, 2018
1 parent d51e314 commit a50f89c
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 0 deletions.
15 changes: 15 additions & 0 deletions lib/minitest.rb
Expand Up @@ -190,6 +190,21 @@ def self.process_args args = [] # :nodoc:
options[:exclude] = a
end

opts.on "-l", "--listen PORT", "Listen on port PORT" do |a|
require 'minitest/drb'
uri = "druby://localhost:#{a}"
number_of_clients = (ENV["N"] || 2).to_i
self.parallel_executor = Minitest::DRb::Server::Executor.new(number_of_clients, uri)
puts "Listening on #{uri}"
end

opts.on "-c", "--connect PORT", "Connect to port PORT" do |a|
require 'minitest/drb'
uri = "druby://localhost:#{a}"
self.parallel_executor = Minitest::DRb::Client::Executor.new(uri)
options[:connect] = a
end

unless extensions.empty?
opts.separator ""
opts.separator "Known extensions: #{extensions.join(", ")}"
Expand Down
115 changes: 115 additions & 0 deletions lib/minitest/drb.rb
@@ -0,0 +1,115 @@
# frozen_string_literal: true

require "drb"

module Minitest
module DRb
class Server
include ::DRb::DRbUndumped

def initialize finish_queue
@work_queue = Queue.new
@finish_queue = finish_queue
end

def record(reporter, result)
reporter.synchronize do
reporter.record(result)
end
end

# Server pushes work on the queue here
def << work
@work_queue << work
end

# Client pops work off the queue here
def pop
@work_queue.pop
end

# Clients call this when they are done working
def finish
@finish_queue << Object.new
end

class Executor
def initialize client_count, uri
@client_count = client_count
@finish_queue = Queue.new
@queue = Server.new @finish_queue
@pool = []

@url = ::DRb.start_service(uri, @queue).uri
@work_finished = nil
end

def start
@work_finished = Thread.new do
# Read from the finish_queue, client_count times. Then we know
# all clients have finished their work
@client_count.times { @finish_queue.pop }
end
end

def << work
@queue << work
end

def shutdown
# Shutdown was called which means all work has been added to the
# work queue. Push `client_count` poison pills on the work queue
# so that each client knows there is no more work to do.
@client_count.times { @queue << nil }

# Wait until all clients have finished their work and call `finish`
# on the server
@work_finished.join
end
end
end

module Client
class Executor
def initialize url
::DRb.stop_service
@url = url
@queue = nil
@reporter = nil
@server_reporter = nil
end

def start
@queue = ::DRbObject.new_with_uri(@url)
end

def << work
@reporter = work.last
# Clients are going to load all test cases, and they will still try
# to queue up all their work, but we don't want the clients queued
# up work, we want the server's queued up work. So we'll just do
# nothing when someone tries to queue work in a client
end

def shutdown
# Loop, running tests until we find a `nil` in the work queue
while job = @queue.pop
klass = job[0]
method = job[1]
@server_reporter ||= job[2]
result = Minitest.run_one_method(klass, method)

# Report the result locally
@reporter.record result

# Report the result back to the server
@queue.record @server_reporter, result
end

# After we've finished all our work, let the server know we're done
@queue.finish
end
end
end
end
end

0 comments on commit a50f89c

Please sign in to comment.