Skip to content

Commit f25ef64

Browse files
committed
Initial.
0 parents  commit f25ef64

File tree

6 files changed

+267
-0
lines changed

6 files changed

+267
-0
lines changed

Gemfile

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
source :rubygems
2+
3+
gemspec

Gemfile.lock

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
PATH
2+
remote: .
3+
specs:
4+
resque-mock (1.0.0)
5+
resque
6+
7+
GEM
8+
remote: http://rubygems.org/
9+
specs:
10+
diff-lcs (1.1.2)
11+
json (1.5.1)
12+
rack (1.3.0)
13+
rake (0.9.2)
14+
redis (2.2.1)
15+
redis-namespace (1.0.3)
16+
redis (< 3.0.0)
17+
resque (1.17.1)
18+
json (< 1.6, >= 1.4.6)
19+
redis-namespace (~> 1.0.2)
20+
sinatra (>= 0.9.2)
21+
vegas (~> 0.1.2)
22+
rspec (2.6.0)
23+
rspec-core (~> 2.6.0)
24+
rspec-expectations (~> 2.6.0)
25+
rspec-mocks (~> 2.6.0)
26+
rspec-core (2.6.3)
27+
rspec-expectations (2.6.0)
28+
diff-lcs (~> 1.1.2)
29+
rspec-mocks (2.6.0)
30+
sinatra (1.2.6)
31+
rack (~> 1.1)
32+
tilt (< 2.0, >= 1.2.2)
33+
tilt (1.3.2)
34+
vegas (0.1.8)
35+
rack (>= 1.0.0)
36+
37+
PLATFORMS
38+
ruby
39+
40+
DEPENDENCIES
41+
rake
42+
resque-mock!
43+
rspec

Rakefile

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
begin
2+
require 'rspec/core/rake_task'
3+
RSpec::Core::RakeTask.new do |t|
4+
t.rspec_opts = %w[ -c ]
5+
t.pattern = 'spec/**/*_spec.rb'
6+
end
7+
8+
task :default => :spec
9+
rescue LoadError
10+
end

lib/resque/mock.rb

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
require 'resque'
2+
3+
module Resque
4+
def self.mock!
5+
extend MockExt
6+
end
7+
8+
module MockExt
9+
def async
10+
@async = true
11+
create_worker_manager
12+
yield
13+
ensure
14+
wait_for_worker_manager
15+
@async = false
16+
end
17+
18+
def enqueue(klass, *args)
19+
puts "Mock enqueue: async=#{!!@async}, stack_depth=#{caller.size}, #{klass}, #{args.inspect}" if ENV['VERBOSE']
20+
defer(klass, args)
21+
end
22+
23+
def enqueue_in(delay, klass, *args)
24+
puts "Mock enqueue in #{delay}: async=#{!!@async}, stack_depth=#{caller.size}, #{klass}, #{args.inspect}" if ENV['VERBOSE']
25+
defer(klass, args, delay)
26+
end
27+
28+
def defer(klass, args, delay = nil)
29+
if @async
30+
add_job('payload' => { 'class' => klass, 'args' => args }, 'delay' => delay)
31+
else
32+
sleep delay if delay
33+
klass.perform(*roundtrip(args))
34+
end
35+
end
36+
37+
def create_worker_manager
38+
@worker_manager = Thread.new do
39+
Thread.current.abort_on_exception = true
40+
worker_threads = []
41+
42+
while true
43+
break if Thread.current[:exit] && worker_threads.empty? && Thread.current[:jobs].empty?
44+
45+
worker_threads.reject! {|t| !t.alive? }
46+
47+
while Thread.current[:jobs] && job_data = Thread.current[:jobs].shift
48+
worker_threads << create_worker_thread_for(job_data)
49+
end
50+
51+
sleep 0.5
52+
end
53+
end.tap {|t| t[:jobs] = [] }
54+
end
55+
56+
def wait_for_worker_manager
57+
@worker_manager[:exit] = true
58+
@worker_manager.join
59+
@worker_manager = nil
60+
end
61+
62+
def create_worker_thread_for(data)
63+
Thread.new(data) do |data|
64+
Thread.current.abort_on_exception = true
65+
if delay = data['delay']
66+
sleep delay
67+
end
68+
69+
klass = data['payload']['class']
70+
puts "Mock perform: #{klass}.perform(*#{data['payload']['args'].inspect})" if ENV['VERBOSE']
71+
klass.perform(*roundtrip(data['payload']['args']))
72+
puts "Mock exit: #{klass}.perform(*#{data['payload']['args'].inspect})" if ENV['VERBOSE']
73+
end
74+
end
75+
76+
def roundtrip(args)
77+
decode(encode(args))
78+
end
79+
80+
def add_job(data)
81+
@worker_manager[:jobs] << data
82+
end
83+
end
84+
end

resque-mock.gemspec

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Gem::Specification.new do |gem|
2+
gem.authors = ["Dan Peterson"]
3+
gem.email = ["[email protected]"]
4+
gem.description = gem.summary = %q{Mock resque with threads}
5+
gem.homepage = 'https://github.com/dpiddy/resque-mock'
6+
7+
gem.files = ['lib/resque/mock.rb']
8+
gem.test_files = ['Rakefile'] + Dir['spec/**.rb']
9+
gem.name = "resque-mock"
10+
gem.require_paths = ['lib']
11+
gem.version = '0.1.0.pre'
12+
13+
gem.add_runtime_dependency 'resque'
14+
gem.add_development_dependency 'rake'
15+
gem.add_development_dependency 'rspec'
16+
end

spec/mock_spec.rb

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#require 'spec_helper'
2+
require 'resque/mock'
3+
4+
Resque.mock!
5+
6+
class Performer
7+
def self.run?
8+
!!@args
9+
end
10+
11+
def self.args
12+
@args
13+
end
14+
15+
def self.runs
16+
@runs || 0
17+
end
18+
19+
def self.perform(*args)
20+
@runs += 1
21+
if Hash === (options = args.first)
22+
if runs_left = options['runs']
23+
runs_left -= 1
24+
if runs_left > 0
25+
Resque.enqueue(self, 'runs' => runs_left)
26+
end
27+
end
28+
end
29+
@args = args
30+
end
31+
32+
def self.reset!
33+
@args = nil
34+
@runs = 0
35+
end
36+
end
37+
38+
class BadPerformer < Performer
39+
def self.perform(*args)
40+
raise 'hello'
41+
end
42+
end
43+
44+
describe Resque do
45+
before { Performer.reset! }
46+
47+
describe "synchronously" do
48+
it "performs jobs without delay" do
49+
Resque.enqueue(Performer, 'hello', 'there')
50+
Performer.should be_run
51+
Performer.args.should == ['hello', 'there']
52+
end
53+
54+
it "performs jobs with a delay" do
55+
Resque.should_receive(:sleep).with(5)
56+
Resque.enqueue_in(5, Performer, 'hello', 'there')
57+
Performer.should be_run
58+
Performer.args.should == ['hello', 'there']
59+
end
60+
61+
it "can perform more jobs that are queued" do
62+
Resque.enqueue(Performer, 'runs' => 3)
63+
Performer.runs.should == 3
64+
end
65+
66+
it "roundtrips arguments" do
67+
Resque.enqueue(Performer, :hello => :there)
68+
Performer.args.should == [{ 'hello' => 'there' }]
69+
end
70+
end
71+
72+
describe "asynchronously" do
73+
it "performs jobs without delay" do
74+
Resque.async do
75+
Resque.enqueue(Performer, 'hello', 'there')
76+
end
77+
78+
Performer.should be_run
79+
Performer.args.should == ['hello', 'there']
80+
end
81+
82+
it "performs jobs with delay" do
83+
# not immediately sure how to mock this
84+
85+
Resque.async do
86+
Resque.enqueue_in(5, Performer, 'hello', 'there')
87+
end
88+
89+
Performer.should be_run
90+
Performer.args.should == ['hello', 'there']
91+
end
92+
93+
it "can perform more jobs that are queued" do
94+
Resque.async { Resque.enqueue(Performer, 'runs' => 3) }
95+
Performer.runs.should == 3
96+
end
97+
98+
it "roundtrips arguments" do
99+
Resque.async { Resque.enqueue(Performer, :hello => :there) }
100+
Performer.args.should == [{ 'hello' => 'there' }]
101+
end
102+
103+
it "raises errors encountered inside the block" do
104+
expect { Resque.async { raise 'hello' } }.to raise_error
105+
end
106+
107+
it "raises errors encountered by jobs" do
108+
expect { Resque.async { Resque.enqueue(BadPerformer, 5) } }.to raise_error
109+
end
110+
end
111+
end

0 commit comments

Comments
 (0)