thrift 0.0.751142

Sign up to get free protection for your applications and to get access to all the features.
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