ffi-rzmq 0.8.2 → 0.9.0

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.
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