Skip to content

Commit bb0a8b9

Browse files
committed
proc_spy: expose to library users
1 parent b741a62 commit bb0a8b9

File tree

4 files changed

+58
-44
lines changed

4 files changed

+58
-44
lines changed

lib/mpv.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module MPV
1010
require_relative "ass/text"
1111

1212
require_relative "mpv/exceptions"
13+
require_relative "mpv/proc_spy"
1314
require_relative "mpv/multi_queue"
1415
require_relative "mpv/utils"
1516
require_relative "mpv/client"

lib/mpv/proc_spy.rb

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# frozen_string_literal: true
2+
3+
module MPV
4+
# Utility class to test threaded asyncronous code that calls blocks/procs.
5+
# Exposed as production code for the sake of the users who want to TDD their
6+
# mpv Ruby scripts (since it's not trivial code to write).
7+
class ProcSpy
8+
def initialize
9+
@mutex = Mutex.new
10+
@resource = ConditionVariable.new
11+
@queue = []
12+
end
13+
14+
def to_proc
15+
proc do |*args|
16+
@mutex.synchronize do
17+
@queue << args.dup
18+
@resource.signal
19+
end
20+
end
21+
end
22+
23+
DEFAULT_TIMEOUT = 5
24+
25+
# Waits until the spy has been run at least "runs" times or the timeout is
26+
# triggered, and returns the calls performed on the spy
27+
# @return [Array<Array<Object>>] array of calls to the spy, containing
28+
# arguments of each call
29+
def wait(runs: 1, timeout: DEFAULT_TIMEOUT)
30+
start = Concurrent.monotonic_time
31+
@mutex.synchronize do
32+
loop do
33+
break if @queue.size >= runs
34+
break if (Concurrent.monotonic_time - start) >= timeout
35+
36+
@resource.wait(@mutex, 0.05) # sleep 50ms and poll again
37+
end
38+
result = @queue.dup
39+
@queue.clear
40+
result
41+
end
42+
end
43+
44+
# Clears the calls history
45+
def clear!
46+
@mutex.synchronize do
47+
@queue.clear
48+
end
49+
end
50+
end
51+
end

spec/mpv_client_spec.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,15 @@
3232
end
3333

3434
it "can observe properties" do
35-
spy = ProcSpy.new
35+
spy = MPV::ProcSpy.new
3636
@mpv.observe_property(:volume, &spy)
3737
@mpv.set_property(:volume, 10)
3838
result = spy.wait(runs: 2)
3939
expect(result.map(&:first).map(&:data)).to eql([100.0, 10.0])
4040
end
4141

4242
it "can handle client-message" do
43-
spy = ProcSpy.new
43+
spy = MPV::ProcSpy.new
4444
m = "cool-message"
4545
@mpv.register_message_handler(m, &spy)
4646
@mpv.command("script-message", m, "a", "b")
@@ -50,7 +50,7 @@
5050
end
5151

5252
it "can register a binding" do
53-
spy = ProcSpy.new
53+
spy = MPV::ProcSpy.new
5454
section = @mpv.register_keybindings(%w[b c d], &spy)
5555
@mpv.command("keypress", "g")
5656
@mpv.command("keypress", "c")
@@ -74,7 +74,7 @@
7474
end
7575

7676
it "doesn't deadlock" do
77-
spy = ProcSpy.new
77+
spy = MPV::ProcSpy.new
7878
section = @mpv.register_keybindings(%w[b]) do
7979
volume = @mpv.get_property("volume").data
8080
spy.to_proc.call(volume)
@@ -106,7 +106,7 @@
106106
end
107107

108108
it "handles modal keypresses" do
109-
spy = ProcSpy.new
109+
spy = MPV::ProcSpy.new
110110
@mpv.enter_modal_mode("really delete?", %w[y n], &spy)
111111
expect(@mpv.osd_messages.size).to eql(1)
112112
@mpv.command("keypress", "y")
@@ -115,7 +115,7 @@
115115
end
116116

117117
it "handles modal exit key" do
118-
spy = ProcSpy.new
118+
spy = MPV::ProcSpy.new
119119
@mpv.enter_modal_mode("really delete?", %w[y n], &spy)
120120
expect(@mpv.osd_messages.size).to eql(1)
121121
@mpv.command("keypress", "ESC")

spec/spec_helper.rb

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,3 @@
66
def test_instance
77
MPV::Session.new(user_args: %w[--no-config])
88
end
9-
10-
class ProcSpy
11-
def initialize
12-
@mutex = Mutex.new
13-
@resource = ConditionVariable.new
14-
clear
15-
end
16-
17-
def clear
18-
@queue = []
19-
end
20-
21-
def to_proc
22-
proc do |*args|
23-
@mutex.synchronize do
24-
@queue << args.dup
25-
@resource.signal
26-
end
27-
end
28-
end
29-
30-
DEFAULT_TIMEOUT = 5
31-
32-
def wait(runs: 1, timeout: DEFAULT_TIMEOUT)
33-
start = Time.now
34-
@mutex.synchronize do
35-
loop do
36-
break if @queue.size >= runs
37-
break if (Time.now - start) >= timeout
38-
39-
@resource.wait(@mutex, 0.05) # sleep 50ms and poll again
40-
end
41-
end
42-
result = @queue.dup
43-
clear
44-
result
45-
end
46-
end

0 commit comments

Comments
 (0)