Is it possible to test threads in RSpec? For example (FYI: Jukebox is a
module):
it "should have a current_song" do
thread = Thread.new do
Jukebox.play # forks a music process and waits till
completion
end
sleep 0.1
Jukebox.current_song.is_a?(Song)
end
If RSpec can’t test threaded programs, any suggestions how threaded
programs can be tested?
Thanks!
James
On Thursday 01 September 2011 03:45:17 James Lavin wrote:
it "should have a current_song" do
thread = Thread.new do
Jukebox.play # forks a music process and waits till
completion
end
sleep 0.1
Jukebox.current_song.is_a?(Song)
end
You should try putting
thread.join
instead of sleep. That makes the main thread wait for the thread to
return
before going on. See the ri documentation for Thread#join for more
information.
I hope this helps
Stefano
One issue could be scoping. Can the main thread see into the named
thread? Do I need to reference Jukebox in the main thread before playing
it in the named thread?
it "should have a current_song" do
jj = Jukebox
thread = Thread.new do
jj.play_once
end
sleep 0.1
jj.current_song.is_a?(Song)
end
I’ll test and report back.
Stefano C. wrote in post #1019453:
You should try putting
thread.join
instead of sleep. That makes the main thread wait for the thread to
return
before going on. See the ri documentation for Thread#join for more
information.
I hope this helps
Stefano
Thanks for replying, Stefano. But I DON’T want the main thread to wait
for the named thread to return. I want the opposite.
Thread#join keeps the named thread alive to completion. This would help
if the named thread died faster than the main thread’s code executed. In
my case, it’s the opposite. The named thread (playing a “song”)
naturally takes longer than the main thread. So I’m not clear how
Thread#join would help.
My intent (which I should have spelled out) is to have the Jukebox start
(but not finish) playing a “song” (for testing purposes, just a “sleep
0.2” or something) and test that it’s playing while the thread is
running. The “sleep 0.1” makes the main thread wait until the “song”
starts playing before testing that it’s playing.
–James
Thanks again, Stefano.
Your code pretty much matches what I posted originally, but my test was
failing. I think I’ve isolated my problem…
I oversimplified things in my example. My actual program has several
moving parts – Song, UserConfig, Jukebox and system calls – and I
think that’s causing my problem. Here’s what’s supposed to happen:
-
Jukebox is supposed to create a @current_song (which it does)
-
@current_song is asked to play itself (based on @user_config)
-
@current_song then starts a system process (via fork…exec or
Spoon.spawnp) to play the actual mp3/ogg file (which it does)
-
Jukebox is supposed to remember what song it’s playing, but when I
ask Jukebox to tell me the @current_song, it says it’s nil.
It’s as if Jukebox forgets @current_song as soon as it tells
@current_song to play
I didn’t have this problem before I refactored Song out of Jukebox, so
I’ve done something stupid.
I’ll figure this out tomorrow.
–James
On Thursday 01 September 2011 04:18:48 James Lavin wrote:
I hope this helps
Thread#join would help.
My intent (which I should have spelled out) is to have the Jukebox start
(but not finish) playing a “song” (for testing purposes, just a “sleep
0.2” or something) and test that it’s playing while the thread is
running. The “sleep 0.1” makes the main thread wait until the “song”
starts playing before testing that it’s playing.
–James
Ok. Now I understand what you want. Unless I’m missing something, it
should
work.
This is a simple Jukebox class with its play method with a test to check
it.
Not knowing what your Jukebox class does, I can’t say whether there are
key
differences, but the test here passes.
class Jukebox
class << self
attr_reader :current_song
end
def self.play
@current_song = ‘x’
sleep 2
end
end
describe Jukebox do
it “should have a current_song” do
thread = Thread.new do
Jukebox.play
end
sleep 0.1
Jukebox.current_song.should == ‘x’
end
end
Stefano
I believe I found the bug. It was caused by conflicts between my
threads…
In my user interaction thread, Jukebox.skip_song was calling
Jukebox.terminate_current_song which was calling @current_song.terminate
AND explicitly setting “@current_song = nil.”
In my Jukebox looping thread, Jukebox.play_loop was set to automatically
play another song whenever the previous song terminated.
I should have just let the Jukebox looping thread assign a new Song
instance to @current_song. There was no need to also explicitly set
“@current_song = nil”. Doing both sometimes caused a conflict because
one thread would try to stop @current_song but couldn’t find one.
After eliminating “@current_song = nil”, Jukebox has stopped randomly
choking and telling me @current_song is nil. Yay!
Threaded programming can be tricky!
(Tomorrow I’ll try to figure out why Spoon.spawnp isn’t working any more
in JRuby. Way too late now.)
P.S. If you want to try it: “gem install jimmy_jukebox”