Skip to content

Commit

Permalink
Merge pull request #29 from jlong/multithread-test
Browse files Browse the repository at this point in the history
Simplify testing multithreaded applications
  • Loading branch information
saturnflyer authored Dec 20, 2024
2 parents 9a0ceaa + 2548bcf commit 87f5ddc
Showing 1 changed file with 40 additions and 42 deletions.
82 changes: 40 additions & 42 deletions test/multithreaded_test.rb
Original file line number Diff line number Diff line change
@@ -1,63 +1,61 @@
require 'thread'
require 'test_helper'
require 'radius'
require 'etc'

class MultithreadTest < Minitest::Test

def setup
Thread.abort_on_exception
Thread.abort_on_exception = true
@context = Radius::Context.new do |c|
c.define_tag('thread') do |tag|
"#{tag.locals.thread_id} / #{tag.globals.object_id}"
end
end
end

if RUBY_PLATFORM == 'java'
require 'java'
# call once before the thread to keep from using hidden require in a thread
Radius::Parser.new
def test_runs_multithreaded
lock = java.lang.String.new("lock")
threads = []
1000.times do |t|
thread = Thread.new do
parser = Radius::Parser.new(@context, :tag_prefix => 'r')
parser.context.globals.thread_id = Thread.current.object_id
expected = "#{Thread.current.object_id} / "+
"#{parser.context.globals.object_id}"
actual = parser.parse('<r:thread />')
assert_equal expected, actual
end
lock.synchronized do
threads << thread
def test_runs_multithreaded
thread_count = [Etc.nprocessors, 16].min
iterations_per_thread = 500
failures = Queue.new
results = Array.new(thread_count) { [] }
threads = []

thread_count.times do |i|
threads << Thread.new do
local_results = []
iterations_per_thread.times do
begin
parser = Radius::Parser.new(@context, :tag_prefix => 'r')
parser.context.globals.thread_id = Thread.current.object_id
expected = "#{Thread.current.object_id} / #{parser.context.globals.object_id}"
result = parser.parse('<r:thread />')

local_results << result

failures << "Expected: #{expected}, Got: #{result}" unless result == expected
rescue => e
failures << "Thread #{Thread.current.object_id} failed: #{e.message}\n#{e.backtrace.join("\n")}"
end
end
end
lock.synchronized do
threads.each{|t| t.join }
results[i] = local_results
end
end
else
def test_runs_multithreaded
threads = []
mute = Mutex.new
1000.times do |t|
thread = Thread.new do
parser = Radius::Parser.new(@context, :tag_prefix => 'r')
parser.context.globals.thread_id = Thread.current.object_id
expected = "#{Thread.current.object_id} / "+
"#{parser.context.globals.object_id}"
actual = parser.parse('<r:thread />')
assert_equal expected, actual
end
mute.synchronize do
threads << thread
end
end
mute.synchronize do
threads.each{|t| t.join }
end

threads.each(&:join)

# Only try to show failures if there are any
failure_message = if failures.empty?
nil
else
"Thread failures detected:\n#{failures.size} times:\n#{failures.pop(5).join("\n")}"
end

assert(failures.empty?, failure_message)
total_results = results.flatten.uniq.size
expected_unique_results = thread_count * iterations_per_thread
assert_equal expected_unique_results, total_results,
"Expected #{expected_unique_results} unique results (#{thread_count} threads × #{iterations_per_thread} iterations)"
end

end

0 comments on commit 87f5ddc

Please sign in to comment.