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.
Files changed (82) hide show
  1. data/CHANGELOG +2 -0
  2. data/COPYING +14 -0
  3. data/LICENSE +14 -0
  4. data/Makefile.am +15 -0
  5. data/Manifest +78 -0
  6. data/README +30 -0
  7. data/Rakefile +102 -0
  8. data/benchmark/Benchmark.thrift +5 -0
  9. data/benchmark/benchmark.rb +254 -0
  10. data/benchmark/client.rb +56 -0
  11. data/benchmark/gen-rb/BenchmarkService.rb +81 -0
  12. data/benchmark/gen-rb/Benchmark_constants.rb +11 -0
  13. data/benchmark/gen-rb/Benchmark_types.rb +10 -0
  14. data/benchmark/server.rb +64 -0
  15. data/benchmark/thin_server.rb +26 -0
  16. data/ext/binary_protocol_accelerated.c +463 -0
  17. data/ext/binary_protocol_accelerated.h +1 -0
  18. data/ext/constants.h +77 -0
  19. data/ext/extconf.rb +7 -0
  20. data/ext/memory_buffer.c +52 -0
  21. data/ext/memory_buffer.h +1 -0
  22. data/ext/protocol.c +166 -0
  23. data/ext/protocol.h +1 -0
  24. data/ext/struct.c +574 -0
  25. data/ext/struct.h +48 -0
  26. data/ext/thrift_native.c +173 -0
  27. data/lib/thrift/client.rb +44 -0
  28. data/lib/thrift/deprecation.rb +155 -0
  29. data/lib/thrift/exceptions.rb +65 -0
  30. data/lib/thrift/processor.rb +39 -0
  31. data/lib/thrift/protocol/binaryprotocol.rb +213 -0
  32. data/lib/thrift/protocol/binaryprotocolaccelerated.rb +19 -0
  33. data/lib/thrift/protocol/tbinaryprotocol.rb +2 -0
  34. data/lib/thrift/protocol/tprotocol.rb +2 -0
  35. data/lib/thrift/protocol.rb +270 -0
  36. data/lib/thrift/serializer.rb +27 -0
  37. data/lib/thrift/server/httpserver.rb +44 -0
  38. data/lib/thrift/server/nonblockingserver.rb +278 -0
  39. data/lib/thrift/server/thttpserver.rb +2 -0
  40. data/lib/thrift/server/tserver.rb +2 -0
  41. data/lib/thrift/server.rb +135 -0
  42. data/lib/thrift/struct.rb +272 -0
  43. data/lib/thrift/thrift.rb +14 -0
  44. data/lib/thrift/transport/httpclient.rb +29 -0
  45. data/lib/thrift/transport/socket.rb +167 -0
  46. data/lib/thrift/transport/thttpclient.rb +2 -0
  47. data/lib/thrift/transport/tsocket.rb +2 -0
  48. data/lib/thrift/transport/ttransport.rb +2 -0
  49. data/lib/thrift/transport/unixsocket.rb +58 -0
  50. data/lib/thrift/transport.rb +319 -0
  51. data/lib/thrift/types.rb +83 -0
  52. data/lib/thrift.rb +28 -0
  53. data/setup.rb +1585 -0
  54. data/spec/ThriftSpec.thrift +46 -0
  55. data/spec/backwards_compatibility_spec.rb +136 -0
  56. data/spec/binaryprotocol_spec.rb +45 -0
  57. data/spec/binaryprotocol_spec_shared.rb +274 -0
  58. data/spec/binaryprotocolaccelerated_spec.rb +101 -0
  59. data/spec/client_spec.rb +81 -0
  60. data/spec/deprecation_spec.rb +443 -0
  61. data/spec/exception_spec.rb +123 -0
  62. data/spec/gen-rb/NonblockingService.rb +268 -0
  63. data/spec/gen-rb/ThriftSpec_constants.rb +11 -0
  64. data/spec/gen-rb/ThriftSpec_types.rb +134 -0
  65. data/spec/httpclient_spec.rb +31 -0
  66. data/spec/httpserver_spec.rb +98 -0
  67. data/spec/nonblockingserver_spec.rb +245 -0
  68. data/spec/processor_spec.rb +64 -0
  69. data/spec/protocol_spec.rb +142 -0
  70. data/spec/serializer_spec.rb +52 -0
  71. data/spec/server_spec.rb +141 -0
  72. data/spec/socket_spec.rb +97 -0
  73. data/spec/socket_spec_shared.rb +85 -0
  74. data/spec/spec_helper.rb +35 -0
  75. data/spec/struct_spec.rb +244 -0
  76. data/spec/transport_spec.rb +359 -0
  77. data/spec/types_spec.rb +98 -0
  78. data/spec/unixsocket_spec.rb +90 -0
  79. data/thrift.gemspec +33 -0
  80. data.tar.gz.sig +0 -0
  81. metadata +200 -0
  82. 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
@@ -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