thrift 0.0.751142
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.
- data/CHANGELOG +2 -0
- data/COPYING +14 -0
- data/LICENSE +14 -0
- data/Makefile.am +15 -0
- data/Manifest +78 -0
- data/README +30 -0
- data/Rakefile +102 -0
- data/benchmark/Benchmark.thrift +5 -0
- data/benchmark/benchmark.rb +254 -0
- data/benchmark/client.rb +56 -0
- data/benchmark/gen-rb/BenchmarkService.rb +81 -0
- data/benchmark/gen-rb/Benchmark_constants.rb +11 -0
- data/benchmark/gen-rb/Benchmark_types.rb +10 -0
- data/benchmark/server.rb +64 -0
- data/benchmark/thin_server.rb +26 -0
- data/ext/binary_protocol_accelerated.c +463 -0
- data/ext/binary_protocol_accelerated.h +1 -0
- data/ext/constants.h +77 -0
- data/ext/extconf.rb +7 -0
- data/ext/memory_buffer.c +52 -0
- data/ext/memory_buffer.h +1 -0
- data/ext/protocol.c +166 -0
- data/ext/protocol.h +1 -0
- data/ext/struct.c +574 -0
- data/ext/struct.h +48 -0
- data/ext/thrift_native.c +173 -0
- data/lib/thrift/client.rb +44 -0
- data/lib/thrift/deprecation.rb +155 -0
- data/lib/thrift/exceptions.rb +65 -0
- data/lib/thrift/processor.rb +39 -0
- data/lib/thrift/protocol/binaryprotocol.rb +213 -0
- data/lib/thrift/protocol/binaryprotocolaccelerated.rb +19 -0
- data/lib/thrift/protocol/tbinaryprotocol.rb +2 -0
- data/lib/thrift/protocol/tprotocol.rb +2 -0
- data/lib/thrift/protocol.rb +270 -0
- data/lib/thrift/serializer.rb +27 -0
- data/lib/thrift/server/httpserver.rb +44 -0
- data/lib/thrift/server/nonblockingserver.rb +278 -0
- data/lib/thrift/server/thttpserver.rb +2 -0
- data/lib/thrift/server/tserver.rb +2 -0
- data/lib/thrift/server.rb +135 -0
- data/lib/thrift/struct.rb +272 -0
- data/lib/thrift/thrift.rb +14 -0
- data/lib/thrift/transport/httpclient.rb +29 -0
- data/lib/thrift/transport/socket.rb +167 -0
- data/lib/thrift/transport/thttpclient.rb +2 -0
- data/lib/thrift/transport/tsocket.rb +2 -0
- data/lib/thrift/transport/ttransport.rb +2 -0
- data/lib/thrift/transport/unixsocket.rb +58 -0
- data/lib/thrift/transport.rb +319 -0
- data/lib/thrift/types.rb +83 -0
- data/lib/thrift.rb +28 -0
- data/setup.rb +1585 -0
- data/spec/ThriftSpec.thrift +46 -0
- data/spec/backwards_compatibility_spec.rb +136 -0
- data/spec/binaryprotocol_spec.rb +45 -0
- data/spec/binaryprotocol_spec_shared.rb +274 -0
- data/spec/binaryprotocolaccelerated_spec.rb +101 -0
- data/spec/client_spec.rb +81 -0
- data/spec/deprecation_spec.rb +443 -0
- data/spec/exception_spec.rb +123 -0
- data/spec/gen-rb/NonblockingService.rb +268 -0
- data/spec/gen-rb/ThriftSpec_constants.rb +11 -0
- data/spec/gen-rb/ThriftSpec_types.rb +134 -0
- data/spec/httpclient_spec.rb +31 -0
- data/spec/httpserver_spec.rb +98 -0
- data/spec/nonblockingserver_spec.rb +245 -0
- data/spec/processor_spec.rb +64 -0
- data/spec/protocol_spec.rb +142 -0
- data/spec/serializer_spec.rb +52 -0
- data/spec/server_spec.rb +141 -0
- data/spec/socket_spec.rb +97 -0
- data/spec/socket_spec_shared.rb +85 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/struct_spec.rb +244 -0
- data/spec/transport_spec.rb +359 -0
- data/spec/types_spec.rb +98 -0
- data/spec/unixsocket_spec.rb +90 -0
- data/thrift.gemspec +33 -0
- data.tar.gz.sig +0 -0
- metadata +200 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,245 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'thrift/server/nonblockingserver'
|
3
|
+
require File.dirname(__FILE__) + '/gen-rb/NonblockingService'
|
4
|
+
|
5
|
+
class ThriftNonblockingServerSpec < Spec::ExampleGroup
|
6
|
+
include Thrift
|
7
|
+
include SpecNamespace
|
8
|
+
|
9
|
+
class Handler
|
10
|
+
def initialize
|
11
|
+
@queue = Queue.new
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor :server
|
15
|
+
|
16
|
+
def greeting(english)
|
17
|
+
if english
|
18
|
+
SpecNamespace::Hello.new
|
19
|
+
else
|
20
|
+
SpecNamespace::Hello.new(:greeting => "Aloha!")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def block
|
25
|
+
@queue.pop
|
26
|
+
end
|
27
|
+
|
28
|
+
def unblock(n)
|
29
|
+
n.times { @queue.push true }
|
30
|
+
end
|
31
|
+
|
32
|
+
def sleep(time)
|
33
|
+
Kernel.sleep time
|
34
|
+
end
|
35
|
+
|
36
|
+
def shutdown
|
37
|
+
@server.shutdown(0, false)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class SpecTransport < Transport
|
42
|
+
def initialize(transport, queue)
|
43
|
+
@transport = transport
|
44
|
+
@queue = queue
|
45
|
+
@flushed = false
|
46
|
+
end
|
47
|
+
|
48
|
+
def open?
|
49
|
+
@transport.open?
|
50
|
+
end
|
51
|
+
|
52
|
+
def open
|
53
|
+
@transport.open
|
54
|
+
end
|
55
|
+
|
56
|
+
def close
|
57
|
+
@transport.close
|
58
|
+
end
|
59
|
+
|
60
|
+
def read(sz)
|
61
|
+
@transport.read(sz)
|
62
|
+
end
|
63
|
+
|
64
|
+
def write(buf,sz=nil)
|
65
|
+
@transport.write(buf, sz)
|
66
|
+
end
|
67
|
+
|
68
|
+
def flush
|
69
|
+
@queue.push :flushed unless @flushed or @queue.nil?
|
70
|
+
@flushed = true
|
71
|
+
@transport.flush
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class SpecServerSocket < ServerSocket
|
76
|
+
def initialize(host, port, queue)
|
77
|
+
super(host, port)
|
78
|
+
@queue = queue
|
79
|
+
end
|
80
|
+
|
81
|
+
def listen
|
82
|
+
super
|
83
|
+
@queue.push :listen
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe Thrift::NonblockingServer do
|
88
|
+
before(:each) do
|
89
|
+
@port = 43251
|
90
|
+
handler = Handler.new
|
91
|
+
processor = NonblockingService::Processor.new(handler)
|
92
|
+
queue = Queue.new
|
93
|
+
@transport = SpecServerSocket.new('localhost', @port, queue)
|
94
|
+
transportFactory = FramedTransportFactory.new
|
95
|
+
logger = Logger.new(STDERR)
|
96
|
+
logger.level = Logger::WARN
|
97
|
+
@server = NonblockingServer.new(processor, @transport, transportFactory, nil, 5, logger)
|
98
|
+
handler.server = @server
|
99
|
+
@server_thread = Thread.new(Thread.current) do |master_thread|
|
100
|
+
begin
|
101
|
+
@server.serve
|
102
|
+
rescue => e
|
103
|
+
p e
|
104
|
+
puts e.backtrace * "\n"
|
105
|
+
master_thread.raise e
|
106
|
+
end
|
107
|
+
end
|
108
|
+
queue.pop
|
109
|
+
|
110
|
+
@clients = []
|
111
|
+
@catch_exceptions = false
|
112
|
+
end
|
113
|
+
|
114
|
+
after(:each) do
|
115
|
+
@clients.each { |client, trans| trans.close }
|
116
|
+
# @server.shutdown(1)
|
117
|
+
@server_thread.kill
|
118
|
+
@transport.close
|
119
|
+
end
|
120
|
+
|
121
|
+
def setup_client(queue = nil)
|
122
|
+
transport = SpecTransport.new(FramedTransport.new(Socket.new('localhost', @port)), queue)
|
123
|
+
protocol = BinaryProtocol.new(transport)
|
124
|
+
client = NonblockingService::Client.new(protocol)
|
125
|
+
transport.open
|
126
|
+
@clients << [client, transport]
|
127
|
+
client
|
128
|
+
end
|
129
|
+
|
130
|
+
def setup_client_thread(result)
|
131
|
+
queue = Queue.new
|
132
|
+
Thread.new do
|
133
|
+
begin
|
134
|
+
client = setup_client
|
135
|
+
while (cmd = queue.pop)
|
136
|
+
msg, *args = cmd
|
137
|
+
case msg
|
138
|
+
when :block
|
139
|
+
result << client.block
|
140
|
+
when :unblock
|
141
|
+
client.unblock(args.first)
|
142
|
+
when :hello
|
143
|
+
result << client.greeting(true) # ignore result
|
144
|
+
when :sleep
|
145
|
+
client.sleep(args[0] || 0.5)
|
146
|
+
result << :slept
|
147
|
+
when :shutdown
|
148
|
+
client.shutdown
|
149
|
+
when :exit
|
150
|
+
result << :done
|
151
|
+
break
|
152
|
+
end
|
153
|
+
end
|
154
|
+
@clients.each { |c,t| t.close and break if c == client } #close the transport
|
155
|
+
rescue => e
|
156
|
+
raise e unless @catch_exceptions
|
157
|
+
end
|
158
|
+
end
|
159
|
+
queue
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should handle basic message passing" do
|
163
|
+
client = setup_client
|
164
|
+
client.greeting(true).should == Hello.new
|
165
|
+
client.greeting(false).should == Hello.new(:greeting => 'Aloha!')
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should handle concurrent clients" do
|
169
|
+
queue = Queue.new
|
170
|
+
trans_queue = Queue.new
|
171
|
+
4.times do
|
172
|
+
Thread.new(Thread.current) do |main_thread|
|
173
|
+
begin
|
174
|
+
queue.push setup_client(trans_queue).block
|
175
|
+
rescue => e
|
176
|
+
main_thread.raise e
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
4.times { trans_queue.pop }
|
181
|
+
setup_client.unblock(4)
|
182
|
+
4.times { queue.pop.should be_true }
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should handle messages from more than 5 long-lived connections" do
|
186
|
+
queues = []
|
187
|
+
result = Queue.new
|
188
|
+
7.times do |i|
|
189
|
+
queues << setup_client_thread(result)
|
190
|
+
Thread.pass if i == 4 # give the server time to accept connections
|
191
|
+
end
|
192
|
+
client = setup_client
|
193
|
+
# block 4 connections
|
194
|
+
4.times { |i| queues[i] << :block }
|
195
|
+
queues[4] << :hello
|
196
|
+
queues[5] << :hello
|
197
|
+
queues[6] << :hello
|
198
|
+
3.times { result.pop.should == Hello.new }
|
199
|
+
client.greeting(true).should == Hello.new
|
200
|
+
queues[5] << [:unblock, 4]
|
201
|
+
4.times { result.pop.should be_true }
|
202
|
+
queues[2] << :hello
|
203
|
+
result.pop.should == Hello.new
|
204
|
+
client.greeting(false).should == Hello.new(:greeting => 'Aloha!')
|
205
|
+
7.times { queues.shift << :exit }
|
206
|
+
client.greeting(true).should == Hello.new
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should shut down when asked" do
|
210
|
+
# connect first to ensure it's running
|
211
|
+
client = setup_client
|
212
|
+
client.greeting(false) # force a message pass
|
213
|
+
@server.shutdown
|
214
|
+
@server_thread.join(2).should be_an_instance_of(Thread)
|
215
|
+
end
|
216
|
+
|
217
|
+
it "should continue processing active messages when shutting down" do
|
218
|
+
result = Queue.new
|
219
|
+
client = setup_client_thread(result)
|
220
|
+
client << :sleep
|
221
|
+
sleep 0.1 # give the server time to start processing the client's message
|
222
|
+
@server.shutdown
|
223
|
+
@server_thread.join(2).should be_an_instance_of(Thread)
|
224
|
+
result.pop.should == :slept
|
225
|
+
end
|
226
|
+
|
227
|
+
it "should kill active messages when they don't expire while shutting down" do
|
228
|
+
result = Queue.new
|
229
|
+
client = setup_client_thread(result)
|
230
|
+
client << [:sleep, 10]
|
231
|
+
sleep 0.1 # start processing the client's message
|
232
|
+
@server.shutdown(1)
|
233
|
+
@catch_exceptions = true
|
234
|
+
@server_thread.join(3).should_not be_nil
|
235
|
+
result.should be_empty
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should allow shutting down in response to a message" do
|
239
|
+
client = setup_client
|
240
|
+
client.greeting(true).should == Hello.new
|
241
|
+
client.shutdown
|
242
|
+
@server_thread.join(2).should_not be_nil
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class ThriftProcessorSpec < Spec::ExampleGroup
|
4
|
+
include Thrift
|
5
|
+
|
6
|
+
class ProcessorSpec
|
7
|
+
include Thrift::Processor
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "Processor" do
|
11
|
+
before(:each) do
|
12
|
+
@processor = ProcessorSpec.new(mock("MockHandler"))
|
13
|
+
@prot = mock("MockProtocol")
|
14
|
+
end
|
15
|
+
|
16
|
+
def mock_trans(obj)
|
17
|
+
obj.should_receive(:trans).ordered.and_return do
|
18
|
+
mock("trans").tee do |trans|
|
19
|
+
trans.should_receive(:flush).ordered
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should call process_<message> when it receives that message" do
|
25
|
+
@prot.should_receive(:read_message_begin).ordered.and_return ['testMessage', MessageTypes::CALL, 17]
|
26
|
+
@processor.should_receive(:process_testMessage).with(17, @prot, @prot).ordered
|
27
|
+
@processor.process(@prot, @prot).should == true
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should raise an ApplicationException when the received message cannot be processed" do
|
31
|
+
@prot.should_receive(:read_message_begin).ordered.and_return ['testMessage', MessageTypes::CALL, 4]
|
32
|
+
@prot.should_receive(:skip).with(Types::STRUCT).ordered
|
33
|
+
@prot.should_receive(:read_message_end).ordered
|
34
|
+
@prot.should_receive(:write_message_begin).with('testMessage', MessageTypes::EXCEPTION, 4).ordered
|
35
|
+
ApplicationException.should_receive(:new).with(ApplicationException::UNKNOWN_METHOD, "Unknown function testMessage").and_return do
|
36
|
+
mock(ApplicationException).tee do |e|
|
37
|
+
e.should_receive(:write).with(@prot).ordered
|
38
|
+
end
|
39
|
+
end
|
40
|
+
@prot.should_receive(:write_message_end).ordered
|
41
|
+
mock_trans(@prot)
|
42
|
+
@processor.process(@prot, @prot)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should pass args off to the args class" do
|
46
|
+
args_class = mock("MockArgsClass")
|
47
|
+
args = mock("#<MockArgsClass:mock>").tee do |args|
|
48
|
+
args.should_receive(:read).with(@prot).ordered
|
49
|
+
end
|
50
|
+
args_class.should_receive(:new).and_return args
|
51
|
+
@prot.should_receive(:read_message_end).ordered
|
52
|
+
@processor.read_args(@prot, args_class).should eql(args)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should write out a reply when asked" do
|
56
|
+
@prot.should_receive(:write_message_begin).with('testMessage', MessageTypes::REPLY, 23).ordered
|
57
|
+
result = mock("MockResult")
|
58
|
+
result.should_receive(:write).with(@prot).ordered
|
59
|
+
@prot.should_receive(:write_message_end).ordered
|
60
|
+
mock_trans(@prot)
|
61
|
+
@processor.write_result(result, @prot, 'testMessage', 23)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require "thrift_native"
|
3
|
+
|
4
|
+
class ThriftProtocolSpec < Spec::ExampleGroup
|
5
|
+
include Thrift
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@trans = mock("MockTransport")
|
9
|
+
@prot = Protocol.new(@trans)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe Protocol do
|
13
|
+
# most of the methods are stubs, so we can ignore them
|
14
|
+
|
15
|
+
it "should make trans accessible" do
|
16
|
+
@prot.trans.should eql(@trans)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should write out a field nicely" do
|
20
|
+
@prot.should_receive(:write_field_begin).with('field', 'type', 'fid').ordered
|
21
|
+
@prot.should_receive(:write_type).with('type', 'value').ordered
|
22
|
+
@prot.should_receive(:write_field_end).ordered
|
23
|
+
@prot.write_field('field', 'type', 'fid', 'value')
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should write out the different types" do
|
27
|
+
@prot.should_receive(:write_bool).with('bool').ordered
|
28
|
+
@prot.should_receive(:write_byte).with('byte').ordered
|
29
|
+
@prot.should_receive(:write_double).with('double').ordered
|
30
|
+
@prot.should_receive(:write_i16).with('i16').ordered
|
31
|
+
@prot.should_receive(:write_i32).with('i32').ordered
|
32
|
+
@prot.should_receive(:write_i64).with('i64').ordered
|
33
|
+
@prot.should_receive(:write_string).with('string').ordered
|
34
|
+
struct = mock('Struct')
|
35
|
+
struct.should_receive(:write).with(@prot).ordered
|
36
|
+
@prot.write_type(Types::BOOL, 'bool')
|
37
|
+
@prot.write_type(Types::BYTE, 'byte')
|
38
|
+
@prot.write_type(Types::DOUBLE, 'double')
|
39
|
+
@prot.write_type(Types::I16, 'i16')
|
40
|
+
@prot.write_type(Types::I32, 'i32')
|
41
|
+
@prot.write_type(Types::I64, 'i64')
|
42
|
+
@prot.write_type(Types::STRING, 'string')
|
43
|
+
@prot.write_type(Types::STRUCT, struct)
|
44
|
+
# all other types are not implemented
|
45
|
+
[Types::STOP, Types::VOID, Types::MAP, Types::SET, Types::LIST].each do |type|
|
46
|
+
lambda { @prot.write_type(type, type.to_s) }.should raise_error(NotImplementedError)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should read the different types" do
|
51
|
+
@prot.should_receive(:read_bool).ordered
|
52
|
+
@prot.should_receive(:read_byte).ordered
|
53
|
+
@prot.should_receive(:read_i16).ordered
|
54
|
+
@prot.should_receive(:read_i32).ordered
|
55
|
+
@prot.should_receive(:read_i64).ordered
|
56
|
+
@prot.should_receive(:read_double).ordered
|
57
|
+
@prot.should_receive(:read_string).ordered
|
58
|
+
@prot.read_type(Types::BOOL)
|
59
|
+
@prot.read_type(Types::BYTE)
|
60
|
+
@prot.read_type(Types::I16)
|
61
|
+
@prot.read_type(Types::I32)
|
62
|
+
@prot.read_type(Types::I64)
|
63
|
+
@prot.read_type(Types::DOUBLE)
|
64
|
+
@prot.read_type(Types::STRING)
|
65
|
+
# all other types are not implemented
|
66
|
+
[Types::STOP, Types::VOID, Types::MAP, Types::SET, Types::LIST].each do |type|
|
67
|
+
lambda { @prot.read_type(type) }.should raise_error(NotImplementedError)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should skip the basic types" do
|
72
|
+
@prot.should_receive(:read_bool).ordered
|
73
|
+
@prot.should_receive(:read_byte).ordered
|
74
|
+
@prot.should_receive(:read_i16).ordered
|
75
|
+
@prot.should_receive(:read_i32).ordered
|
76
|
+
@prot.should_receive(:read_i64).ordered
|
77
|
+
@prot.should_receive(:read_double).ordered
|
78
|
+
@prot.should_receive(:read_string).ordered
|
79
|
+
@prot.skip(Types::BOOL)
|
80
|
+
@prot.skip(Types::BYTE)
|
81
|
+
@prot.skip(Types::I16)
|
82
|
+
@prot.skip(Types::I32)
|
83
|
+
@prot.skip(Types::I64)
|
84
|
+
@prot.skip(Types::DOUBLE)
|
85
|
+
@prot.skip(Types::STRING)
|
86
|
+
@prot.skip(Types::STOP) # should do absolutely nothing
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should skip structs" do
|
90
|
+
real_skip = @prot.method(:skip)
|
91
|
+
@prot.should_receive(:read_struct_begin).ordered
|
92
|
+
@prot.should_receive(:read_field_begin).exactly(4).times.and_return(
|
93
|
+
['field 1', Types::STRING, 1],
|
94
|
+
['field 2', Types::I32, 2],
|
95
|
+
['field 3', Types::MAP, 3],
|
96
|
+
[nil, Types::STOP, 0]
|
97
|
+
)
|
98
|
+
@prot.should_receive(:read_field_end).exactly(3).times
|
99
|
+
@prot.should_receive(:read_string).exactly(3).times
|
100
|
+
@prot.should_receive(:read_i32).ordered
|
101
|
+
@prot.should_receive(:read_map_begin).ordered.and_return([Types::STRING, Types::STRING, 1])
|
102
|
+
# @prot.should_receive(:read_string).exactly(2).times
|
103
|
+
@prot.should_receive(:read_map_end).ordered
|
104
|
+
@prot.should_receive(:read_struct_end).ordered
|
105
|
+
real_skip.call(Types::STRUCT)
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should skip maps" do
|
109
|
+
real_skip = @prot.method(:skip)
|
110
|
+
@prot.should_receive(:read_map_begin).ordered.and_return([Types::STRING, Types::STRUCT, 1])
|
111
|
+
@prot.should_receive(:read_string).ordered
|
112
|
+
@prot.should_receive(:read_struct_begin).ordered.and_return(["some_struct"])
|
113
|
+
@prot.should_receive(:read_field_begin).ordered.and_return([nil, Types::STOP, nil]);
|
114
|
+
@prot.should_receive(:read_struct_end).ordered
|
115
|
+
@prot.should_receive(:read_map_end).ordered
|
116
|
+
real_skip.call(Types::MAP)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should skip sets" do
|
120
|
+
real_skip = @prot.method(:skip)
|
121
|
+
@prot.should_receive(:read_set_begin).ordered.and_return([Types::I64, 9])
|
122
|
+
@prot.should_receive(:read_i64).ordered.exactly(9).times
|
123
|
+
@prot.should_receive(:read_set_end)
|
124
|
+
real_skip.call(Types::SET)
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should skip lists" do
|
128
|
+
real_skip = @prot.method(:skip)
|
129
|
+
@prot.should_receive(:read_list_begin).ordered.and_return([Types::DOUBLE, 11])
|
130
|
+
@prot.should_receive(:read_double).ordered.exactly(11).times
|
131
|
+
@prot.should_receive(:read_list_end)
|
132
|
+
real_skip.call(Types::LIST)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe ProtocolFactory do
|
137
|
+
it "should return nil" do
|
138
|
+
# returning nil since Protocol is just an abstract class
|
139
|
+
ProtocolFactory.new.get_protocol(mock("MockTransport")).should be_nil
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'thrift/serializer'
|
3
|
+
require File.dirname(__FILE__) + '/gen-rb/ThriftSpec_types'
|
4
|
+
|
5
|
+
class ThriftSerializerSpec < Spec::ExampleGroup
|
6
|
+
include Thrift
|
7
|
+
include SpecNamespace
|
8
|
+
|
9
|
+
describe Serializer do
|
10
|
+
it "should serialize structs to binary by default" do
|
11
|
+
serializer = Serializer.new(Thrift::BinaryProtocolAcceleratedFactory.new)
|
12
|
+
data = serializer.serialize(Hello.new(:greeting => "'Ello guv'nor!"))
|
13
|
+
data.should == "\x0B\x00\x01\x00\x00\x00\x0E'Ello guv'nor!\x00"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should serialize structs to the given protocol" do
|
17
|
+
protocol = Protocol.new(mock("transport"))
|
18
|
+
protocol.should_receive(:write_struct_begin).with("SpecNamespace::Hello")
|
19
|
+
protocol.should_receive(:write_field_begin).with("greeting", Types::STRING, 1)
|
20
|
+
protocol.should_receive(:write_string).with("Good day")
|
21
|
+
protocol.should_receive(:write_field_end)
|
22
|
+
protocol.should_receive(:write_field_stop)
|
23
|
+
protocol.should_receive(:write_struct_end)
|
24
|
+
protocolFactory = mock("ProtocolFactory")
|
25
|
+
protocolFactory.stub!(:get_protocol).and_return(protocol)
|
26
|
+
serializer = Serializer.new(protocolFactory)
|
27
|
+
serializer.serialize(Hello.new(:greeting => "Good day"))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe Deserializer do
|
32
|
+
it "should deserialize structs from binary by default" do
|
33
|
+
deserializer = Deserializer.new
|
34
|
+
data = "\x0B\x00\x01\x00\x00\x00\x0E'Ello guv'nor!\x00"
|
35
|
+
deserializer.deserialize(Hello.new, data).should == Hello.new(:greeting => "'Ello guv'nor!")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should deserialize structs from the given protocol" do
|
39
|
+
protocol = Protocol.new(mock("transport"))
|
40
|
+
protocol.should_receive(:read_struct_begin).and_return("SpecNamespace::Hello")
|
41
|
+
protocol.should_receive(:read_field_begin).and_return(["greeting", Types::STRING, 1],
|
42
|
+
[nil, Types::STOP, 0])
|
43
|
+
protocol.should_receive(:read_string).and_return("Good day")
|
44
|
+
protocol.should_receive(:read_field_end)
|
45
|
+
protocol.should_receive(:read_struct_end)
|
46
|
+
protocolFactory = mock("ProtocolFactory")
|
47
|
+
protocolFactory.stub!(:get_protocol).and_return(protocol)
|
48
|
+
deserializer = Deserializer.new(protocolFactory)
|
49
|
+
deserializer.deserialize(Hello.new, "").should == Hello.new(:greeting => "Good day")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/spec/server_spec.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class ThriftServerSpec < Spec::ExampleGroup
|
4
|
+
include Thrift
|
5
|
+
|
6
|
+
describe Server do
|
7
|
+
it "should default to TransportFactory and BinaryProtocolFactory when not specified" do
|
8
|
+
server = Server.new(mock("Processor"), mock("ServerTransport"))
|
9
|
+
server.instance_variable_get(:'@transportFactory').should be_an_instance_of(TransportFactory)
|
10
|
+
server.instance_variable_get(:'@protocolFactory').should be_an_instance_of(BinaryProtocolFactory)
|
11
|
+
end
|
12
|
+
|
13
|
+
# serve is a noop, so can't test that
|
14
|
+
end
|
15
|
+
|
16
|
+
shared_examples_for "servers" do
|
17
|
+
before(:each) do
|
18
|
+
@processor = mock("Processor")
|
19
|
+
@serverTrans = mock("ServerTransport")
|
20
|
+
@trans = mock("Transport")
|
21
|
+
@prot = mock("Protocol")
|
22
|
+
@client = mock("Client")
|
23
|
+
@server = server_type.new(@processor, @serverTrans, @trans, @prot)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe SimpleServer do
|
28
|
+
it_should_behave_like "servers"
|
29
|
+
|
30
|
+
def server_type
|
31
|
+
SimpleServer
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should serve in the main thread" do
|
35
|
+
@serverTrans.should_receive(:listen).ordered
|
36
|
+
@serverTrans.should_receive(:accept).exactly(3).times.and_return(@client)
|
37
|
+
@trans.should_receive(:get_transport).exactly(3).times.with(@client).and_return(@trans)
|
38
|
+
@prot.should_receive(:get_protocol).exactly(3).times.with(@trans).and_return(@prot)
|
39
|
+
x = 0
|
40
|
+
@processor.should_receive(:process).exactly(3).times.with(@prot, @prot).and_return do
|
41
|
+
case (x += 1)
|
42
|
+
when 1: raise Thrift::TransportException
|
43
|
+
when 2: raise Thrift::ProtocolException
|
44
|
+
when 3: throw :stop
|
45
|
+
end
|
46
|
+
end
|
47
|
+
@trans.should_receive(:close).exactly(3).times
|
48
|
+
@serverTrans.should_receive(:close).ordered
|
49
|
+
lambda { @server.serve }.should throw_symbol(:stop)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe ThreadedServer do
|
54
|
+
it_should_behave_like "servers"
|
55
|
+
|
56
|
+
def server_type
|
57
|
+
ThreadedServer
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should serve using threads" do
|
61
|
+
@serverTrans.should_receive(:listen).ordered
|
62
|
+
@serverTrans.should_receive(:accept).exactly(3).times.and_return(@client)
|
63
|
+
@trans.should_receive(:get_transport).exactly(3).times.with(@client).and_return(@trans)
|
64
|
+
@prot.should_receive(:get_protocol).exactly(3).times.with(@trans).and_return(@prot)
|
65
|
+
Thread.should_receive(:new).with(@prot, @trans).exactly(3).times.and_yield(@prot, @trans)
|
66
|
+
x = 0
|
67
|
+
@processor.should_receive(:process).exactly(3).times.with(@prot, @prot).and_return do
|
68
|
+
case (x += 1)
|
69
|
+
when 1: raise Thrift::TransportException
|
70
|
+
when 2: raise Thrift::ProtocolException
|
71
|
+
when 3: throw :stop
|
72
|
+
end
|
73
|
+
end
|
74
|
+
@trans.should_receive(:close).exactly(3).times
|
75
|
+
@serverTrans.should_receive(:close).ordered
|
76
|
+
lambda { @server.serve }.should throw_symbol(:stop)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe ThreadPoolServer do
|
81
|
+
it_should_behave_like "servers"
|
82
|
+
|
83
|
+
def server_type
|
84
|
+
# put this stuff here so it runs before the server is created
|
85
|
+
@threadQ = mock("SizedQueue")
|
86
|
+
SizedQueue.should_receive(:new).with(20).and_return(@threadQ)
|
87
|
+
@excQ = mock("Queue")
|
88
|
+
Queue.should_receive(:new).and_return(@excQ)
|
89
|
+
ThreadPoolServer
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should set up the queues" do
|
93
|
+
@server.instance_variable_get(:'@thread_q').should be(@threadQ)
|
94
|
+
@server.instance_variable_get(:'@exception_q').should be(@excQ)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should serve inside a thread" do
|
98
|
+
Thread.should_receive(:new).and_return do |block|
|
99
|
+
@server.should_receive(:serve)
|
100
|
+
block.call
|
101
|
+
@server.rspec_verify
|
102
|
+
end
|
103
|
+
@excQ.should_receive(:pop).and_throw(:popped)
|
104
|
+
lambda { @server.rescuable_serve }.should throw_symbol(:popped)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should avoid running the server twice when retrying rescuable_serve" do
|
108
|
+
Thread.should_receive(:new).and_return do |block|
|
109
|
+
@server.should_receive(:serve)
|
110
|
+
block.call
|
111
|
+
@server.rspec_verify
|
112
|
+
end
|
113
|
+
@excQ.should_receive(:pop).twice.and_throw(:popped)
|
114
|
+
lambda { @server.rescuable_serve }.should throw_symbol(:popped)
|
115
|
+
lambda { @server.rescuable_serve }.should throw_symbol(:popped)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should serve using a thread pool" do
|
119
|
+
@serverTrans.should_receive(:listen).ordered
|
120
|
+
@threadQ.should_receive(:push).with(:token)
|
121
|
+
@threadQ.should_receive(:pop)
|
122
|
+
Thread.should_receive(:new).and_yield
|
123
|
+
@serverTrans.should_receive(:accept).exactly(3).times.and_return(@client)
|
124
|
+
@trans.should_receive(:get_transport).exactly(3).times.and_return(@trans)
|
125
|
+
@prot.should_receive(:get_protocol).exactly(3).times.and_return(@prot)
|
126
|
+
x = 0
|
127
|
+
error = RuntimeError.new("Stopped")
|
128
|
+
@processor.should_receive(:process).exactly(3).times.with(@prot, @prot).and_return do
|
129
|
+
case (x += 1)
|
130
|
+
when 1: raise Thrift::TransportException
|
131
|
+
when 2: raise Thrift::ProtocolException
|
132
|
+
when 3: raise error
|
133
|
+
end
|
134
|
+
end
|
135
|
+
@trans.should_receive(:close).exactly(3).times
|
136
|
+
@excQ.should_receive(:push).with(error).and_throw(:stop)
|
137
|
+
@serverTrans.should_receive(:close)
|
138
|
+
lambda { @server.serve }.should throw_symbol(:stop)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|