ffi-rzmq 0.8.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/AUTHORS.txt +1 -0
  2. data/History.txt +35 -0
  3. data/README.rdoc +48 -15
  4. data/Rakefile +7 -2
  5. data/examples/README.rdoc +21 -76
  6. data/examples/{local_lat.rb → v2api/local_lat.rb} +27 -12
  7. data/examples/v2api/local_lat_poll.rb +66 -0
  8. data/examples/{local_throughput.rb → v2api/local_throughput.rb} +24 -9
  9. data/examples/v2api/publish_subscribe.rb +82 -0
  10. data/examples/{remote_lat.rb → v2api/remote_lat.rb} +26 -8
  11. data/examples/v2api/remote_throughput.rb +39 -0
  12. data/examples/v2api/reqrep_poll.rb +62 -0
  13. data/examples/v2api/request_response.rb +40 -0
  14. data/examples/v2api/throughput_measurement.rb +138 -0
  15. data/examples/v3api/local_lat.rb +59 -0
  16. data/examples/v3api/local_lat_poll.rb +66 -0
  17. data/examples/v3api/local_throughput.rb +65 -0
  18. data/examples/v3api/publish_subscribe.rb +82 -0
  19. data/examples/v3api/remote_lat.rb +71 -0
  20. data/examples/v3api/remote_throughput.rb +47 -0
  21. data/examples/v3api/reqrep_poll.rb +62 -0
  22. data/examples/v3api/request_response.rb +40 -0
  23. data/examples/v3api/throughput_measurement.rb +166 -0
  24. data/ext/README +5 -0
  25. data/ffi-rzmq.gemspec +4 -4
  26. data/lib/ffi-rzmq.rb +4 -1
  27. data/lib/ffi-rzmq/constants.rb +178 -0
  28. data/lib/ffi-rzmq/context.rb +61 -45
  29. data/lib/ffi-rzmq/device.rb +22 -9
  30. data/lib/ffi-rzmq/exceptions.rb +0 -98
  31. data/lib/ffi-rzmq/libc.rb +19 -0
  32. data/lib/ffi-rzmq/libzmq.rb +188 -0
  33. data/lib/ffi-rzmq/message.rb +33 -40
  34. data/lib/ffi-rzmq/poll.rb +49 -52
  35. data/lib/ffi-rzmq/socket.rb +902 -392
  36. data/lib/ffi-rzmq/util.rb +101 -0
  37. data/spec/context_spec.rb +47 -21
  38. data/spec/device_spec.rb +78 -58
  39. data/spec/message_spec.rb +90 -12
  40. data/spec/multipart_spec.rb +162 -0
  41. data/spec/nonblocking_recv_spec.rb +325 -0
  42. data/spec/pushpull_spec.rb +95 -34
  43. data/spec/reqrep_spec.rb +55 -20
  44. data/spec/socket_spec.rb +353 -204
  45. data/spec/spec_helper.rb +46 -3
  46. data/version.txt +1 -1
  47. metadata +91 -66
  48. data/examples/local_lat_poll.rb +0 -54
  49. data/examples/local_lat_zerocopy.rb +0 -24
  50. data/examples/publish_subscribe.rb +0 -52
  51. data/examples/remote_lat_zerocopy.rb +0 -35
  52. data/examples/remote_throughput.rb +0 -27
  53. data/examples/reqrep_poll.rb +0 -49
  54. data/examples/request_response.rb +0 -23
  55. data/lib/ffi-rzmq/wrapper.rb +0 -121
  56. data/lib/ffi-rzmq/zmq.rb +0 -198
@@ -13,55 +13,116 @@ module ZMQ
13
13
  @context = ZMQ::Context.new
14
14
  @push = @context.socket ZMQ::PUSH
15
15
  @pull = @context.socket ZMQ::PULL
16
- @link = "tcp://127.0.0.1:#{random_port}"
17
- @pull.connect @link
16
+ @push.setsockopt ZMQ::LINGER, 0
17
+ @pull.setsockopt ZMQ::LINGER, 0
18
+ port = connect_to_random_tcp_port(@pull)
19
+ @link = "tcp://127.0.0.1:#{port}"
18
20
  @push.bind @link
19
21
  end
20
-
22
+
21
23
  after(:each) do
22
24
  @push.close
23
25
  @pull.close
26
+ @context.terminate
24
27
  end
25
-
28
+
26
29
  it "should receive an exact copy of the sent message using Message objects directly on one pull socket" do
27
30
  @push.send_string string
28
- received = @pull.recv_string
31
+ received = ''
32
+ rc = @pull.recv_string received
29
33
  received.should == string
30
34
  end
31
-
32
- it "should receive an exact string copy of the message sent when receiving in non-blocking mode and using Message objects directly" do
33
- sent_message = Message.new string
34
- received_message = Message.new
35
-
36
- @push.send sent_message
37
- sleep 0.1 # give it time for delivery
38
- @pull.recv received_message, ZMQ::NOBLOCK
39
- received_message.copy_out_string.should == string
35
+
36
+ if version2?
37
+ it "should receive an exact string copy of the message sent when receiving in non-blocking mode and using Message objects directly" do
38
+ sent_message = Message.new string
39
+ received_message = Message.new
40
+
41
+ rc = @push.send sent_message
42
+ rc.should == 0
43
+ sleep 0.1 # give it time for delivery
44
+ rc = @pull.recv received_message, ZMQ::NOBLOCK
45
+ received_message.copy_out_string.should == string
46
+ end
47
+
48
+ else
49
+
50
+ it "should receive an exact string copy of the message sent when receiving in non-blocking mode and using Message objects directly" do
51
+ sent_message = Message.new string
52
+ received_message = Message.new
53
+
54
+ rc = @push.sendmsg sent_message
55
+ rc.should == string.size
56
+ sleep 0.1 # give it time for delivery
57
+ rc = @pull.recvmsg received_message, ZMQ::DONTWAIT
58
+ rc.should == string.size
59
+ received_message.copy_out_string.should == string
60
+ end
40
61
  end
41
62
 
42
- it "should receive a single message for each message sent on each socket listening, when an equal number pulls to messages" do
43
- received = []
44
- threads = []
45
- count = 4
46
- @pull.close # close this one since we aren't going to use it below and we don't want it to receive a message
47
-
48
- count.times do
49
- threads << Thread.new do
50
- pull = @context.socket ZMQ::PULL
51
- rc = pull.connect @link
52
- received << pull.recv_string
53
- pull.close
63
+
64
+ if version2?
65
+
66
+ it "should receive a single message for each message sent on each socket listening, when an equal number pulls to messages" do
67
+ received = []
68
+ threads = []
69
+ count = 4
70
+ @pull.close # close this one since we aren't going to use it below and we don't want it to receive a message
71
+
72
+ count.times do |i|
73
+ threads << Thread.new do
74
+ pull = @context.socket ZMQ::PULL
75
+ rc = pull.setsockopt ZMQ::LINGER, 0
76
+ rc = pull.connect @link
77
+ rc.should == 0
78
+ buffer = ''
79
+ rc = pull.recv_string buffer
80
+ rc.should == 0
81
+ received << buffer
82
+ pull.close
83
+ end
84
+ sleep 0.01 # give each thread time to spin up
54
85
  end
55
- sleep 0.001 # give each thread time to spin up
86
+
87
+ count.times { @push.send_string(string) }
88
+
89
+ threads.each {|t| t.join}
90
+
91
+ received.find_all {|r| r == string}.length.should == count
56
92
  end
57
-
58
- count.times { @push.send_string(string) }
59
93
 
60
- threads.each {|t| t.join}
61
-
62
- received.find_all {|r| r == string}.length.should == count
63
- end
64
-
94
+ else # version3 or 4
95
+
96
+ it "should receive a single message for each message sent on each socket listening, when an equal number pulls to messages" do
97
+ received = []
98
+ threads = []
99
+ count = 4
100
+ @pull.close # close this one since we aren't going to use it below and we don't want it to receive a message
101
+
102
+ count.times do |i|
103
+ threads << Thread.new do
104
+ pull = @context.socket ZMQ::PULL
105
+ rc = pull.setsockopt ZMQ::LINGER, 0
106
+ rc = pull.connect @link
107
+ rc.should == 0
108
+ buffer = ''
109
+ rc = pull.recv_string buffer
110
+ rc.should == string.size
111
+ received << buffer
112
+ pull.close
113
+ end
114
+ sleep 0.01 # give each thread time to spin up
115
+ end
116
+
117
+ count.times { @push.send_string(string) }
118
+
119
+ threads.each {|t| t.join}
120
+
121
+ received.find_all {|r| r == string}.length.should == count
122
+ end
123
+
124
+ end # if version...
125
+
65
126
  end # @context ping-pong
66
127
  end # describe
67
128
  end # module ZMQ
@@ -15,11 +15,10 @@ module ZMQ
15
15
  context = ZMQ::Context.new 1
16
16
  @ping = context.socket ZMQ::REQ
17
17
  @pong = context.socket ZMQ::REP
18
- link = "tcp://127.0.0.1:#{random_port}"
19
- @pong.bind link
20
- @ping.connect link
18
+ port = bind_to_random_tcp_port(@pong)
19
+ @ping.connect "tcp://127.0.0.1:#{port}"
21
20
  end
22
-
21
+
23
22
  after(:each) do
24
23
  @ping.close
25
24
  @pong.close
@@ -27,31 +26,67 @@ module ZMQ
27
26
 
28
27
  it "should receive an exact string copy of the string message sent" do
29
28
  @ping.send_string string
30
- received_message = @pong.recv_string
29
+ received_message = ''
30
+ rc = @pong.recv_string received_message
31
31
 
32
32
  received_message.should == string
33
33
  end
34
34
 
35
- it "should receive an exact copy of the sent message using Message objects directly" do
36
- sent_message = Message.new string
37
- received_message = Message.new
35
+ if version2?
36
+ it "should receive an exact copy of the sent message using Message objects directly" do
37
+ sent_message = Message.new string
38
+ received_message = Message.new
38
39
 
39
- @ping.send sent_message
40
- @pong.recv received_message
40
+ rc = @ping.send sent_message
41
+ rc.should == 0
42
+ rc = @pong.recv received_message
43
+ rc.should == 0
41
44
 
42
- received_message.copy_out_string.should == string
43
- end
45
+ received_message.copy_out_string.should == string
46
+ end
44
47
 
45
- it "should receive an exact copy of the sent message using Message objects directly in non-blocking mode" do
46
- sent_message = Message.new string
47
- received_message = Message.new
48
+ it "should receive an exact copy of the sent message using Message objects directly in non-blocking mode" do
49
+ sent_message = Message.new string
50
+ received_message = Message.new
48
51
 
49
- @ping.send sent_message, ZMQ::NOBLOCK
50
- sleep 0.001 # give it time for delivery
51
- @pong.recv received_message, ZMQ::NOBLOCK
52
+ rc = @ping.send sent_message, ZMQ::NOBLOCK
53
+ rc.should == 0
54
+ sleep 0.1 # give it time for delivery
55
+ rc = @pong.recv received_message, ZMQ::NOBLOCK
56
+ rc.should == 0
57
+
58
+ received_message.copy_out_string.should == string
59
+ end
60
+
61
+ else # version3 or 4
62
+
63
+ it "should receive an exact copy of the sent message using Message objects directly" do
64
+ sent_message = Message.new string
65
+ received_message = Message.new
66
+
67
+ rc = @ping.sendmsg sent_message
68
+ rc.should == string.size
69
+ rc = @pong.recvmsg received_message
70
+ rc.should == string.size
71
+
72
+ received_message.copy_out_string.should == string
73
+ end
74
+
75
+ it "should receive an exact copy of the sent message using Message objects directly in non-blocking mode" do
76
+ sent_message = Message.new string
77
+ received_message = Message.new
78
+
79
+ rc = @ping.sendmsg sent_message, ZMQ::DONTWAIT
80
+ rc.should == string.size
81
+ sleep 0.001 # give it time for delivery
82
+ rc = @pong.recvmsg received_message, ZMQ::DONTWAIT
83
+ rc.should == string.size
84
+
85
+ received_message.copy_out_string.should == string
86
+ end
87
+
88
+ end # if version...
52
89
 
53
- received_message.copy_out_string.should == string
54
- end
55
90
  end # context ping-pong
56
91
 
57
92
 
@@ -6,6 +6,14 @@ module ZMQ
6
6
 
7
7
  describe Socket do
8
8
 
9
+ socket_types = if LibZMQ.version2?
10
+ [ZMQ::REQ, ZMQ::REP, ZMQ::DEALER, ZMQ::ROUTER, ZMQ::PUB, ZMQ::SUB, ZMQ::PUSH, ZMQ::PULL, ZMQ::PAIR]
11
+ elsif LibZMQ.version3?
12
+ [ZMQ::REQ, ZMQ::REP, ZMQ::DEALER, ZMQ::ROUTER, ZMQ::PUB, ZMQ::SUB, ZMQ::PUSH, ZMQ::PULL, ZMQ::PAIR, ZMQ::XPUB, ZMQ::XSUB]
13
+ elsif LibZMQ.version4?
14
+ [ZMQ::REQ, ZMQ::REP, ZMQ::ROUTER, ZMQ::PUB, ZMQ::SUB, ZMQ::PUSH, ZMQ::PULL, ZMQ::PAIR, ZMQ::XPUB, ZMQ::XSUB]
15
+ end
16
+
9
17
  context "when initializing" do
10
18
  before(:all) { @ctx = Context.new }
11
19
  after(:all) { @ctx.terminate }
@@ -15,9 +23,24 @@ module ZMQ
15
23
  lambda { Socket.new(FFI::Pointer.new(0), ZMQ::REQ) }.should raise_exception(ZMQ::ContextError)
16
24
  end
17
25
 
18
- [ZMQ::REQ, ZMQ::REP, ZMQ::DEALER, ZMQ::ROUTER, ZMQ::PUB, ZMQ::SUB, ZMQ::PUSH, ZMQ::PULL, ZMQ::PAIR].each do |socket_type|
26
+ it "works with a Context#pointer as the context_ptr" do
27
+ lambda do
28
+ s = Socket.new(@ctx.pointer, ZMQ::REQ)
29
+ s.close
30
+ end.should_not raise_exception(ZMQ::ContextError)
31
+ end
32
+
33
+ it "works with a Context instance as the context_ptr" do
34
+ lambda do
35
+ s = Socket.new(@ctx, ZMQ::SUB)
36
+ s.close
37
+ end.should_not raise_exception(ZMQ::ContextError)
38
+ end
39
+
19
40
 
20
- it "should not raise an error for a #{ZMQ::SocketTypeNameMap[socket_type]} socket type" do
41
+ socket_types.each do |socket_type|
42
+
43
+ it "should not raise an error for a [#{ZMQ::SocketTypeNameMap[socket_type]}] socket type" do
21
44
  sock = nil
22
45
  lambda { sock = Socket.new(@ctx.pointer, socket_type) }.should_not raise_error
23
46
  sock.close
@@ -39,8 +62,8 @@ module ZMQ
39
62
  sock.close
40
63
  end
41
64
  end # context initializing
42
-
43
-
65
+
66
+
44
67
  context "calling close" do
45
68
  before(:all) { @ctx = Context.new }
46
69
  after(:all) { @ctx.terminate }
@@ -57,49 +80,57 @@ module ZMQ
57
80
  end # context calling close
58
81
 
59
82
 
60
- context "identity=" do
61
- before(:all) { @ctx = Context.new }
62
- after(:all) { @ctx.terminate }
83
+ if version2? || version3?
63
84
 
64
- it "should raise an exception for identities in excess of 255 bytes" do
65
- sock = Socket.new @ctx.pointer, ZMQ::REQ
85
+ context "identity=" do
86
+ before(:all) { @ctx = Context.new }
87
+ after(:all) { @ctx.terminate }
66
88
 
67
- lambda { sock.identity = ('a' * 256) }.should raise_exception(ZMQ::SocketError)
68
- sock.close
69
- end
89
+ it "fails to set identity for identities in excess of 255 bytes" do
90
+ sock = Socket.new @ctx.pointer, ZMQ::REQ
70
91
 
71
- it "should raise an exception for identities of length 0" do
72
- sock = Socket.new @ctx.pointer, ZMQ::REQ
92
+ sock.identity = ('a' * 256)
93
+ sock.identity.should == ''
94
+ sock.close
95
+ end
73
96
 
74
- lambda { sock.identity = '' }.should raise_exception(ZMQ::SocketError)
75
- sock.close
76
- end
97
+ it "fails to set identity for identities of length 0" do
98
+ sock = Socket.new @ctx.pointer, ZMQ::REQ
77
99
 
78
- it "should NOT raise an exception for identities of 1 byte" do
79
- sock = Socket.new @ctx.pointer, ZMQ::REQ
100
+ sock.identity = ''
101
+ sock.identity.should == ''
102
+ sock.close
103
+ end
80
104
 
81
- lambda { sock.identity = 'a' }.should_not raise_exception(ZMQ::SocketError)
82
- sock.close
83
- end
105
+ it "sets the identity for identities of 1 byte" do
106
+ sock = Socket.new @ctx.pointer, ZMQ::REQ
84
107
 
85
- it "should NOT raise an exception for identities of 255 bytes" do
86
- sock = Socket.new @ctx.pointer, ZMQ::REQ
108
+ sock.identity = 'a'
109
+ sock.identity.should == 'a'
110
+ sock.close
111
+ end
87
112
 
88
- lambda { sock.identity = ('a' * 255) }.should_not raise_exception(ZMQ::SocketError)
89
- sock.close
90
- end
113
+ it "set the identity identities of 255 bytes" do
114
+ sock = Socket.new @ctx.pointer, ZMQ::REQ
91
115
 
92
- it "should convert numeric identities to strings" do
93
- sock = Socket.new @ctx.pointer, ZMQ::REQ
116
+ sock.identity = ('a' * 255)
117
+ sock.identity.should == ('a' * 255)
118
+ sock.close
119
+ end
94
120
 
95
- sock.identity = 7
96
- sock.identity.should == '7'
97
- sock.close
98
- end
99
- end # context identity=
121
+ it "should convert numeric identities to strings" do
122
+ sock = Socket.new @ctx.pointer, ZMQ::REQ
123
+
124
+ sock.identity = 7
125
+ sock.identity.should == '7'
126
+ sock.close
127
+ end
128
+ end # context identity=
100
129
 
130
+ end # version2? || version3?
101
131
 
102
- [ZMQ::REQ, ZMQ::REP, ZMQ::DEALER, ZMQ::ROUTER, ZMQ::PUB, ZMQ::SUB, ZMQ::PUSH, ZMQ::PULL, ZMQ::PAIR].each do |socket_type|
132
+
133
+ socket_types.each do |socket_type|
103
134
 
104
135
  context "#setsockopt for a #{ZMQ::SocketTypeNameMap[socket_type]} socket" do
105
136
  before(:all) { @ctx = Context.new }
@@ -114,14 +145,116 @@ module ZMQ
114
145
  end
115
146
 
116
147
 
148
+ if version2? || version3?
149
+
150
+ context "using option ZMQ::IDENTITY" do
151
+ it "should set the identity given any string under 255 characters" do
152
+ length = 4
153
+ (1..255).each do |length|
154
+ identity = 'a' * length
155
+ socket.setsockopt ZMQ::IDENTITY, identity
156
+
157
+ array = []
158
+ rc = socket.getsockopt(ZMQ::IDENTITY, array)
159
+ rc.should == 0
160
+ array[0].should == identity
161
+ end
162
+ end
163
+
164
+ it "returns -1 given a string 256 characters or longer" do
165
+ identity = 'a' * 256
166
+ array = []
167
+ rc = socket.setsockopt(ZMQ::IDENTITY, identity)
168
+ rc.should == -1
169
+ end
170
+ end # context using option ZMQ::IDENTITY
171
+
172
+ end # version2? || version3?
173
+
174
+
175
+ if version2?
176
+
177
+ context "using option ZMQ::HWM" do
178
+ it "should set the high water mark given a positive value" do
179
+ hwm = 4
180
+ socket.setsockopt ZMQ::HWM, hwm
181
+ array = []
182
+ rc = socket.getsockopt(ZMQ::HWM, array)
183
+ rc.should == 0
184
+ array[0].should == hwm
185
+ end
186
+ end # context using option ZMQ::HWM
187
+
188
+
189
+ context "using option ZMQ::SWAP" do
190
+ it "should set the swap value given a positive value" do
191
+ swap = 10_000
192
+ socket.setsockopt ZMQ::SWAP, swap
193
+ array = []
194
+ rc = socket.getsockopt(ZMQ::SWAP, array)
195
+ rc.should == 0
196
+ array[0].should == swap
197
+ end
198
+
199
+ it "returns -1 given a negative value" do
200
+ swap = -10_000
201
+ rc = socket.setsockopt(ZMQ::SWAP, swap)
202
+ rc.should == -1
203
+ end
204
+ end # context using option ZMQ::SWP
205
+
206
+
207
+ context "using option ZMQ::MCAST_LOOP" do
208
+ it "should enable the multicast loopback given a 1 (true) value" do
209
+ socket.setsockopt ZMQ::MCAST_LOOP, 1
210
+ array = []
211
+ rc = socket.getsockopt(ZMQ::MCAST_LOOP, array)
212
+ rc.should == 0
213
+ array[0].should be_true
214
+ end
215
+
216
+ it "should disable the multicast loopback given a 0 (false) value" do
217
+ socket.setsockopt ZMQ::MCAST_LOOP, 0
218
+ array = []
219
+ rc = socket.getsockopt(ZMQ::MCAST_LOOP, array)
220
+ rc.should == 0
221
+ array[0].should be_false
222
+ end
223
+ end # context using option ZMQ::MCAST_LOOP
224
+
225
+
226
+ context "using option ZMQ::RECOVERY_IVL_MSEC" do
227
+ it "should set the time interval for saving messages measured in milliseconds given a positive value" do
228
+ value = 200
229
+ socket.setsockopt ZMQ::RECOVERY_IVL_MSEC, value
230
+ array = []
231
+ rc = socket.getsockopt(ZMQ::RECOVERY_IVL_MSEC, array)
232
+ rc.should == 0
233
+ array[0].should == value
234
+ end
235
+
236
+ it "should default to a value of -1" do
237
+ value = -1
238
+ array = []
239
+ rc = socket.getsockopt(ZMQ::RECOVERY_IVL_MSEC, array)
240
+ rc.should == 0
241
+ array[0].should == value
242
+ end
243
+ end # context using option ZMQ::RECOVERY_IVL_MSEC
244
+
245
+ end # version2?
246
+
247
+
117
248
  context "using option ZMQ::SUBSCRIBE" do
118
249
  if ZMQ::SUB == socket_type
119
- it "should *not* raise a ZMQ::SocketError" do
120
- lambda { socket.setsockopt(ZMQ::SUBSCRIBE, "topic.string") }.should_not raise_error(SocketError)
250
+ it "returns 0 for a SUB socket" do
251
+ rc = socket.setsockopt(ZMQ::SUBSCRIBE, "topic.string")
252
+ rc.should == 0
121
253
  end
122
254
  else
123
- it "should raise a ZMQ::SocketError" do
124
- lambda { socket.setsockopt(ZMQ::SUBSCRIBE, "topic.string") }.should raise_error(SocketError)
255
+ it "returns -1 for non-SUB sockets" do
256
+ rc = socket.setsockopt(ZMQ::SUBSCRIBE, "topic.string")
257
+ rc.should == -1
125
258
  end
126
259
  end
127
260
  end # context using option ZMQ::SUBSCRIBE
@@ -129,94 +262,47 @@ module ZMQ
129
262
 
130
263
  context "using option ZMQ::UNSUBSCRIBE" do
131
264
  if ZMQ::SUB == socket_type
132
- it "should *not* raise a ZMQ::SocketError given a topic string that was previously subscribed" do
265
+ it "returns 0 given a topic string that was previously subscribed" do
133
266
  socket.setsockopt ZMQ::SUBSCRIBE, "topic.string"
134
- lambda { socket.setsockopt(ZMQ::UNSUBSCRIBE, "topic.string") }.should_not raise_error(SocketError)
267
+ rc = socket.setsockopt(ZMQ::UNSUBSCRIBE, "topic.string")
268
+ rc.should == 0
135
269
  end
136
270
 
137
- # it "should raise a ZMQ::SocketError given a topic string that was never subscribed" do
138
- # socket.setsockopt ZMQ::SUBSCRIBE, "topic.string"
139
- # lambda { socket.setsockopt(ZMQ::UNSUBSCRIBE, "unknown") }.should raise_error(SocketError)
140
- # end
141
271
  else
142
- it "should raise a ZMQ::SocketError" do
143
- lambda { socket.setsockopt(ZMQ::UNSUBSCRIBE, "topic.string") }.should raise_error(SocketError)
272
+ it "returns -1 for non-SUB sockets" do
273
+ rc = socket.setsockopt(ZMQ::UNSUBSCRIBE, "topic.string")
274
+ rc.should == -1
144
275
  end
145
276
  end
146
277
  end # context using option ZMQ::UNSUBSCRIBE
147
278
 
148
279
 
149
- context "using option ZMQ::HWM" do
150
- it "should set the high water mark given a positive value" do
151
- hwm = 4
152
- socket.setsockopt ZMQ::HWM, hwm
153
- socket.getsockopt(ZMQ::HWM).should == hwm
154
- end
155
-
156
- it "should convert a negative value to a positive value" do
157
- hwm = -4
158
- socket.setsockopt ZMQ::HWM, hwm
159
- socket.getsockopt(ZMQ::HWM).should == hwm.abs
160
- end
161
- end # context using option ZMQ::HWM
162
-
163
-
164
- context "using option ZMQ::SWAP" do
165
- it "should set the swap value given a positive value" do
166
- swap = 10_000
167
- socket.setsockopt ZMQ::SWAP, swap
168
- socket.getsockopt(ZMQ::SWAP).should == swap
169
- end
170
-
171
- it "should raise a SocketError given a negative value" do
172
- swap = -10_000
173
- lambda { socket.setsockopt(ZMQ::SWAP, swap) }.should raise_error(SocketError)
174
- end
175
- end # context using option ZMQ::SWP
176
-
177
-
178
280
  context "using option ZMQ::AFFINITY" do
179
281
  it "should set the affinity value given a positive value" do
180
282
  affinity = 3
181
283
  socket.setsockopt ZMQ::AFFINITY, affinity
182
- socket.getsockopt(ZMQ::AFFINITY).should == affinity
183
- end
184
-
185
- it "should set the affinity value as a positive value given a negative value" do
186
- affinity = -3
187
- socket.setsockopt ZMQ::AFFINITY, affinity
188
- socket.getsockopt(ZMQ::AFFINITY).should == affinity.abs
284
+ array = []
285
+ rc = socket.getsockopt(ZMQ::AFFINITY, array)
286
+ rc.should == 0
287
+ array[0].should == affinity
189
288
  end
190
289
  end # context using option ZMQ::AFFINITY
191
290
 
192
291
 
193
- context "using option ZMQ::IDENTITY" do
194
- it "should set the identity given any string under 255 characters" do
195
- length = 4
196
- (1..255).each do |length|
197
- identity = 'a' * length
198
- socket.setsockopt ZMQ::IDENTITY, identity
199
- socket.getsockopt(ZMQ::IDENTITY).should == identity
200
- end
201
- end
202
-
203
- it "should raise a SocketError given a string 256 characters or longer" do
204
- identity = 'a' * 256
205
- lambda { socket.setsockopt(ZMQ::IDENTITY, identity) }.should raise_error(SocketError)
206
- end
207
- end # context using option ZMQ::IDENTITY
208
-
209
-
210
292
  context "using option ZMQ::RATE" do
211
293
  it "should set the multicast send rate given a positive value" do
212
294
  rate = 200
213
295
  socket.setsockopt ZMQ::RATE, rate
214
- socket.getsockopt(ZMQ::RATE).should == rate
296
+ array = []
297
+ rc = socket.getsockopt(ZMQ::RATE, array)
298
+ rc.should == 0
299
+ array[0].should == rate
215
300
  end
216
301
 
217
- it "should raise a SocketError given a negative value" do
302
+ it "returns -1 given a negative value" do
218
303
  rate = -200
219
- lambda { socket.setsockopt ZMQ::RATE, rate }.should raise_error(SocketError)
304
+ rc = socket.setsockopt ZMQ::RATE, rate
305
+ rc.should == -1
220
306
  end
221
307
  end # context using option ZMQ::RATE
222
308
 
@@ -225,40 +311,28 @@ module ZMQ
225
311
  it "should set the multicast recovery buffer measured in seconds given a positive value" do
226
312
  rate = 200
227
313
  socket.setsockopt ZMQ::RECOVERY_IVL, rate
228
- socket.getsockopt(ZMQ::RECOVERY_IVL).should == rate
314
+ array = []
315
+ rc = socket.getsockopt(ZMQ::RECOVERY_IVL, array)
316
+ rc.should == 0
317
+ array[0].should == rate
229
318
  end
230
319
 
231
- it "should raise a SocketError given a negative value" do
320
+ it "returns -1 given a negative value" do
232
321
  rate = -200
233
- lambda { socket.setsockopt ZMQ::RECOVERY_IVL, rate }.should raise_error(SocketError)
322
+ rc = socket.setsockopt ZMQ::RECOVERY_IVL, rate
323
+ rc.should == -1
234
324
  end
235
325
  end # context using option ZMQ::RECOVERY_IVL
236
326
 
237
327
 
238
- context "using option ZMQ::MCAST_LOOP" do
239
- it "should enable the multicast loopback given a true value" do
240
- socket.setsockopt ZMQ::MCAST_LOOP, true
241
- socket.getsockopt(ZMQ::MCAST_LOOP).should be_true
242
- end
243
-
244
- it "should disable the multicast loopback given a false value" do
245
- socket.setsockopt ZMQ::MCAST_LOOP, false
246
- socket.getsockopt(ZMQ::MCAST_LOOP).should be_false
247
- end
248
- end # context using option ZMQ::MCAST_LOOP
249
-
250
-
251
328
  context "using option ZMQ::SNDBUF" do
252
329
  it "should set the OS send buffer given a positive value" do
253
330
  size = 100
254
331
  socket.setsockopt ZMQ::SNDBUF, size
255
- socket.getsockopt(ZMQ::SNDBUF).should == size
256
- end
257
-
258
- it "should set the OS send buffer to a positive value given a false value" do
259
- size = -100
260
- socket.setsockopt ZMQ::SNDBUF, size
261
- socket.getsockopt(ZMQ::SNDBUF).should == size.abs
332
+ array = []
333
+ rc = socket.getsockopt(ZMQ::SNDBUF, array)
334
+ rc.should == 0
335
+ array[0].should == size
262
336
  end
263
337
  end # context using option ZMQ::SNDBUF
264
338
 
@@ -267,13 +341,10 @@ module ZMQ
267
341
  it "should set the OS receive buffer given a positive value" do
268
342
  size = 100
269
343
  socket.setsockopt ZMQ::RCVBUF, size
270
- socket.getsockopt(ZMQ::RCVBUF).should == size
271
- end
272
-
273
- it "should set the OS receive buffer to a positive value given a false value" do
274
- size = -100
275
- socket.setsockopt ZMQ::RCVBUF, size
276
- socket.getsockopt(ZMQ::RCVBUF).should == size.abs
344
+ array = []
345
+ rc = socket.getsockopt(ZMQ::RCVBUF, array)
346
+ rc.should == 0
347
+ array[0].should == size
277
348
  end
278
349
  end # context using option ZMQ::RCVBUF
279
350
 
@@ -282,18 +353,27 @@ module ZMQ
282
353
  it "should set the socket message linger option measured in milliseconds given a positive value" do
283
354
  value = 200
284
355
  socket.setsockopt ZMQ::LINGER, value
285
- socket.getsockopt(ZMQ::LINGER).should == value
356
+ array = []
357
+ rc = socket.getsockopt(ZMQ::LINGER, array)
358
+ rc.should == 0
359
+ array[0].should == value
286
360
  end
287
361
 
288
362
  it "should set the socket message linger option to 0 for dropping packets" do
289
363
  value = 0
290
364
  socket.setsockopt ZMQ::LINGER, value
291
- socket.getsockopt(ZMQ::LINGER).should == value
365
+ array = []
366
+ rc = socket.getsockopt(ZMQ::LINGER, array)
367
+ rc.should == 0
368
+ array[0].should == value
292
369
  end
293
370
 
294
371
  it "should default to a value of -1" do
295
372
  value = -1
296
- socket.getsockopt(ZMQ::LINGER).should == value
373
+ array = []
374
+ rc = socket.getsockopt(ZMQ::LINGER, array)
375
+ rc.should == 0
376
+ array[0].should == value
297
377
  end
298
378
  end # context using option ZMQ::LINGER
299
379
 
@@ -302,12 +382,18 @@ module ZMQ
302
382
  it "should set the time interval for reconnecting disconnected sockets measured in milliseconds given a positive value" do
303
383
  value = 200
304
384
  socket.setsockopt ZMQ::RECONNECT_IVL, value
305
- socket.getsockopt(ZMQ::RECONNECT_IVL).should == value
385
+ array = []
386
+ rc = socket.getsockopt(ZMQ::RECONNECT_IVL, array)
387
+ rc.should == 0
388
+ array[0].should == value
306
389
  end
307
390
 
308
391
  it "should default to a value of 100" do
309
392
  value = 100
310
- socket.getsockopt(ZMQ::RECONNECT_IVL).should == value
393
+ array = []
394
+ rc = socket.getsockopt(ZMQ::RECONNECT_IVL, array)
395
+ rc.should == 0
396
+ array[0].should == value
311
397
  end
312
398
  end # context using option ZMQ::RECONNECT_IVL
313
399
 
@@ -316,28 +402,20 @@ module ZMQ
316
402
  it "should set the maximum number of pending socket connections given a positive value" do
317
403
  value = 200
318
404
  socket.setsockopt ZMQ::BACKLOG, value
319
- socket.getsockopt(ZMQ::BACKLOG).should == value
405
+ array = []
406
+ rc = socket.getsockopt(ZMQ::BACKLOG, array)
407
+ rc.should == 0
408
+ array[0].should == value
320
409
  end
321
410
 
322
411
  it "should default to a value of 100" do
323
412
  value = 100
324
- socket.getsockopt(ZMQ::BACKLOG).should == value
413
+ array = []
414
+ rc = socket.getsockopt(ZMQ::BACKLOG, array)
415
+ rc.should == 0
416
+ array[0].should == value
325
417
  end
326
418
  end # context using option ZMQ::BACKLOG
327
-
328
-
329
- context "using option ZMQ::RECOVERY_IVL_MSEC" do
330
- it "should set the time interval for saving messages measured in milliseconds given a positive value" do
331
- value = 200
332
- socket.setsockopt ZMQ::RECOVERY_IVL_MSEC, value
333
- socket.getsockopt(ZMQ::RECOVERY_IVL_MSEC).should == value
334
- end
335
-
336
- it "should default to a value of -1" do
337
- value = -1
338
- socket.getsockopt(ZMQ::RECOVERY_IVL_MSEC).should == value
339
- end
340
- end # context using option ZMQ::RECOVERY_IVL_MSEC
341
419
  end # context #setsockopt
342
420
 
343
421
 
@@ -353,48 +431,70 @@ module ZMQ
353
431
  socket.close
354
432
  end
355
433
 
356
- context "using option ZMQ::FD" do
357
- it "should return an FD as a positive integer" do
358
- socket.getsockopt(ZMQ::FD).should be_a(Fixnum)
359
- end
434
+ if RUBY_PLATFORM =~ /linux|darwin/
435
+ # this spec doesn't work on Windows; hints welcome
360
436
 
361
- it "should return a valid FD" do
362
- # Use FFI to wrap the C library function +getsockopt+ so that we can execute it
363
- # on the 0mq file descriptor. If it returns 0, then it succeeded and the FD
364
- # is valid!
365
- module LibSocket
366
- extend FFI::Library
367
- # figures out the correct libc for each platform including Windows
368
- library = ffi_lib(FFI::Library::LIBC).first
369
- attach_function :getsockopt, [:int, :int, :int, :pointer, :pointer], :int
370
- end # module LibC
371
-
372
- if RUBY_PLATFORM =~ /linux/ || (RUBY_PLATFORM == 'java' && `uname` =~ /linux/i)
373
- so_rcvbuf = 8
374
- sol_socket = 1
375
- else #OSX
376
- so_rcvbuf = 0x1002
377
- sol_socket = 0xffff
437
+ context "using option ZMQ::FD" do
438
+ it "should return an FD as a positive integer" do
439
+ array = []
440
+ rc = socket.getsockopt(ZMQ::FD, array)
441
+ rc.should == 0
442
+ array[0].should be_a(Fixnum)
378
443
  end
379
444
 
380
- socklen_size = FFI::MemoryPointer.new :uint32
381
- socklen_size.write_int 8
382
- rcvbuf = FFI::MemoryPointer.new :int64
383
- fd = socket.getsockopt(ZMQ::FD)
384
-
385
- LibSocket.getsockopt(fd, sol_socket, so_rcvbuf, rcvbuf, socklen_size).should be_zero
445
+ it "returns a valid FD that is accepted by the system poll() function" do
446
+ # Use FFI to wrap the C library function +poll+ so that we can execute it
447
+ # on the 0mq file descriptor. If it returns 0, then it succeeded and the FD
448
+ # is valid!
449
+ module LibSocket
450
+ extend FFI::Library
451
+ # figures out the correct libc for each platform including Windows
452
+ library = ffi_lib(FFI::Library::LIBC).first
453
+
454
+ find_type(:nfds_t) rescue typedef(:uint32, :nfds_t)
455
+
456
+ attach_function :poll, [:pointer, :nfds_t, :int], :int
457
+
458
+ class PollFD < FFI::Struct
459
+ layout :fd, :int,
460
+ :events, :short,
461
+ :revents, :short
462
+ end
463
+ end # module LibSocket
464
+
465
+ array = []
466
+ rc = socket.getsockopt(ZMQ::FD, array)
467
+ rc.should be_zero
468
+ fd = array[0]
469
+
470
+ # setup the BSD poll_fd struct
471
+ pollfd = LibSocket::PollFD.new
472
+ pollfd[:fd] = fd
473
+ pollfd[:events] = 0
474
+ pollfd[:revents] = 0
475
+
476
+ rc = LibSocket.poll(pollfd, 1, 0)
477
+ rc.should be_zero
478
+ end
386
479
  end
387
- end
480
+
481
+ end # posix platform
388
482
 
389
483
  context "using option ZMQ::EVENTS" do
390
484
  it "should return a mask of events as a Fixnum" do
391
- socket.getsockopt(ZMQ::EVENTS).should be_a(Fixnum)
485
+ array = []
486
+ rc = socket.getsockopt(ZMQ::EVENTS, array)
487
+ rc.should == 0
488
+ array[0].should be_a(Fixnum)
392
489
  end
393
490
  end
394
491
 
395
492
  context "using option ZMQ::TYPE" do
396
493
  it "should return the socket type" do
397
- socket.getsockopt(ZMQ::TYPE).should == socket_type
494
+ array = []
495
+ rc = socket.getsockopt(ZMQ::TYPE, array)
496
+ rc.should == 0
497
+ array[0].should == socket_type
398
498
  end
399
499
  end
400
500
  end # context #getsockopt
@@ -402,38 +502,87 @@ module ZMQ
402
502
  end # each socket_type
403
503
 
404
504
 
405
- describe "Events mapping to POLLIN and POLLOUT" do
505
+ describe "Mapping socket EVENTS to POLLIN and POLLOUT" do
406
506
  include APIHelper
507
+
508
+ shared_examples_for "pubsub sockets where" do
509
+ it "SUB socket that received a message always has POLLIN set" do
510
+ events = []
511
+ rc = @sub.getsockopt(ZMQ::EVENTS, events)
512
+ rc.should == 0
513
+ events[0].should == ZMQ::POLLIN
514
+ end
515
+
516
+ it "PUB socket always has POLLOUT set" do
517
+ events = []
518
+ rc = @pub.getsockopt(ZMQ::EVENTS, events)
519
+ rc.should == 0
520
+ events[0].should == ZMQ::POLLOUT
521
+ end
407
522
 
408
- before(:all) do
409
- @ctx = Context.new
410
- addr = "tcp://127.0.0.1:#{random_port}"
523
+ it "PUB socket never has POLLIN set" do
524
+ events = []
525
+ rc = @pub.getsockopt(ZMQ::EVENTS, events)
526
+ rc.should == 0
527
+ events[0].should_not == ZMQ::POLLIN
528
+ end
411
529
 
412
- @sub = @ctx.socket ZMQ::SUB
413
- @sub.setsockopt ZMQ::SUBSCRIBE, ''
530
+ it "SUB socket never has POLLOUT set" do
531
+ events = []
532
+ rc = @sub.getsockopt(ZMQ::EVENTS, events)
533
+ rc.should == 0
534
+ events[0].should_not == ZMQ::POLLOUT
535
+ end
536
+ end # shared example for pubsub
414
537
 
415
- @pub = @ctx.socket ZMQ::PUB
416
- @pub.connect addr
538
+ context "when SUB binds and PUB connects" do
417
539
 
418
- @sub.bind addr
540
+ before(:each) do
541
+ @ctx = Context.new
419
542
 
420
- @pub.send_string('test')
421
- sleep 0.1
422
- end
423
- after(:all) do
543
+ @sub = @ctx.socket ZMQ::SUB
544
+ rc = @sub.setsockopt ZMQ::SUBSCRIBE, ''
545
+
546
+ @pub = @ctx.socket ZMQ::PUB
547
+ port = bind_to_random_tcp_port(@sub)
548
+ rc = @pub.connect "tcp://127.0.0.1:#{port}"
549
+ sleep 0.5
550
+
551
+ rc = @pub.send_string('test')
552
+ sleep 0.2
553
+ end
554
+
555
+ it_behaves_like "pubsub sockets where"
556
+ end # context SUB binds PUB connects
557
+
558
+ context "when SUB connects and PUB binds" do
559
+
560
+ before(:each) do
561
+ @ctx = Context.new
562
+
563
+ @sub = @ctx.socket ZMQ::SUB
564
+ rc = @sub.setsockopt ZMQ::SUBSCRIBE, ''
565
+
566
+ @pub = @ctx.socket ZMQ::PUB
567
+ port = bind_to_random_tcp_port(@pub)
568
+ rc = @sub.connect "tcp://127.0.0.1:#{port}"
569
+ sleep 0.5
570
+
571
+ rc = @pub.send_string('test')
572
+ sleep 0.2
573
+ end
574
+
575
+ it_behaves_like "pubsub sockets where"
576
+ end # context SUB binds PUB connects
577
+
578
+
579
+ after(:each) do
424
580
  @sub.close
425
581
  @pub.close
426
582
  # must call close on *every* socket before calling terminate otherwise it blocks indefinitely
427
583
  @ctx.terminate
428
584
  end
429
585
 
430
- it "should have only POLLIN set for a sub socket that received a message" do
431
- #@sub.getsockopt(ZMQ::EVENTS).should == ZMQ::POLLIN
432
- end
433
-
434
- it "should have only POLLOUT set for a sub socket that received a message" do
435
- #@pub.getsockopt(ZMQ::EVENTS).should == ZMQ::POLLOUT
436
- end
437
586
  end # describe 'events mapping to pollin and pollout'
438
587
 
439
588
  end # describe Socket