Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify testing multithreaded applications #29

Merged
merged 1 commit into from
Dec 20, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading