zerg_support 0.0.9 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,5 @@
1
+ v0.1. Socket primitives.
2
+
1
3
  v0.0.9. Changed ProcTable wrapper to deal with API change in 0.8.0.
2
4
 
3
5
  v0.0.8. Extension emulation works in Windows without a build environment.
data/Manifest CHANGED
@@ -1,10 +1,14 @@
1
1
  CHANGELOG
2
2
  lib/zerg_support/event_machine/connection_mocks.rb
3
- lib/zerg_support/event_machine/frame_protocol.rb
4
- lib/zerg_support/event_machine/object_protocol.rb
3
+ lib/zerg_support/event_machine/protocol_adapter.rb
5
4
  lib/zerg_support/gems.rb
6
5
  lib/zerg_support/open_ssh.rb
7
6
  lib/zerg_support/process.rb
7
+ lib/zerg_support/protocols/frame_protocol.rb
8
+ lib/zerg_support/protocols/object_protocol.rb
9
+ lib/zerg_support/socket_factory.rb
10
+ lib/zerg_support/sockets/protocol_adapter.rb
11
+ lib/zerg_support/sockets/socket_mocks.rb
8
12
  lib/zerg_support/spawn.rb
9
13
  lib/zerg_support.rb
10
14
  LICENSE
@@ -19,4 +23,5 @@ test/test_gems.rb
19
23
  test/test_object_protocol.rb
20
24
  test/test_open_ssh.rb
21
25
  test/test_process.rb
26
+ test/test_socket_factory.rb
22
27
  test/test_spawn.rb
@@ -1,6 +1,9 @@
1
+ # :nodoc: namespace
2
+ module Zerg::Support::EventMachine
3
+
1
4
  # Mocks the sending end of an EventMachine connection.
2
5
  # The sent data is concatenated in a string available by calling #string.
3
- class Zerg::Support::EventMachine::SendMock
6
+ class SendMock
4
7
  attr_reader :string
5
8
 
6
9
  def initialize
@@ -10,17 +13,17 @@ class Zerg::Support::EventMachine::SendMock
10
13
  def send_data(data)
11
14
  @string << data
12
15
  end
13
- end
16
+ end # class SendMock
14
17
 
15
18
  # Mocks the receiving end of an EventMachine connection.
16
19
  # The data to be received is passed as an array of strings to the constructor.
17
20
  # Calling #replay mocks receiving the data.
18
- class Zerg::Support::EventMachine::ReceiveMock
21
+ class ReceiveMock
19
22
  attr_accessor :strings
20
23
  attr_accessor :objects
21
24
 
22
25
  def initialize(strings = [''])
23
- @strings = strings
26
+ @strings = strings.kind_of?(String) ? [strings] : strings
24
27
  @objects = []
25
28
  end
26
29
 
@@ -30,17 +33,14 @@ class Zerg::Support::EventMachine::ReceiveMock
30
33
  self
31
34
  end
32
35
 
33
- #:nodoc:
34
- def receive__object(object)
35
- @objects << object
36
- end
37
-
38
36
  # Declares the name of the object to be received. For instance, a frame
39
37
  # protocol would use :frame for name. This generates a receive_frame method,
40
38
  # and a frames accessor.
41
39
  def self.object_name(name)
42
- alias_method "receive_#{name}".to_sym, :receive__object
40
+ define_method(:"receive_#{name}") { |object| @objects << object }
43
41
  return if name == :object
44
- alias_method "#{name}s".to_sym, :objects
42
+ alias_method :"#{name}s", :objects
45
43
  end
46
- end
44
+ end # class ReceiveMock
45
+
46
+ end # namespace Zerg::Support::EventMachine
@@ -0,0 +1,53 @@
1
+ # :nodoc: namespace
2
+ module Zerg::Support::EventMachine
3
+
4
+ # Adapts generic Protocol modules for easy inclusion in EventMachine connection
5
+ # classes / modules.
6
+ module ProtocolAdapter
7
+ def self.adapter_module(protocol_module, object_name = nil)
8
+ unless object_name
9
+ object_name = /(^|:)([^:]*)Protocol$/.
10
+ match(protocol_module.name)[2].split(/(?=[A-Z])/).join('_').downcase
11
+ end
12
+
13
+ state_class = Class.new StateBase
14
+ state_class.send :include, protocol_module
15
+ state_class.class_eval do
16
+ # Called by the protocol when an entire object is available.
17
+ define_method :"received_#{object_name}" do |object|
18
+ @target.send :"receive_#{object_name}", object
19
+ end
20
+ end
21
+
22
+ adapter = Module.new
23
+ adapter.module_eval do
24
+ # Called by Event Machine when TCP stream data is available.
25
+ define_method :receive_data do |data|
26
+ @zerg_protocol_adapter_state ||= state_class.new self
27
+ @zerg_protocol_adapter_state.received_bytes data
28
+ end
29
+
30
+ # Called by the Event Machine user to send data.
31
+ define_method :"send_#{object_name}" do |object|
32
+ @zerg_protocol_adapter_state ||= state_class.new self
33
+ @zerg_protocol_adapter_state.send :"send_#{object_name}", object
34
+ end
35
+ end
36
+
37
+ adapter
38
+ end
39
+
40
+ # Base class for the adapter state.
41
+ class StateBase
42
+ def initialize(target)
43
+ @target = target
44
+ end
45
+
46
+ # Called by the protocol when an entire object is available.
47
+ def send_bytes(data)
48
+ @target.send_data data
49
+ end
50
+ end
51
+ end
52
+
53
+ end # namespace Zerg::Support::EventMachine
@@ -127,11 +127,10 @@ module Zerg::Support::Process
127
127
  self.kill_primitive pid, false
128
128
  Thread.new(pid) do |victim_pid|
129
129
  Kernel.sleep 0.2
130
- self.kill_primitive pid, true
130
+ self.kill_primitive(pid, true) rescue nil
131
131
  end
132
132
  rescue
133
133
  # we probably don't have the right to kill the process
134
- print "#{$!.class.name}: #{$!}\n"
135
134
  end
136
135
  end
137
136
 
@@ -1,10 +1,10 @@
1
1
  #:nodoc: namespace
2
- module Zerg::Support::EventMachine
2
+ module Zerg::Support::Protocols
3
3
 
4
- # Event Machine protocol for sending and receiving discrete-sized frames
5
- module FrameProtocol
6
- #:nodoc: This is called by Event Machine when TCP stream data is available.
7
- def receive_data(data)
4
+ # Protocol for sending and receiving discrete-sized frames over TCP streams.
5
+ module FrameProtocol
6
+ # Called when data is available from the TCP stream.
7
+ def received_bytes(data)
8
8
  @frame_protocol_varsize ||= ''
9
9
 
10
10
  i = 0
@@ -22,7 +22,7 @@ module FrameProtocol
22
22
  return if @frame_protocol_buffer.nil?
23
23
  break if @frame_protocol_bytes_left > data.size - i
24
24
 
25
- receive_frame @frame_protocol_buffer + data[i, @frame_protocol_bytes_left]
25
+ received_frame @frame_protocol_buffer + data[i, @frame_protocol_bytes_left]
26
26
  @frame_protocol_varsize, @frame_protocol_buffer = '', nil
27
27
  i += @frame_protocol_bytes_left
28
28
  end
@@ -32,14 +32,19 @@ module FrameProtocol
32
32
  end
33
33
 
34
34
  # Override to process incoming frames.
35
- def receive_frame(frame_data); end
35
+ def received_frame(frame_data); end
36
36
 
37
- # Sends a frame via the underlying Event Machine TCP stream.
37
+ # Sends a frame via the underlying TCP stream.
38
38
  def send_frame(frame_data)
39
- encoded_length = FrameProtocol.encode_natural(frame_data.length)
40
- send_data encoded_length + frame_data
39
+ send_bytes FrameProtocol.encode_frame(frame_data)
41
40
  end
42
41
 
42
+ # :nodoc: Encodes frame data into data to be sent across a TCP wire.
43
+ def self.encode_frame(frame_data)
44
+ encoded_length = FrameProtocol.encode_natural(frame_data.length)
45
+ encoded_length + frame_data
46
+ end
47
+
43
48
  #:nodoc: Encodes a natural (non-negative) integer into a string.
44
49
  def self.encode_natural(number)
45
50
  string = ''
@@ -65,4 +70,4 @@ module FrameProtocol
65
70
  end
66
71
  end
67
72
 
68
- end # namespace Zerg::Support::EventMachine
73
+ end # namespace Zerg::Support::Protocols
@@ -1,7 +1,7 @@
1
1
  require 'yaml'
2
2
 
3
3
  #:nodoc: namespace
4
- module Zerg::Support::EventMachine
4
+ module Zerg::Support::Protocols
5
5
 
6
6
  # Event Machine protocol for sending serializable objects.
7
7
  module ObjectProtocol
@@ -13,12 +13,12 @@ module ObjectProtocol
13
13
  end
14
14
 
15
15
  #:nodoc: Processes an incoming frame and de-serializes the object in it.
16
- def receive_frame(frame_data)
17
- receive_object YAML.load(frame_data)
16
+ def received_frame(frame_data)
17
+ received_object YAML.load(frame_data)
18
18
  end
19
19
 
20
20
  # Override to process incoming objects.
21
- def receive_object(object); end
21
+ def received_object(object); end
22
22
  end
23
23
 
24
- end # namespace Zerg::Support::EventMachine
24
+ end # namespace Zerg::Support::Protocols
@@ -0,0 +1,198 @@
1
+ require 'socket'
2
+
3
+ # :nodoc: namespace
4
+ module Zerg::Support
5
+
6
+ module SocketFactory
7
+ # Splits a host:port IP address into its components. IPv6 addresses welcome.
8
+ #
9
+ # The port will be nil if the IP address doesn't contain it.
10
+ def self.split_address(address)
11
+ port_match = /(^|[^:])\:([^:].*)$/.match(address)
12
+ if port_match
13
+ port = port_match[2]
14
+ host = address[0, address.length - port.length - 1]
15
+ [(host.empty? ? nil : host), port]
16
+ else
17
+ [address, nil]
18
+ end
19
+ end
20
+
21
+ # The host from a host:port IP address.
22
+ #
23
+ # Empty string if the IP address does not contain a host (e.g. :3000)
24
+ def self.host_from_address(address)
25
+ address and split_address(address)[0]
26
+ end
27
+
28
+ # The port from a host:port IP address.
29
+ #
30
+ # nil if the IP address does not contain a port (e.g. localhost)
31
+ def self.port_from_address(address)
32
+ address and (port_string = split_address(address)[1]) and port_string.to_i
33
+ end
34
+
35
+ # True for options requesting a connecting (as opposed to listening) socket.
36
+ def self.outbound?(options)
37
+ [:out_port, :out_host, :out_addr].any? { |k| options[k] }
38
+ end
39
+
40
+ # The host from a host:port IP address, in a form suitable for bind().
41
+ def self.bind_host(options)
42
+ host_from_address(options[:in_addr]) or options[:in_host] or '0.0.0.0'
43
+ end
44
+
45
+ # The port from a host:port IP address, in a form suitable for bind().
46
+ def self.bind_port(options)
47
+ port_from_address(options[:in_addr]) or options[:in_port] or 0
48
+ end
49
+
50
+ # An address suitable for bind() based on the options.
51
+ def self.bind_socket_address(options)
52
+ Socket::pack_sockaddr_in bind_port(options), bind_host(options)
53
+ end
54
+
55
+ # The host from a host:port IP address, in a form suitable for connect().
56
+ def self.connect_host(options)
57
+ options[:out_host] or host_from_address(options[:out_addr]) or 'localhost'
58
+ end
59
+
60
+ # The port from a host:port IP address, in a form suitable for connect().
61
+ def self.connect_port(options)
62
+ port_from_address(options[:out_addr]) or options[:out_port]
63
+ end
64
+
65
+ # True if the options indicate TCP should be used, false for UDP.
66
+ def self.tcp?(options)
67
+ options[:tcp] or !options[:udp]
68
+ end
69
+
70
+ # The socket() type based on the options.
71
+ def self.socket_type(options)
72
+ tcp?(options) ? Socket::SOCK_STREAM : Socket::SOCK_DGRAM
73
+ end
74
+
75
+ # Sets socket flags (via setsockopt) based on the options.
76
+ def self.set_options(socket, options)
77
+ if options[:no_delay]
78
+ if tcp?(options)
79
+ socket.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true
80
+ end
81
+ socket.sync = true
82
+ end
83
+
84
+ if options[:reuse_addr]
85
+ socket.setsockopt Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true
86
+ end
87
+
88
+ unless options[:reverse_lookup]
89
+ if socket.respond_to? :do_not_reverse_lookup
90
+ socket.do_not_reverse_lookup = true
91
+ else
92
+ # work around until the patch below actually gets committed:
93
+ # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/2346
94
+ BasicSocket.do_not_reverse_lookup = true
95
+ end
96
+ end
97
+ end
98
+
99
+ # Hacks a socket's accept method so that new sockets have the given flags set.
100
+ #
101
+ # The flags are set in a similar manner to set_options.
102
+ def self.set_options_on_accept_sockets(socket, options)
103
+ socket.instance_variable_set :@zerg_support_factory_options, options
104
+ def socket.accept(*args)
105
+ sock, addr = super
106
+ Zerg::Support::SocketFactory.set_options sock,
107
+ @zerg_support_factory_options
108
+ return sock, addr
109
+ end
110
+ end
111
+
112
+ # Sugar-coat the socket's listen() call with a default value for its argument.
113
+ def self.sugar_socket_listen(socket)
114
+ def socket.listen(*args)
115
+ args = [1000] if args.empty?
116
+ super(*args)
117
+ end
118
+ end
119
+
120
+ # Binds a socket to an address based on the options.
121
+ def self.bind(socket, options)
122
+ socket.bind bind_socket_address(options)
123
+ socket
124
+ end
125
+
126
+ # New inbound socket based on the options.
127
+ def self.new_inbound_socket(options)
128
+ socket = Socket.new Socket::AF_INET, socket_type(options), Socket::PF_UNSPEC
129
+ set_options socket, options
130
+ set_options_on_accept_sockets socket, options
131
+ sugar_socket_listen socket
132
+ bind socket, options
133
+ end
134
+
135
+ # Retrieves possible IP addresses to connect to based on the given options.
136
+ #
137
+ # The retrieval is done via setsockopt.
138
+ def self.addr_infos(options)
139
+ Socket.getaddrinfo connect_host(options), connect_port(options),
140
+ Socket::AF_INET, socket_type(options)
141
+ end
142
+
143
+ # New outbound socket based on an address obtained from getaddrinfo().
144
+ def self.new_outbound_socket_with_addr_info(addr_info)
145
+ Socket.new addr_info[4], addr_info[5], addr_info[6]
146
+ end
147
+
148
+ # Connects a socket to an address obtained from getaddrinfo().
149
+ #
150
+ # Returns the socket for success, or nil if the connection failed.
151
+ def self.connect_with_addr_info(socket, addr_info)
152
+ begin
153
+ socket.connect Socket.pack_sockaddr_in(addr_info[1], addr_info[3])
154
+ socket
155
+ rescue
156
+ nil
157
+ end
158
+ end
159
+
160
+ # New outbound socket based on the options.
161
+ def self.new_outbound_socket(options)
162
+ addr_infos = self.addr_infos options
163
+ addr_infos.each do |addr_info|
164
+ socket = new_outbound_socket_with_addr_info addr_info
165
+ set_options socket, options
166
+ return socket if connect_with_addr_info socket, addr_info
167
+ socket.close rescue nil
168
+ end
169
+ nil
170
+ end
171
+
172
+ # Kitchen-sink socket creation method. The following options are supported:
173
+ # tcp:: forces the use of TCP (overrides the udp option)
174
+ # udp:: selects UDP (the default is TCP)
175
+ # out_port:: the port to connect an outgoing socket to
176
+ # out_host:: the host to connect an outgoing socket to
177
+ # out_addr:: the host:port address to connect an outgoing socket to; the
178
+ # host and port override out_port and out_host, which can be used
179
+ # in conjunction with out_addr to provide default values
180
+ # in_port:: the port to bind a listening socket to
181
+ # in_host:: the host to bind a listening socket to
182
+ # in_addr:: the host:port address to bind a listening socket to; the host
183
+ # and port override in_port and in_host, which can be used in
184
+ # conjunction with in_addr to provide default values
185
+ # no_delay:: disables Nagles' algorithm
186
+ # reuse_addr:: allows binding other sockets to this socket's address
187
+ # reverse_lookup:: enables reverse lookups on connections; this is the Ruby
188
+ # default, but ZergSupport changes it for performance
189
+ def self.socket(options)
190
+ if outbound? options
191
+ new_outbound_socket options
192
+ else
193
+ new_inbound_socket options
194
+ end
195
+ end
196
+ end # class SocketFactory
197
+
198
+ end # namespace Zerg::Support
@@ -0,0 +1,59 @@
1
+ # :nodoc: namespace
2
+ module Zerg::Support::Sockets
3
+
4
+ # Adapts generic Protocol modules to modules that can extend sockets.
5
+ module ProtocolAdapter
6
+ def self.adapter_module(protocol_module, object_name = nil)
7
+ unless object_name
8
+ object_name = /(^|:)([^:]*)Protocol$/.
9
+ match(protocol_module.name)[2].split(/(?=[A-Z])/).join('_').downcase
10
+ end
11
+
12
+ state_class = Class.new StateBase
13
+ state_class.send :include, protocol_module
14
+ state_class.class_eval do
15
+ # Called by the protocol when an entire object is available.
16
+ define_method :"received_#{object_name}" do |object|
17
+ @recv_object_buffer << object
18
+ end
19
+ end
20
+
21
+ adapter = Module.new
22
+ adapter.module_eval do
23
+ # Receives an object from a socket.
24
+ define_method :"recv_#{object_name}" do
25
+ @zerg_protocol_adapter_state ||= state_class.new self
26
+ while @zerg_protocol_adapter_state.recv_object_buffer.length == 0
27
+ data = recv 65536
28
+ return nil if data.length == 0 # Other side closed socket.
29
+ @zerg_protocol_adapter_state.received_bytes data
30
+ end
31
+ @zerg_protocol_adapter_state.recv_object_buffer.shift
32
+ end
33
+
34
+ # Sends an object across a socket.
35
+ define_method :"send_#{object_name}" do |object|
36
+ @zerg_protocol_adapter_state ||= state_class.new self
37
+ @zerg_protocol_adapter_state.send :"send_#{object_name}", object
38
+ end
39
+ end
40
+
41
+ adapter
42
+ end
43
+
44
+ # Base class for the adapter state.
45
+ class StateBase
46
+ attr_reader :recv_object_buffer
47
+ def initialize(target)
48
+ @recv_object_buffer = []
49
+ @target = target
50
+ end
51
+
52
+ # Called by the protocol when an entire object is available.
53
+ def send_bytes(data)
54
+ @target.send data, 0
55
+ end
56
+ end
57
+ end
58
+
59
+ end # namespace Zerg::Support::Sockets
@@ -0,0 +1,60 @@
1
+ # :nodoc: namespace
2
+ module Zerg::Support::Sockets
3
+
4
+ # Mocks the sending end of a Socket connection.
5
+ # The sent data is concatenated in a string available by calling #string.
6
+ class SendMock
7
+ attr_reader :string
8
+
9
+ def initialize
10
+ @string = ''
11
+ end
12
+
13
+ def send(data, flags)
14
+ @string << data
15
+ end
16
+ end # class SendMock
17
+
18
+ # Mocks the receiving end of a Socket connection.
19
+ # The data to be received is passed as an array of strings to the constructor.
20
+ class ReceiveMock
21
+ attr_accessor :strings
22
+ attr_accessor :objects
23
+
24
+ def initialize(strings = [''])
25
+ @strings = strings.kind_of?(String) ? [strings] : strings
26
+ @objects = []
27
+ end
28
+
29
+ def recv(byte_limit)
30
+ bytes = @strings.shift
31
+ return '' unless bytes
32
+ if bytes.length > byte_limit
33
+ @strings.unshift bytes[byte_limit, bytes.length]
34
+ bytes = bytes[0, byte_limit]
35
+ end
36
+ bytes
37
+ end
38
+
39
+ # Declares the name of the object to be received. For instance, a frame
40
+ # protocol would use :frame for name. This generates a receive_frame method,
41
+ # and a frames accessor.
42
+ def self.object_name(name)
43
+ # Calls recv_object until the bytes buffer is drained.
44
+ define_method :replay do
45
+ while @strings.length > 0
46
+ @objects << self.send(:"recv_#{name}")
47
+ end
48
+ loop do
49
+ object = self.send(:"recv_#{name}")
50
+ break unless object
51
+ @objects << object
52
+ end
53
+ self
54
+ end
55
+ return if name == :object
56
+ alias_method :"#{name}s", :objects
57
+ end
58
+ end # class ReceiveMock
59
+
60
+ end # namespace Zerg::Support::Sockets
data/lib/zerg_support.rb CHANGED
@@ -1,4 +1,4 @@
1
- # :nodoc
1
+ # :nodoc: namespace
2
2
  module Zerg
3
3
  end
4
4
 
@@ -10,12 +10,11 @@ require 'rubygems'
10
10
  require 'zerg_support/gems.rb'
11
11
  require 'zerg_support/process.rb'
12
12
  require 'zerg_support/open_ssh.rb'
13
+ require 'zerg_support/socket_factory.rb'
13
14
  require 'zerg_support/spawn.rb'
14
-
15
- # TODO(victor): document this
16
- module Zerg::Support::EventMachine
17
- end
18
-
19
15
  require 'zerg_support/event_machine/connection_mocks.rb'
20
- require 'zerg_support/event_machine/frame_protocol.rb'
21
- require 'zerg_support/event_machine/object_protocol.rb'
16
+ require 'zerg_support/event_machine/protocol_adapter.rb'
17
+ require 'zerg_support/protocols/frame_protocol.rb'
18
+ require 'zerg_support/protocols/object_protocol.rb'
19
+ require 'zerg_support/sockets/protocol_adapter.rb'
20
+ require 'zerg_support/sockets/socket_mocks.rb'
@@ -2,23 +2,12 @@ require 'zerg_support'
2
2
 
3
3
  require 'test/unit'
4
4
 
5
- class TestFrameProtocol < Test::Unit::TestCase
6
- FP = Zerg::Support::EventMachine::FrameProtocol
7
-
8
- # Send mock for frames.
9
- class SendFramesMock < Zerg::Support::EventMachine::SendMock
10
- include FP
11
- end
12
-
13
- # Receive mock for frames.
14
- class ReceiveFramesMock < Zerg::Support::EventMachine::ReceiveMock
15
- include FP
16
- object_name :frame
17
- end
5
+ module FrameProtocolTestMethods
6
+ FP = Zerg::Support::Protocols::FrameProtocol
18
7
 
19
8
  def setup
20
9
  super
21
- @send_mock = SendFramesMock.new
10
+ @send_mock = self.class::SendFramesMock.new
22
11
  end
23
12
 
24
13
  def teardown
@@ -38,12 +27,13 @@ class TestFrameProtocol < Test::Unit::TestCase
38
27
  i += sublen
39
28
  end
40
29
  in_strings << in_string[i..-1] if i < in_string.length
41
- out_frames = ReceiveFramesMock.new(@send_mock.string).replay.frames
30
+ out_frames =
31
+ self.class::ReceiveFramesMock.new(@send_mock.string).replay.frames
42
32
  assert_equal frames, out_frames
43
33
  end
44
34
 
45
35
  def test_empty_frame
46
- continuous_data_test ['']
36
+ continuous_data_test ['']
47
37
  end
48
38
 
49
39
  def test_byte_frame
@@ -88,7 +78,7 @@ class TestFrameProtocol < Test::Unit::TestCase
88
78
  ex_frames = [s2_frame] * s2_count
89
79
 
90
80
  # build cut points in a string
91
- s2_points = [0, 1, 2, 3, 4, 5, 127, 128, 8190, 16381, 16382, 16383]
81
+ s2_points = [1, 2, 3, 4, 5, 127, 128, 8190, 16381, 16382, 16383]
92
82
  cut_points = []
93
83
  0.upto(s2_count - 1) do |i|
94
84
  cut_points += s2_points.map { |p| p + i * s2_string.length }
@@ -101,7 +91,8 @@ class TestFrameProtocol < Test::Unit::TestCase
101
91
  packets = [0...cut_points[i], cut_points[i]...cut_points[j],
102
92
  cut_points[j]...cut_points[k], cut_points[k]..-1].
103
93
  map { |r| send_string[r] }
104
- assert_equal ex_frames, ReceiveFramesMock.new(packets).replay.frames
94
+ assert_equal ex_frames,
95
+ self.class::ReceiveFramesMock.new(packets).replay.frames
105
96
  end
106
97
  end
107
98
  end
@@ -115,5 +106,43 @@ class TestFrameProtocol < Test::Unit::TestCase
115
106
  assert_equal entry.last, FP.encode_natural(entry.first)
116
107
  assert_equal entry.first, FP.decode_natural(entry.last)
117
108
  end
109
+ end
110
+ end
111
+
112
+ class FrameProtocolEventMachineTest < Test::Unit::TestCase
113
+ include FrameProtocolTestMethods
114
+
115
+ ProtocolAdapter = Zerg::Support::EventMachine::ProtocolAdapter
116
+ FP = Zerg::Support::Protocols::FrameProtocol
117
+ FPAdapter = ProtocolAdapter.adapter_module FP
118
+
119
+ # Send mock for frames.
120
+ class SendFramesMock < Zerg::Support::EventMachine::SendMock
121
+ include FPAdapter
122
+ end
123
+
124
+ # Receive mock for frames.
125
+ class ReceiveFramesMock < Zerg::Support::EventMachine::ReceiveMock
126
+ include FPAdapter
127
+ object_name :frame
128
+ end
129
+ end
130
+
131
+ class FrameProtocolSocketsTest < Test::Unit::TestCase
132
+ include FrameProtocolTestMethods
133
+
134
+ ProtocolAdapter = Zerg::Support::Sockets::ProtocolAdapter
135
+ FP = Zerg::Support::Protocols::FrameProtocol
136
+ FPAdapter = ProtocolAdapter.adapter_module FP
137
+
138
+ # Send mock for frames.
139
+ class SendFramesMock < Zerg::Support::Sockets::SendMock
140
+ include FPAdapter
141
+ end
142
+
143
+ # Receive mock for frames.
144
+ class ReceiveFramesMock < Zerg::Support::Sockets::ReceiveMock
145
+ include FPAdapter
146
+ object_name :frame
118
147
  end
119
- end
148
+ end
@@ -3,13 +3,15 @@ require 'test/unit'
3
3
  require 'zerg_support'
4
4
 
5
5
  class ObjectProtocolTest < Test::Unit::TestCase
6
- OP = Zerg::Support::EventMachine::ObjectProtocol
6
+ ProtocolAdapter = Zerg::Support::EventMachine::ProtocolAdapter
7
+ OP = Zerg::Support::Protocols::ObjectProtocol
8
+ OPAdapter = ProtocolAdapter.adapter_module OP
7
9
 
8
10
  class SendObjectMock < Zerg::Support::EventMachine::SendMock
9
- include OP
11
+ include OPAdapter
10
12
  end
11
13
  class ReceiveObjectMock < Zerg::Support::EventMachine::ReceiveMock
12
- include OP
14
+ include OPAdapter
13
15
  object_name :object
14
16
  end
15
17
 
@@ -52,4 +54,4 @@ class ObjectProtocolTest < Test::Unit::TestCase
52
54
  "1234567890",
53
55
  {:command => 'run', :sequence => [1, nil, 2, true, "Q\n"]})
54
56
  end
55
- end
57
+ end
data/test/test_process.rb CHANGED
@@ -4,6 +4,11 @@ require 'test/unit'
4
4
  require 'zerg_support'
5
5
 
6
6
  class TestProcess < Test::Unit::TestCase
7
+ def setup
8
+ super
9
+ Thread.abort_on_exception = true
10
+ end
11
+
7
12
  def teardown
8
13
  @pid_files.each { |f| File.delete f rescue nil } if @pid_files
9
14
  super
@@ -0,0 +1,93 @@
1
+ require 'test/unit'
2
+
3
+ require 'zerg_support'
4
+
5
+
6
+ class SocketFactoryTest < Test::Unit::TestCase
7
+ SF = Zerg::Support::SocketFactory
8
+
9
+ OP = Zerg::Support::Protocols::ObjectProtocol
10
+ OPAdapter = Zerg::Support::Sockets::ProtocolAdapter.adapter_module OP
11
+
12
+ def setup
13
+ Thread.abort_on_exception = true
14
+ end
15
+
16
+ def teardown
17
+
18
+ end
19
+
20
+ def test_host_from_address
21
+ assert_equal nil, SF.host_from_address(nil)
22
+ assert_equal nil, SF.host_from_address(':9000')
23
+ assert_equal '127.0.0.1', SF.host_from_address('127.0.0.1')
24
+ assert_equal '127.0.0.1', SF.host_from_address('127.0.0.1:1234')
25
+ assert_equal 'fe80::1%lo0', SF.host_from_address('fe80::1%lo0')
26
+ assert_equal 'fe80::1%lo0', SF.host_from_address('fe80::1%lo0:19020')
27
+ end
28
+
29
+ def test_port_from_address
30
+ assert_equal nil, SF.port_from_address(nil)
31
+ assert_equal 9000, SF.port_from_address(':9000')
32
+ assert_equal nil, SF.port_from_address('127.0.0.1')
33
+ assert_equal 1234, SF.port_from_address('127.0.0.1:1234')
34
+ assert_equal nil, SF.port_from_address('fe80::1%lo0')
35
+ assert_equal 19020, SF.port_from_address('fe80::1%lo0:19020')
36
+ end
37
+
38
+ def test_inbound
39
+ assert !SF.outbound?(:in_port => 1)
40
+ assert !SF.outbound?(:in_host => '1')
41
+ assert !SF.outbound?(:in_addr => '1')
42
+ assert SF.outbound?(:out_port => 1)
43
+ assert SF.outbound?(:out_host => '1')
44
+ assert SF.outbound?(:out_addr => '1')
45
+ end
46
+
47
+ def test_tcp
48
+ assert SF.tcp?({})
49
+ assert SF.tcp?(:tcp => true)
50
+ assert !SF.tcp?(:udp => true)
51
+ end
52
+
53
+ def _test_connection(server_options, client_options = nil)
54
+ client_options ||= server_options
55
+ test_port = 31996
56
+
57
+ cli_gold = { :request_type => 1, :request_name => "moo" }
58
+ srv_gold = { :response_type => 2, :response_value => [true, 314] }
59
+ cli_hash = nil
60
+
61
+ # Server thread.
62
+ Thread.new do
63
+ server = SF.socket({:in_addr => ":#{test_port}"}.merge server_options)
64
+ server.listen
65
+ serv_client, client_addrinfo = server.accept
66
+ serv_client.extend OPAdapter
67
+ cli_hash = serv_client.recv_object
68
+ serv_client.send_object srv_gold
69
+ serv_client.close
70
+
71
+ server.close
72
+ end
73
+
74
+ # Client.
75
+ client = SF.socket({:out_addr => "localhost:#{test_port}"}.
76
+ merge(client_options))
77
+ client.extend OPAdapter
78
+ client.send_object cli_gold
79
+ srv_hash = client.recv_object
80
+ client.close
81
+
82
+ # Checks
83
+ assert_equal cli_gold, cli_hash, "Client -> server failed"
84
+ assert_equal srv_gold, srv_hash, "Server -> client failed"
85
+ end
86
+
87
+ def test_connection
88
+ _test_connection({:no_delay => true})
89
+ _test_connection({:reuse_addr => true})
90
+
91
+ # TODO(costan): fix UDP at some point
92
+ end
93
+ end
data/test/test_spawn.rb CHANGED
@@ -15,6 +15,7 @@ class TestSpawn < Test::Unit::TestCase
15
15
 
16
16
  def setup
17
17
  super
18
+ Thread.abort_on_exception = true
18
19
  @in_file = temp_file
19
20
  @out_file = temp_file
20
21
  end
data/zerg_support.gemspec CHANGED
@@ -2,27 +2,26 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{zerg_support}
5
- s.version = "0.0.9"
5
+ s.version = "0.1"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Victor Costan"]
9
- s.date = %q{2009-03-28}
9
+ s.date = %q{2009-06-02}
10
10
  s.description = %q{Support libraries used by Zergling.Net deployment code.}
11
11
  s.email = %q{victor@zergling.net}
12
- s.extra_rdoc_files = ["CHANGELOG", "lib/zerg_support/event_machine/connection_mocks.rb", "lib/zerg_support/event_machine/frame_protocol.rb", "lib/zerg_support/event_machine/object_protocol.rb", "lib/zerg_support/gems.rb", "lib/zerg_support/open_ssh.rb", "lib/zerg_support/process.rb", "lib/zerg_support/spawn.rb", "lib/zerg_support.rb", "LICENSE", "README"]
13
- s.files = ["CHANGELOG", "lib/zerg_support/event_machine/connection_mocks.rb", "lib/zerg_support/event_machine/frame_protocol.rb", "lib/zerg_support/event_machine/object_protocol.rb", "lib/zerg_support/gems.rb", "lib/zerg_support/open_ssh.rb", "lib/zerg_support/process.rb", "lib/zerg_support/spawn.rb", "lib/zerg_support.rb", "LICENSE", "Manifest", "Rakefile", "README", "RUBYFORGE", "test/fork_tree.rb", "test/test_connection_mocks.rb", "test/test_frame_protocol.rb", "test/test_gems.rb", "test/test_object_protocol.rb", "test/test_open_ssh.rb", "test/test_process.rb", "test/test_spawn.rb", "zerg_support.gemspec"]
14
- s.has_rdoc = true
12
+ s.extra_rdoc_files = ["CHANGELOG", "lib/zerg_support/event_machine/connection_mocks.rb", "lib/zerg_support/event_machine/protocol_adapter.rb", "lib/zerg_support/gems.rb", "lib/zerg_support/open_ssh.rb", "lib/zerg_support/process.rb", "lib/zerg_support/protocols/frame_protocol.rb", "lib/zerg_support/protocols/object_protocol.rb", "lib/zerg_support/socket_factory.rb", "lib/zerg_support/sockets/protocol_adapter.rb", "lib/zerg_support/sockets/socket_mocks.rb", "lib/zerg_support/spawn.rb", "lib/zerg_support.rb", "LICENSE", "README"]
13
+ s.files = ["CHANGELOG", "lib/zerg_support/event_machine/connection_mocks.rb", "lib/zerg_support/event_machine/protocol_adapter.rb", "lib/zerg_support/gems.rb", "lib/zerg_support/open_ssh.rb", "lib/zerg_support/process.rb", "lib/zerg_support/protocols/frame_protocol.rb", "lib/zerg_support/protocols/object_protocol.rb", "lib/zerg_support/socket_factory.rb", "lib/zerg_support/sockets/protocol_adapter.rb", "lib/zerg_support/sockets/socket_mocks.rb", "lib/zerg_support/spawn.rb", "lib/zerg_support.rb", "LICENSE", "Manifest", "Rakefile", "README", "RUBYFORGE", "test/fork_tree.rb", "test/test_connection_mocks.rb", "test/test_frame_protocol.rb", "test/test_gems.rb", "test/test_object_protocol.rb", "test/test_open_ssh.rb", "test/test_process.rb", "test/test_socket_factory.rb", "test/test_spawn.rb", "zerg_support.gemspec"]
15
14
  s.homepage = %q{http://www.zergling.net}
16
15
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Zerg_support", "--main", "README"]
17
16
  s.require_paths = ["lib"]
18
17
  s.rubyforge_project = %q{zerglings}
19
- s.rubygems_version = %q{1.3.1}
18
+ s.rubygems_version = %q{1.3.4}
20
19
  s.summary = %q{Support libraries used by Zergling.Net deployment code.}
21
- s.test_files = ["test/test_connection_mocks.rb", "test/test_frame_protocol.rb", "test/test_gems.rb", "test/test_object_protocol.rb", "test/test_open_ssh.rb", "test/test_process.rb", "test/test_spawn.rb"]
20
+ s.test_files = ["test/test_connection_mocks.rb", "test/test_frame_protocol.rb", "test/test_gems.rb", "test/test_object_protocol.rb", "test/test_open_ssh.rb", "test/test_process.rb", "test/test_socket_factory.rb", "test/test_spawn.rb"]
22
21
 
23
22
  if s.respond_to? :specification_version then
24
23
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
- s.specification_version = 2
24
+ s.specification_version = 3
26
25
 
27
26
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
27
  s.add_development_dependency(%q<echoe>, [">= 3.0.2"])
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zerg_support
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: "0.1"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Victor Costan
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-03-28 00:00:00 -04:00
12
+ date: 2009-06-02 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -51,11 +51,15 @@ extensions: []
51
51
  extra_rdoc_files:
52
52
  - CHANGELOG
53
53
  - lib/zerg_support/event_machine/connection_mocks.rb
54
- - lib/zerg_support/event_machine/frame_protocol.rb
55
- - lib/zerg_support/event_machine/object_protocol.rb
54
+ - lib/zerg_support/event_machine/protocol_adapter.rb
56
55
  - lib/zerg_support/gems.rb
57
56
  - lib/zerg_support/open_ssh.rb
58
57
  - lib/zerg_support/process.rb
58
+ - lib/zerg_support/protocols/frame_protocol.rb
59
+ - lib/zerg_support/protocols/object_protocol.rb
60
+ - lib/zerg_support/socket_factory.rb
61
+ - lib/zerg_support/sockets/protocol_adapter.rb
62
+ - lib/zerg_support/sockets/socket_mocks.rb
59
63
  - lib/zerg_support/spawn.rb
60
64
  - lib/zerg_support.rb
61
65
  - LICENSE
@@ -63,11 +67,15 @@ extra_rdoc_files:
63
67
  files:
64
68
  - CHANGELOG
65
69
  - lib/zerg_support/event_machine/connection_mocks.rb
66
- - lib/zerg_support/event_machine/frame_protocol.rb
67
- - lib/zerg_support/event_machine/object_protocol.rb
70
+ - lib/zerg_support/event_machine/protocol_adapter.rb
68
71
  - lib/zerg_support/gems.rb
69
72
  - lib/zerg_support/open_ssh.rb
70
73
  - lib/zerg_support/process.rb
74
+ - lib/zerg_support/protocols/frame_protocol.rb
75
+ - lib/zerg_support/protocols/object_protocol.rb
76
+ - lib/zerg_support/socket_factory.rb
77
+ - lib/zerg_support/sockets/protocol_adapter.rb
78
+ - lib/zerg_support/sockets/socket_mocks.rb
71
79
  - lib/zerg_support/spawn.rb
72
80
  - lib/zerg_support.rb
73
81
  - LICENSE
@@ -82,10 +90,13 @@ files:
82
90
  - test/test_object_protocol.rb
83
91
  - test/test_open_ssh.rb
84
92
  - test/test_process.rb
93
+ - test/test_socket_factory.rb
85
94
  - test/test_spawn.rb
86
95
  - zerg_support.gemspec
87
96
  has_rdoc: true
88
97
  homepage: http://www.zergling.net
98
+ licenses: []
99
+
89
100
  post_install_message:
90
101
  rdoc_options:
91
102
  - --line-numbers
@@ -111,9 +122,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
122
  requirements: []
112
123
 
113
124
  rubyforge_project: zerglings
114
- rubygems_version: 1.3.1
125
+ rubygems_version: 1.3.4
115
126
  signing_key:
116
- specification_version: 2
127
+ specification_version: 3
117
128
  summary: Support libraries used by Zergling.Net deployment code.
118
129
  test_files:
119
130
  - test/test_connection_mocks.rb
@@ -122,4 +133,5 @@ test_files:
122
133
  - test/test_object_protocol.rb
123
134
  - test/test_open_ssh.rb
124
135
  - test/test_process.rb
136
+ - test/test_socket_factory.rb
125
137
  - test/test_spawn.rb