em-zmq-tp10 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ module EventMachine
2
+ module Protocols
3
+ module Zmq2
4
+ VERSION = '0.1.7'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,2 @@
1
+ require 'em/protocols/zmq2'
2
+ EventMachine::Protocols::ZmqTP10 = EventMachine::Protocols::Zmq2
data/tests/helper.rb ADDED
@@ -0,0 +1,102 @@
1
+ require 'ffi-rzmq'
2
+ module Native
3
+ extend self
4
+ ZBIND_ADDR = 'tcp://127.0.0.1:7890'
5
+ ZCONNECT_ADDR = 'tcp://127.0.0.1:7891'
6
+
7
+ def native_context
8
+ @zctx = ZMQ::Context.new
9
+ end
10
+
11
+ def with_context
12
+ native_context
13
+ yield
14
+ ensure
15
+ close_context
16
+ end
17
+
18
+ def close_context
19
+ @zctx.terminate
20
+ end
21
+
22
+ def setup_pair(kind)
23
+ kind_n = ZMQ.const_get(kind)
24
+ @zbind = @zctx.socket(kind_n)
25
+ @zbind.identity = "BIND_#{kind}"
26
+ @zbind.bind(ZBIND_ADDR)
27
+ @zconnect = @zctx.socket(kind_n)
28
+ @zconnect.identity = "CONNECT_#{kind}"
29
+ @zconnect.connect(ZCONNECT_ADDR)
30
+ [@zbind, @zconnect]
31
+ end
32
+
33
+ def with_socket_pair(kind)
34
+ with_context do
35
+ begin
36
+ yield setup_pair(kind)
37
+ ensure
38
+ close_pair
39
+ end
40
+ end
41
+ end
42
+
43
+ def close_pair
44
+ @zbind.setsockopt(ZMQ::LINGER, 0)
45
+ @zconnect.setsockopt(ZMQ::LINGER, 0)
46
+ @zbind.close
47
+ @zconnect.close
48
+ end
49
+
50
+ def setup_socket(kind)
51
+ kind_n = ZMQ.const_get(kind)
52
+ @zbind = @zctx.socket(kind_n)
53
+ @zbind.identity = "BIND_#{kind}"
54
+ @zbind.bind(ZBIND_ADDR)
55
+ @zbind
56
+ end
57
+
58
+ def with_socket(kind)
59
+ with_context do
60
+ begin
61
+ yield setup_socket(kind)
62
+ ensure
63
+ close_socket
64
+ end
65
+ end
66
+ end
67
+
68
+ def close_socket
69
+ @zbind.setsockopt(ZMQ::LINGER, 0)
70
+ @zbind.close
71
+ end
72
+ end
73
+
74
+ module DeferredMixin
75
+ def initialize(opts={})
76
+ super(opts)
77
+ @connected = opts[:connected]
78
+ @finished = opts[:finished]
79
+ end
80
+
81
+ attr_writer :connected, :finished
82
+
83
+ def peer_free(peer_ident, connection)
84
+ super
85
+ if @connected && @free_peers.size == 2
86
+ @connected, connected = nil, @connected
87
+ connected.succeed
88
+ end
89
+ end
90
+ end
91
+
92
+ module IncomingMixin
93
+ attr :incoming_queue
94
+ def initialize(opts={})
95
+ super
96
+ @incoming_queue = []
97
+ end
98
+
99
+ def receive_message(message)
100
+ @incoming_queue << message
101
+ end
102
+ end
data/tests/run_all.rb ADDED
@@ -0,0 +1,3 @@
1
+ %w{req router dealer rep}.each do |what|
2
+ require File.expand_path("../test_#{what}.rb", __FILE__)
3
+ end
@@ -0,0 +1,237 @@
1
+ require 'eventmachine'
2
+ require 'minitest/autorun'
3
+ require File.expand_path('../helper.rb', __FILE__)
4
+
5
+ require 'em/protocols/zmq2/dealer'
6
+
7
+ describe 'Dealer' do
8
+
9
+ let(:connected) do
10
+ EM::DefaultDeferrable.new
11
+ end
12
+
13
+ describe EM::Protocols::Zmq2::PreDealer do
14
+ class MyPreDealer < EM::Protocols::Zmq2::PreDealer
15
+
16
+ attr :incoming_queue
17
+ def initialize(opts={})
18
+ super(opts)
19
+ @connected = opts[:connected]
20
+ @incoming_queue = []
21
+ end
22
+ def peer_free(peer_ident, connection)
23
+ super
24
+ @connected.succeed if @free_peers.size == 2
25
+ end
26
+ def receive_message(message)
27
+ @incoming_queue << message
28
+ end
29
+ end
30
+
31
+ attr :dealer
32
+ before do
33
+ @dealer = MyPreDealer.new(identity: 'MyDealer', connected: connected)
34
+ @dealer.connect(Native::ZBIND_ADDR)
35
+ @dealer.bind(Native::ZCONNECT_ADDR)
36
+ end
37
+
38
+ let(:messages) do
39
+ 64.times.map{|n| ['', 'hello', 'world', n.to_s]}
40
+ end
41
+
42
+ it "should be able to send message" do
43
+ results = []
44
+ Native.with_socket_pair('ROUTER') do |zbind, zconnect|
45
+ EM.run {
46
+ connected.callback {
47
+ messages.each{|message|
48
+ dealer.send_message(message).must_equal true
49
+ }
50
+ dealer.close do
51
+ EM.next_tick{ EM.stop }
52
+ end
53
+ }
54
+ }
55
+ result = []
56
+ while zbind.recv_strings(result, ZMQ::NOBLOCK) != -1
57
+ result.shift.must_equal 'MyDealer'
58
+ results << result
59
+ result = []
60
+ end
61
+ results.size.must_be :>, 0
62
+ results.size.must_be :<, messages.size
63
+ while zconnect.recv_strings(result, ZMQ::NOBLOCK) != -1
64
+ result.shift.must_equal 'MyDealer'
65
+ results << result
66
+ result = []
67
+ end
68
+ end
69
+ (messages - results).must_be_empty
70
+ (results - messages).must_be_empty
71
+ end
72
+
73
+ it "should be able to receive messages" do
74
+ Native.with_socket_pair('ROUTER') do |zbind, zconnect|
75
+ EM.run {
76
+ connected.callback {
77
+ messages[0...(messages.size/2)].each{|message|
78
+ zbind.send_strings ['MyDealer', *message]
79
+ }
80
+ messages[(messages.size/2)..-1].each{|message|
81
+ zconnect.send_strings ['MyDealer', *message]
82
+ }
83
+ }
84
+ cb = proc do
85
+ if dealer.incoming_queue.size < messages.size
86
+ EM.add_timer(0.5, cb)
87
+ else
88
+ EM.next_tick{ EM.stop }
89
+ end
90
+ end
91
+ cb.call
92
+ }
93
+ end
94
+ dealer.incoming_queue.size.must_equal messages.size
95
+ (dealer.incoming_queue - messages).must_be_empty
96
+ (messages - dealer.incoming_queue).must_be_empty
97
+ end
98
+ end
99
+
100
+ describe EM::Protocols::Zmq2::Dealer do
101
+ class MyDealer < EM::Protocols::Zmq2::Dealer
102
+ attr :incoming_queue, :canceled_messages
103
+ def initialize(opts={})
104
+ super(opts)
105
+ @connected = opts[:connected]
106
+ @incoming_queue = []
107
+ @canceled_messages = []
108
+ end
109
+ def peer_free(peer_ident, connection)
110
+ super
111
+ @connected.succeed if @free_peers.size == 2
112
+ end
113
+ def receive_message(message)
114
+ @incoming_queue << message
115
+ end
116
+ def cancel_message(message)
117
+ @canceled_messages << message
118
+ end
119
+ end
120
+
121
+ attr :dealer
122
+ before do
123
+ @dealer = MyDealer.new(identity: 'MyDealer', connected: connected)
124
+ @dealer.connect(Native::ZBIND_ADDR)
125
+ @dealer.bind(Native::ZCONNECT_ADDR)
126
+ end
127
+
128
+ let(:messages) do
129
+ 10000.times.map{|n| ['', 'hello', 'world', n.to_s]}
130
+ end
131
+
132
+ class RouteCollector
133
+ attr :res_a, :res_b
134
+ def initialize(till)
135
+ @till = till
136
+ @res_a, @res_b = [], []
137
+ end
138
+ def full?
139
+ @res_a.size + @res_b.size >= @till
140
+ end
141
+ def full_res
142
+ @res_a + @res_b
143
+ end
144
+ def set_sockets(zbind, zconnect)
145
+ @zbind = zbind
146
+ @zconnect = zconnect
147
+ end
148
+ def thread
149
+ Thread.new do
150
+ result = []
151
+ until full?
152
+ while @zbind.recv_strings(result, ZMQ::NOBLOCK) != -1
153
+ result.shift.must_equal 'MyDealer'
154
+ @res_a << result
155
+ result = []
156
+ end
157
+ while @zconnect.recv_strings(result, ZMQ::NOBLOCK) != -1
158
+ result.shift.must_equal 'MyDealer'
159
+ @res_b << result
160
+ result = []
161
+ end
162
+ sleep(0.01)
163
+ end
164
+ end
165
+ end
166
+ end
167
+
168
+ it "should be able to send a lot of messages" do
169
+ collector = RouteCollector.new(messages.size)
170
+ Native.with_socket_pair('ROUTER') do |zbind, zconnect|
171
+ collector.set_sockets zbind, zconnect
172
+ thrd = collector.thread
173
+ EM.run {
174
+ connected.callback {
175
+ messages.each{|message|
176
+ dealer.send_message(message)
177
+ }
178
+ dealer.close do
179
+ EM.next_tick{ EM.stop }
180
+ end
181
+ }
182
+ }
183
+ thrd.join
184
+ end
185
+ collector.full?.must_equal true
186
+ collector.res_a.size.must_be :>, 0
187
+ collector.res_a.size.must_be :<, messages.size
188
+ (messages - collector.full_res).must_be_empty
189
+ (collector.full_res - messages).must_be_empty
190
+ end
191
+
192
+ it "should be closed after writting" do
193
+ collector = RouteCollector.new(messages.size)
194
+ Native.with_socket_pair('ROUTER') do |zbind, zconnect|
195
+ collector.set_sockets zbind, zconnect
196
+ thrd = collector.thread
197
+ EM.run {
198
+ connected.callback {
199
+ messages.each{|message|
200
+ dealer.send_message(message)
201
+ }
202
+ dealer.close do
203
+ EM.next_tick{ EM.stop }
204
+ end
205
+ }
206
+ }
207
+ thrd.join
208
+ end
209
+ collector.full?.must_equal true
210
+ collector.res_a.size.must_be :>, 0
211
+ collector.res_a.size.must_be :<, messages.size
212
+ (messages - collector.full_res).must_be_empty
213
+ (collector.full_res - messages).must_be_empty
214
+ end
215
+
216
+ it "should not accept message on low hwm with strategy :drop_last" do
217
+ dealer.hwm = 1
218
+ dealer.hwm_strategy = :drop_last
219
+ EM.run {
220
+ dealer.send_message(['hi', 'ho1']).must_equal true
221
+ dealer.send_message(['hi', 'ho2']).wont_equal true
222
+ EM.stop
223
+ }
224
+ end
225
+
226
+ it "should cancel earlier message on low hwm with strategy :drop_first" do
227
+ dealer.hwm = 1
228
+ dealer.hwm_strategy = :drop_first
229
+ EM.run {
230
+ dealer.send_message(['hi', 'ho1']).must_equal true
231
+ dealer.send_message(['hi', 'ho2']).must_equal true
232
+ EM.stop
233
+ }
234
+ dealer.canceled_messages.must_equal [['hi', 'ho1']]
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,113 @@
1
+ require 'eventmachine'
2
+ require 'minitest/autorun'
3
+ require File.expand_path('../helper.rb', __FILE__)
4
+
5
+ require 'em/protocols/zmq2'
6
+
7
+ describe 'InProc' do
8
+ let(:connected) do
9
+ EM::DefaultDeferrable.new
10
+ end
11
+
12
+ class IPDealer < EM::Protocols::Zmq2::Dealer
13
+ include DeferredMixin
14
+ include IncomingMixin
15
+ def peers
16
+ @peers
17
+ end
18
+ end
19
+
20
+ let(:messages) do
21
+ 1000.times.map{|i| ['hi', i.to_s]}
22
+ end
23
+
24
+ let(:fan) do
25
+ IPDealer.new(connected: connected, identity: 'fan')
26
+ end
27
+
28
+ let(:sinks) do
29
+ 2.times.map{|i| IPDealer.new(identity: "sink.#{i}") }
30
+ end
31
+
32
+ def run_test
33
+ EM.run {
34
+ connected.callback do
35
+ fan.peers['sink.0'].wont_be_nil
36
+ fan.peers['sink.1'].wont_be_nil
37
+ sinks.each{|sink| sink.peers['fan'].wont_be_nil}
38
+ messages.each{|message| fan.send_message(message)}
39
+ fan.close do
40
+ EM.next_tick{ EM.stop }
41
+ end
42
+ end
43
+ }
44
+ sinks.each do |sink|
45
+ sink.incoming_queue.size.must_be :>, 0
46
+ sink.incoming_queue.size.must_be :<, messages.size
47
+ end
48
+ incomings = sinks.map(&:incoming_queue).flatten(1)
49
+ (incomings - messages).must_be_empty
50
+ (messages - incomings).must_be_empty
51
+ end
52
+
53
+ it "should send messages to several connected socket" do
54
+ sinks.each{|sink| sink.connect 'inproc://fan'}
55
+ fan.bind 'inproc://fan'
56
+ run_test
57
+ end
58
+
59
+ it "should send messages to several binded sockets" do
60
+ sinks.each{|sink|
61
+ bind_point = "inproc://#{sink.identity}"
62
+ sink.bind bind_point
63
+ fan.connect bind_point
64
+ }
65
+ run_test
66
+ end
67
+
68
+ it "should send messages to inproc and tcp" do
69
+ sinks[0].bind 'tcp://127.0.0.1:9876'
70
+ fan.connect 'tcp://127.0.0.1:9876'
71
+ sinks[1].bind 'inproc://sink.1'
72
+ fan.connect 'inproc://sink.1'
73
+ run_test
74
+ end
75
+
76
+ if RUBY_PLATFORM !~ /window/
77
+ it "should send messages to inproc and tcp" do
78
+ sinks[0].bind 'ipc://falcon'
79
+ fan.connect 'ipc://falcon'
80
+ sinks[1].bind 'inproc://sink.1'
81
+ fan.connect 'inproc://sink.1'
82
+ run_test
83
+ end
84
+ end
85
+
86
+ class IPPub < EM::Protocols::Zmq2::Pub
87
+ include DeferredMixin
88
+ include IncomingMixin
89
+ end
90
+ class IPSub < EM::Protocols::Zmq2::Sub
91
+ include DeferredMixin
92
+ include IncomingMixin
93
+ end
94
+ it "should pub messages to inproc and tcp sub" do
95
+ pub = IPPub.new(identity: 'pub', connected: connected)
96
+ subs = 2.times.map{ sub = IPSub.new(subscribe: 'hi') }
97
+ subs[0].bind 'tcp://127.0.0.1:9876'
98
+ pub.connect 'tcp://127.0.0.1:9876'
99
+ subs[1].bind 'inproc://sink.1'
100
+ pub.connect 'inproc://sink.1'
101
+ EM.run {
102
+ connected.callback {
103
+ messages.each{|message| pub.send_message(message)}
104
+ pub.close do
105
+ EM.next_tick{ EM.stop }
106
+ end
107
+ }
108
+ }
109
+ subs.each {|sub|
110
+ sub.incoming_queue.must_equal messages
111
+ }
112
+ end
113
+ end