tiny_thrift 1.0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/README +43 -0
  3. data/benchmark/Benchmark.thrift +24 -0
  4. data/benchmark/benchmark.rb +271 -0
  5. data/benchmark/client.rb +74 -0
  6. data/benchmark/server.rb +82 -0
  7. data/benchmark/thin_server.rb +44 -0
  8. data/ext/binary_protocol_accelerated.c +460 -0
  9. data/ext/binary_protocol_accelerated.h +20 -0
  10. data/ext/bytes.c +36 -0
  11. data/ext/bytes.h +31 -0
  12. data/ext/compact_protocol.c +635 -0
  13. data/ext/compact_protocol.h +20 -0
  14. data/ext/constants.h +96 -0
  15. data/ext/extconf.rb +32 -0
  16. data/ext/macros.h +41 -0
  17. data/ext/memory_buffer.c +134 -0
  18. data/ext/memory_buffer.h +20 -0
  19. data/ext/protocol.c +0 -0
  20. data/ext/protocol.h +0 -0
  21. data/ext/strlcpy.c +41 -0
  22. data/ext/strlcpy.h +34 -0
  23. data/ext/struct.c +688 -0
  24. data/ext/struct.h +25 -0
  25. data/ext/thrift_native.c +195 -0
  26. data/lib/thrift.rb +66 -0
  27. data/lib/thrift/bytes.rb +131 -0
  28. data/lib/thrift/client.rb +62 -0
  29. data/lib/thrift/core_ext.rb +23 -0
  30. data/lib/thrift/core_ext/fixnum.rb +29 -0
  31. data/lib/thrift/exceptions.rb +87 -0
  32. data/lib/thrift/processor.rb +57 -0
  33. data/lib/thrift/protocol/base_protocol.rb +377 -0
  34. data/lib/thrift/protocol/binary_protocol.rb +237 -0
  35. data/lib/thrift/protocol/binary_protocol_accelerated.rb +39 -0
  36. data/lib/thrift/protocol/compact_protocol.rb +434 -0
  37. data/lib/thrift/protocol/json_protocol.rb +769 -0
  38. data/lib/thrift/serializer/deserializer.rb +33 -0
  39. data/lib/thrift/serializer/serializer.rb +34 -0
  40. data/lib/thrift/server/base_server.rb +31 -0
  41. data/lib/thrift/server/mongrel_http_server.rb +60 -0
  42. data/lib/thrift/server/nonblocking_server.rb +305 -0
  43. data/lib/thrift/server/simple_server.rb +43 -0
  44. data/lib/thrift/server/thin_http_server.rb +91 -0
  45. data/lib/thrift/server/thread_pool_server.rb +75 -0
  46. data/lib/thrift/server/threaded_server.rb +47 -0
  47. data/lib/thrift/struct.rb +237 -0
  48. data/lib/thrift/struct_union.rb +192 -0
  49. data/lib/thrift/thrift_native.rb +24 -0
  50. data/lib/thrift/transport/base_server_transport.rb +37 -0
  51. data/lib/thrift/transport/base_transport.rb +109 -0
  52. data/lib/thrift/transport/buffered_transport.rb +114 -0
  53. data/lib/thrift/transport/framed_transport.rb +117 -0
  54. data/lib/thrift/transport/http_client_transport.rb +56 -0
  55. data/lib/thrift/transport/io_stream_transport.rb +39 -0
  56. data/lib/thrift/transport/memory_buffer_transport.rb +125 -0
  57. data/lib/thrift/transport/server_socket.rb +63 -0
  58. data/lib/thrift/transport/socket.rb +139 -0
  59. data/lib/thrift/transport/unix_server_socket.rb +60 -0
  60. data/lib/thrift/transport/unix_socket.rb +40 -0
  61. data/lib/thrift/types.rb +101 -0
  62. data/lib/thrift/union.rb +179 -0
  63. data/lib/tiny_thrift.rb +1 -0
  64. data/spec/ThriftSpec.thrift +183 -0
  65. data/spec/base_protocol_spec.rb +217 -0
  66. data/spec/base_transport_spec.rb +350 -0
  67. data/spec/binary_protocol_accelerated_spec.rb +42 -0
  68. data/spec/binary_protocol_spec.rb +66 -0
  69. data/spec/binary_protocol_spec_shared.rb +455 -0
  70. data/spec/bytes_spec.rb +160 -0
  71. data/spec/client_spec.rb +99 -0
  72. data/spec/compact_protocol_spec.rb +143 -0
  73. data/spec/exception_spec.rb +141 -0
  74. data/spec/http_client_spec.rb +120 -0
  75. data/spec/json_protocol_spec.rb +513 -0
  76. data/spec/nonblocking_server_spec.rb +263 -0
  77. data/spec/processor_spec.rb +80 -0
  78. data/spec/serializer_spec.rb +67 -0
  79. data/spec/server_socket_spec.rb +79 -0
  80. data/spec/server_spec.rb +147 -0
  81. data/spec/socket_spec.rb +61 -0
  82. data/spec/socket_spec_shared.rb +104 -0
  83. data/spec/spec_helper.rb +61 -0
  84. data/spec/struct_nested_containers_spec.rb +191 -0
  85. data/spec/struct_spec.rb +293 -0
  86. data/spec/thin_http_server_spec.rb +141 -0
  87. data/spec/types_spec.rb +115 -0
  88. data/spec/union_spec.rb +203 -0
  89. data/spec/unix_socket_spec.rb +107 -0
  90. metadata +313 -0
@@ -0,0 +1,217 @@
1
+ #
2
+ # Licensed to the Apache Software Foundation (ASF) under one
3
+ # or more contributor license agreements. See the NOTICE file
4
+ # distributed with this work for additional information
5
+ # regarding copyright ownership. The ASF licenses this file
6
+ # to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance
8
+ # with the License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing,
13
+ # software distributed under the License is distributed on an
14
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ # KIND, either express or implied. See the License for the
16
+ # specific language governing permissions and limitations
17
+ # under the License.
18
+ #
19
+
20
+ require 'spec_helper'
21
+
22
+ describe 'BaseProtocol' do
23
+
24
+ before(:each) do
25
+ @trans = mock("MockTransport")
26
+ @prot = Thrift::BaseProtocol.new(@trans)
27
+ end
28
+
29
+ describe Thrift::BaseProtocol do
30
+ # most of the methods are stubs, so we can ignore them
31
+
32
+ it "should make trans accessible" do
33
+ @prot.trans.should eql(@trans)
34
+ end
35
+
36
+ it 'should write out a field nicely (deprecated write_field signature)' do
37
+ @prot.should_receive(:write_field_begin).with('field', 'type', 'fid').ordered
38
+ @prot.should_receive(:write_type).with({:name => 'field', :type => 'type'}, 'value').ordered
39
+ @prot.should_receive(:write_field_end).ordered
40
+ @prot.write_field('field', 'type', 'fid', 'value')
41
+ end
42
+
43
+ it 'should write out a field nicely' do
44
+ @prot.should_receive(:write_field_begin).with('field', 'type', 'fid').ordered
45
+ @prot.should_receive(:write_type).with({:name => 'field', :type => 'type', :binary => false}, 'value').ordered
46
+ @prot.should_receive(:write_field_end).ordered
47
+ @prot.write_field({:name => 'field', :type => 'type', :binary => false}, 'fid', 'value')
48
+ end
49
+
50
+ it 'should write out the different types (deprecated write_type signature)' do
51
+ @prot.should_receive(:write_bool).with('bool').ordered
52
+ @prot.should_receive(:write_byte).with('byte').ordered
53
+ @prot.should_receive(:write_double).with('double').ordered
54
+ @prot.should_receive(:write_i16).with('i16').ordered
55
+ @prot.should_receive(:write_i32).with('i32').ordered
56
+ @prot.should_receive(:write_i64).with('i64').ordered
57
+ @prot.should_receive(:write_string).with('string').ordered
58
+ struct = mock('Struct')
59
+ struct.should_receive(:write).with(@prot).ordered
60
+ @prot.write_type(Thrift::Types::BOOL, 'bool')
61
+ @prot.write_type(Thrift::Types::BYTE, 'byte')
62
+ @prot.write_type(Thrift::Types::DOUBLE, 'double')
63
+ @prot.write_type(Thrift::Types::I16, 'i16')
64
+ @prot.write_type(Thrift::Types::I32, 'i32')
65
+ @prot.write_type(Thrift::Types::I64, 'i64')
66
+ @prot.write_type(Thrift::Types::STRING, 'string')
67
+ @prot.write_type(Thrift::Types::STRUCT, struct)
68
+ # all other types are not implemented
69
+ [Thrift::Types::STOP, Thrift::Types::VOID, Thrift::Types::MAP, Thrift::Types::SET, Thrift::Types::LIST].each do |type|
70
+ expect { @prot.write_type(type, type.to_s) }.to raise_error(NotImplementedError)
71
+ end
72
+ end
73
+
74
+ it 'should write out the different types' do
75
+ @prot.should_receive(:write_bool).with('bool').ordered
76
+ @prot.should_receive(:write_byte).with('byte').ordered
77
+ @prot.should_receive(:write_double).with('double').ordered
78
+ @prot.should_receive(:write_i16).with('i16').ordered
79
+ @prot.should_receive(:write_i32).with('i32').ordered
80
+ @prot.should_receive(:write_i64).with('i64').ordered
81
+ @prot.should_receive(:write_string).with('string').ordered
82
+ @prot.should_receive(:write_binary).with('binary').ordered
83
+ struct = mock('Struct')
84
+ struct.should_receive(:write).with(@prot).ordered
85
+ @prot.write_type({:type => Thrift::Types::BOOL}, 'bool')
86
+ @prot.write_type({:type => Thrift::Types::BYTE}, 'byte')
87
+ @prot.write_type({:type => Thrift::Types::DOUBLE}, 'double')
88
+ @prot.write_type({:type => Thrift::Types::I16}, 'i16')
89
+ @prot.write_type({:type => Thrift::Types::I32}, 'i32')
90
+ @prot.write_type({:type => Thrift::Types::I64}, 'i64')
91
+ @prot.write_type({:type => Thrift::Types::STRING}, 'string')
92
+ @prot.write_type({:type => Thrift::Types::STRING, :binary => true}, 'binary')
93
+ @prot.write_type({:type => Thrift::Types::STRUCT}, struct)
94
+ # all other types are not implemented
95
+ [Thrift::Types::STOP, Thrift::Types::VOID, Thrift::Types::MAP, Thrift::Types::SET, Thrift::Types::LIST].each do |type|
96
+ expect { @prot.write_type({:type => type}, type.to_s) }.to raise_error(NotImplementedError)
97
+ end
98
+ end
99
+
100
+ it 'should read the different types (deprecated read_type signature)' do
101
+ @prot.should_receive(:read_bool).ordered
102
+ @prot.should_receive(:read_byte).ordered
103
+ @prot.should_receive(:read_i16).ordered
104
+ @prot.should_receive(:read_i32).ordered
105
+ @prot.should_receive(:read_i64).ordered
106
+ @prot.should_receive(:read_double).ordered
107
+ @prot.should_receive(:read_string).ordered
108
+ @prot.read_type(Thrift::Types::BOOL)
109
+ @prot.read_type(Thrift::Types::BYTE)
110
+ @prot.read_type(Thrift::Types::I16)
111
+ @prot.read_type(Thrift::Types::I32)
112
+ @prot.read_type(Thrift::Types::I64)
113
+ @prot.read_type(Thrift::Types::DOUBLE)
114
+ @prot.read_type(Thrift::Types::STRING)
115
+ # all other types are not implemented
116
+ [Thrift::Types::STOP, Thrift::Types::VOID, Thrift::Types::MAP,
117
+ Thrift::Types::SET, Thrift::Types::LIST, Thrift::Types::STRUCT].each do |type|
118
+ expect { @prot.read_type(type) }.to raise_error(NotImplementedError)
119
+ end
120
+ end
121
+
122
+ it 'should read the different types' do
123
+ @prot.should_receive(:read_bool).ordered
124
+ @prot.should_receive(:read_byte).ordered
125
+ @prot.should_receive(:read_i16).ordered
126
+ @prot.should_receive(:read_i32).ordered
127
+ @prot.should_receive(:read_i64).ordered
128
+ @prot.should_receive(:read_double).ordered
129
+ @prot.should_receive(:read_string).ordered
130
+ @prot.should_receive(:read_binary).ordered
131
+ @prot.read_type({:type => Thrift::Types::BOOL})
132
+ @prot.read_type({:type => Thrift::Types::BYTE})
133
+ @prot.read_type({:type => Thrift::Types::I16})
134
+ @prot.read_type({:type => Thrift::Types::I32})
135
+ @prot.read_type({:type => Thrift::Types::I64})
136
+ @prot.read_type({:type => Thrift::Types::DOUBLE})
137
+ @prot.read_type({:type => Thrift::Types::STRING})
138
+ @prot.read_type({:type => Thrift::Types::STRING, :binary => true})
139
+ # all other types are not implemented
140
+ [Thrift::Types::STOP, Thrift::Types::VOID, Thrift::Types::MAP,
141
+ Thrift::Types::SET, Thrift::Types::LIST, Thrift::Types::STRUCT].each do |type|
142
+ expect { @prot.read_type({:type => type}) }.to raise_error(NotImplementedError)
143
+ end
144
+ end
145
+
146
+ it "should skip the basic types" do
147
+ @prot.should_receive(:read_bool).ordered
148
+ @prot.should_receive(:read_byte).ordered
149
+ @prot.should_receive(:read_i16).ordered
150
+ @prot.should_receive(:read_i32).ordered
151
+ @prot.should_receive(:read_i64).ordered
152
+ @prot.should_receive(:read_double).ordered
153
+ @prot.should_receive(:read_string).ordered
154
+ @prot.skip(Thrift::Types::BOOL)
155
+ @prot.skip(Thrift::Types::BYTE)
156
+ @prot.skip(Thrift::Types::I16)
157
+ @prot.skip(Thrift::Types::I32)
158
+ @prot.skip(Thrift::Types::I64)
159
+ @prot.skip(Thrift::Types::DOUBLE)
160
+ @prot.skip(Thrift::Types::STRING)
161
+ @prot.skip(Thrift::Types::STOP) # should do absolutely nothing
162
+ end
163
+
164
+ it "should skip structs" do
165
+ real_skip = @prot.method(:skip)
166
+ @prot.should_receive(:read_struct_begin).ordered
167
+ @prot.should_receive(:read_field_begin).exactly(4).times.and_return(
168
+ ['field 1', Thrift::Types::STRING, 1],
169
+ ['field 2', Thrift::Types::I32, 2],
170
+ ['field 3', Thrift::Types::MAP, 3],
171
+ [nil, Thrift::Types::STOP, 0]
172
+ )
173
+ @prot.should_receive(:read_field_end).exactly(3).times
174
+ @prot.should_receive(:read_string).exactly(3).times
175
+ @prot.should_receive(:read_i32).ordered
176
+ @prot.should_receive(:read_map_begin).ordered.and_return([Thrift::Types::STRING, Thrift::Types::STRING, 1])
177
+ # @prot.should_receive(:read_string).exactly(2).times
178
+ @prot.should_receive(:read_map_end).ordered
179
+ @prot.should_receive(:read_struct_end).ordered
180
+ real_skip.call(Thrift::Types::STRUCT)
181
+ end
182
+
183
+ it "should skip maps" do
184
+ real_skip = @prot.method(:skip)
185
+ @prot.should_receive(:read_map_begin).ordered.and_return([Thrift::Types::STRING, Thrift::Types::STRUCT, 1])
186
+ @prot.should_receive(:read_string).ordered
187
+ @prot.should_receive(:read_struct_begin).ordered.and_return(["some_struct"])
188
+ @prot.should_receive(:read_field_begin).ordered.and_return([nil, Thrift::Types::STOP, nil]);
189
+ @prot.should_receive(:read_struct_end).ordered
190
+ @prot.should_receive(:read_map_end).ordered
191
+ real_skip.call(Thrift::Types::MAP)
192
+ end
193
+
194
+ it "should skip sets" do
195
+ real_skip = @prot.method(:skip)
196
+ @prot.should_receive(:read_set_begin).ordered.and_return([Thrift::Types::I64, 9])
197
+ @prot.should_receive(:read_i64).ordered.exactly(9).times
198
+ @prot.should_receive(:read_set_end)
199
+ real_skip.call(Thrift::Types::SET)
200
+ end
201
+
202
+ it "should skip lists" do
203
+ real_skip = @prot.method(:skip)
204
+ @prot.should_receive(:read_list_begin).ordered.and_return([Thrift::Types::DOUBLE, 11])
205
+ @prot.should_receive(:read_double).ordered.exactly(11).times
206
+ @prot.should_receive(:read_list_end)
207
+ real_skip.call(Thrift::Types::LIST)
208
+ end
209
+ end
210
+
211
+ describe Thrift::BaseProtocolFactory do
212
+ it "should raise NotImplementedError" do
213
+ # returning nil since Protocol is just an abstract class
214
+ lambda {Thrift::BaseProtocolFactory.new.get_protocol(mock("MockTransport"))}.should raise_error(NotImplementedError)
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,350 @@
1
+ #
2
+ # Licensed to the Apache Software Foundation (ASF) under one
3
+ # or more contributor license agreements. See the NOTICE file
4
+ # distributed with this work for additional information
5
+ # regarding copyright ownership. The ASF licenses this file
6
+ # to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance
8
+ # with the License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing,
13
+ # software distributed under the License is distributed on an
14
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ # KIND, either express or implied. See the License for the
16
+ # specific language governing permissions and limitations
17
+ # under the License.
18
+ #
19
+
20
+ require 'spec_helper'
21
+
22
+ describe 'BaseTransport' do
23
+
24
+ describe Thrift::TransportException do
25
+ it "should make type accessible" do
26
+ exc = Thrift::TransportException.new(Thrift::TransportException::ALREADY_OPEN, "msg")
27
+ exc.type.should == Thrift::TransportException::ALREADY_OPEN
28
+ exc.message.should == "msg"
29
+ end
30
+ end
31
+
32
+ describe Thrift::BaseTransport do
33
+ it "should read the specified size" do
34
+ transport = Thrift::BaseTransport.new
35
+ transport.should_receive(:read).with(40).ordered.and_return("10 letters")
36
+ transport.should_receive(:read).with(30).ordered.and_return("fifteen letters")
37
+ transport.should_receive(:read).with(15).ordered.and_return("more characters")
38
+ transport.read_all(40).should == "10 lettersfifteen lettersmore characters"
39
+ end
40
+
41
+ it "should stub out the rest of the methods" do
42
+ # can't test for stubbiness, so just make sure they're defined
43
+ [:open?, :open, :close, :read, :write, :flush].each do |sym|
44
+ Thrift::BaseTransport.method_defined?(sym).should be_true
45
+ end
46
+ end
47
+
48
+ it "should alias << to write" do
49
+ Thrift::BaseTransport.instance_method(:<<).should == Thrift::BaseTransport.instance_method(:write)
50
+ end
51
+ end
52
+
53
+ describe Thrift::BaseServerTransport do
54
+ it "should stub out its methods" do
55
+ [:listen, :accept, :close].each do |sym|
56
+ Thrift::BaseServerTransport.method_defined?(sym).should be_true
57
+ end
58
+ end
59
+ end
60
+
61
+ describe Thrift::BaseTransportFactory do
62
+ it "should return the transport it's given" do
63
+ transport = mock("Transport")
64
+ Thrift::BaseTransportFactory.new.get_transport(transport).should eql(transport)
65
+ end
66
+ end
67
+
68
+ describe Thrift::BufferedTransport do
69
+ it "should pass through everything but write/flush/read" do
70
+ trans = mock("Transport")
71
+ trans.should_receive(:open?).ordered.and_return("+ open?")
72
+ trans.should_receive(:open).ordered.and_return("+ open")
73
+ trans.should_receive(:flush).ordered # from the close
74
+ trans.should_receive(:close).ordered.and_return("+ close")
75
+ btrans = Thrift::BufferedTransport.new(trans)
76
+ btrans.open?.should == "+ open?"
77
+ btrans.open.should == "+ open"
78
+ btrans.close.should == "+ close"
79
+ end
80
+
81
+ it "should buffer reads in chunks of #{Thrift::BufferedTransport::DEFAULT_BUFFER}" do
82
+ trans = mock("Transport")
83
+ trans.should_receive(:read).with(Thrift::BufferedTransport::DEFAULT_BUFFER).and_return("lorum ipsum dolor emet")
84
+ btrans = Thrift::BufferedTransport.new(trans)
85
+ btrans.read(6).should == "lorum "
86
+ btrans.read(6).should == "ipsum "
87
+ btrans.read(6).should == "dolor "
88
+ btrans.read(6).should == "emet"
89
+ end
90
+
91
+ it "should buffer writes and send them on flush" do
92
+ trans = mock("Transport")
93
+ btrans = Thrift::BufferedTransport.new(trans)
94
+ btrans.write("one/")
95
+ btrans.write("two/")
96
+ btrans.write("three/")
97
+ trans.should_receive(:write).with("one/two/three/").ordered
98
+ trans.should_receive(:flush).ordered
99
+ btrans.flush
100
+ end
101
+
102
+ it "should only send buffered data once" do
103
+ trans = mock("Transport")
104
+ btrans = Thrift::BufferedTransport.new(trans)
105
+ btrans.write("one/")
106
+ btrans.write("two/")
107
+ btrans.write("three/")
108
+ trans.should_receive(:write).with("one/two/three/")
109
+ trans.stub!(:flush)
110
+ btrans.flush
111
+ # Nothing to flush with no data
112
+ btrans.flush
113
+ end
114
+
115
+ it "should flush on close" do
116
+ trans = mock("Transport")
117
+ trans.should_receive(:close)
118
+ btrans = Thrift::BufferedTransport.new(trans)
119
+ btrans.should_receive(:flush)
120
+ btrans.close
121
+ end
122
+
123
+ it "should not write to socket if there's no data" do
124
+ trans = mock("Transport")
125
+ trans.should_receive(:flush)
126
+ btrans = Thrift::BufferedTransport.new(trans)
127
+ btrans.flush
128
+ end
129
+ end
130
+
131
+ describe Thrift::BufferedTransportFactory do
132
+ it "should wrap the given transport in a BufferedTransport" do
133
+ trans = mock("Transport")
134
+ btrans = mock("BufferedTransport")
135
+ Thrift::BufferedTransport.should_receive(:new).with(trans).and_return(btrans)
136
+ Thrift::BufferedTransportFactory.new.get_transport(trans).should == btrans
137
+ end
138
+ end
139
+
140
+ describe Thrift::FramedTransport do
141
+ before(:each) do
142
+ @trans = mock("Transport")
143
+ end
144
+
145
+ it "should pass through open?/open/close" do
146
+ ftrans = Thrift::FramedTransport.new(@trans)
147
+ @trans.should_receive(:open?).ordered.and_return("+ open?")
148
+ @trans.should_receive(:open).ordered.and_return("+ open")
149
+ @trans.should_receive(:close).ordered.and_return("+ close")
150
+ ftrans.open?.should == "+ open?"
151
+ ftrans.open.should == "+ open"
152
+ ftrans.close.should == "+ close"
153
+ end
154
+
155
+ it "should pass through read when read is turned off" do
156
+ ftrans = Thrift::FramedTransport.new(@trans, false, true)
157
+ @trans.should_receive(:read).with(17).ordered.and_return("+ read")
158
+ ftrans.read(17).should == "+ read"
159
+ end
160
+
161
+ it "should pass through write/flush when write is turned off" do
162
+ ftrans = Thrift::FramedTransport.new(@trans, true, false)
163
+ @trans.should_receive(:write).with("foo").ordered.and_return("+ write")
164
+ @trans.should_receive(:flush).ordered.and_return("+ flush")
165
+ ftrans.write("foo").should == "+ write"
166
+ ftrans.flush.should == "+ flush"
167
+ end
168
+
169
+ it "should return a full frame if asked for >= the frame's length" do
170
+ frame = "this is a frame"
171
+ @trans.should_receive(:read_all).with(4).and_return("\000\000\000\017")
172
+ @trans.should_receive(:read_all).with(frame.length).and_return(frame)
173
+ Thrift::FramedTransport.new(@trans).read(frame.length + 10).should == frame
174
+ end
175
+
176
+ it "should return slices of the frame when asked for < the frame's length" do
177
+ frame = "this is a frame"
178
+ @trans.should_receive(:read_all).with(4).and_return("\000\000\000\017")
179
+ @trans.should_receive(:read_all).with(frame.length).and_return(frame)
180
+ ftrans = Thrift::FramedTransport.new(@trans)
181
+ ftrans.read(4).should == "this"
182
+ ftrans.read(4).should == " is "
183
+ ftrans.read(16).should == "a frame"
184
+ end
185
+
186
+ it "should return nothing if asked for <= 0" do
187
+ Thrift::FramedTransport.new(@trans).read(-2).should == ""
188
+ end
189
+
190
+ it "should pull a new frame when the first is exhausted" do
191
+ frame = "this is a frame"
192
+ frame2 = "yet another frame"
193
+ @trans.should_receive(:read_all).with(4).and_return("\000\000\000\017", "\000\000\000\021")
194
+ @trans.should_receive(:read_all).with(frame.length).and_return(frame)
195
+ @trans.should_receive(:read_all).with(frame2.length).and_return(frame2)
196
+ ftrans = Thrift::FramedTransport.new(@trans)
197
+ ftrans.read(4).should == "this"
198
+ ftrans.read(8).should == " is a fr"
199
+ ftrans.read(6).should == "ame"
200
+ ftrans.read(4).should == "yet "
201
+ ftrans.read(16).should == "another frame"
202
+ end
203
+
204
+ it "should buffer writes" do
205
+ ftrans = Thrift::FramedTransport.new(@trans)
206
+ @trans.should_not_receive(:write)
207
+ ftrans.write("foo")
208
+ ftrans.write("bar")
209
+ ftrans.write("this is a frame")
210
+ end
211
+
212
+ it "should write slices of the buffer" do
213
+ ftrans = Thrift::FramedTransport.new(@trans)
214
+ ftrans.write("foobar", 3)
215
+ ftrans.write("barfoo", 1)
216
+ @trans.stub!(:flush)
217
+ @trans.should_receive(:write).with("\000\000\000\004foob")
218
+ ftrans.flush
219
+ end
220
+
221
+ it "should flush frames with a 4-byte header" do
222
+ ftrans = Thrift::FramedTransport.new(@trans)
223
+ @trans.should_receive(:write).with("\000\000\000\035one/two/three/this is a frame").ordered
224
+ @trans.should_receive(:flush).ordered
225
+ ftrans.write("one/")
226
+ ftrans.write("two/")
227
+ ftrans.write("three/")
228
+ ftrans.write("this is a frame")
229
+ ftrans.flush
230
+ end
231
+
232
+ it "should not flush the same buffered data twice" do
233
+ ftrans = Thrift::FramedTransport.new(@trans)
234
+ @trans.should_receive(:write).with("\000\000\000\007foo/bar")
235
+ @trans.stub!(:flush)
236
+ ftrans.write("foo")
237
+ ftrans.write("/bar")
238
+ ftrans.flush
239
+ @trans.should_receive(:write).with("\000\000\000\000")
240
+ ftrans.flush
241
+ end
242
+ end
243
+
244
+ describe Thrift::FramedTransportFactory do
245
+ it "should wrap the given transport in a FramedTransport" do
246
+ trans = mock("Transport")
247
+ Thrift::FramedTransport.should_receive(:new).with(trans)
248
+ Thrift::FramedTransportFactory.new.get_transport(trans)
249
+ end
250
+ end
251
+
252
+ describe Thrift::MemoryBufferTransport do
253
+ before(:each) do
254
+ @buffer = Thrift::MemoryBufferTransport.new
255
+ end
256
+
257
+ it "should accept a buffer on input and use it directly" do
258
+ s = "this is a test"
259
+ @buffer = Thrift::MemoryBufferTransport.new(s)
260
+ @buffer.read(4).should == "this"
261
+ s.slice!(-4..-1)
262
+ @buffer.read(@buffer.available).should == " is a "
263
+ end
264
+
265
+ it "should always remain open" do
266
+ @buffer.should be_open
267
+ @buffer.close
268
+ @buffer.should be_open
269
+ end
270
+
271
+ it "should respond to peek and available" do
272
+ @buffer.write "some data"
273
+ @buffer.peek.should be_true
274
+ @buffer.available.should == 9
275
+ @buffer.read(4)
276
+ @buffer.peek.should be_true
277
+ @buffer.available.should == 5
278
+ @buffer.read(5)
279
+ @buffer.peek.should be_false
280
+ @buffer.available.should == 0
281
+ end
282
+
283
+ it "should be able to reset the buffer" do
284
+ @buffer.write "test data"
285
+ @buffer.reset_buffer("foobar")
286
+ @buffer.available.should == 6
287
+ @buffer.read(@buffer.available).should == "foobar"
288
+ @buffer.reset_buffer
289
+ @buffer.available.should == 0
290
+ end
291
+
292
+ it "should copy the given string when resetting the buffer" do
293
+ s = "this is a test"
294
+ @buffer.reset_buffer(s)
295
+ @buffer.available.should == 14
296
+ @buffer.read(10)
297
+ @buffer.available.should == 4
298
+ s.should == "this is a test"
299
+ end
300
+
301
+ it "should return from read what was given in write" do
302
+ @buffer.write "test data"
303
+ @buffer.read(4).should == "test"
304
+ @buffer.read(@buffer.available).should == " data"
305
+ @buffer.write "foo"
306
+ @buffer.write " bar"
307
+ @buffer.read(@buffer.available).should == "foo bar"
308
+ end
309
+
310
+ it "should throw an EOFError when there isn't enough data in the buffer" do
311
+ @buffer.reset_buffer("")
312
+ lambda{@buffer.read(1)}.should raise_error(EOFError)
313
+
314
+ @buffer.reset_buffer("1234")
315
+ lambda{@buffer.read(5)}.should raise_error(EOFError)
316
+ end
317
+ end
318
+
319
+ describe Thrift::IOStreamTransport do
320
+ before(:each) do
321
+ @input = mock("Input", :closed? => false)
322
+ @output = mock("Output", :closed? => false)
323
+ @trans = Thrift::IOStreamTransport.new(@input, @output)
324
+ end
325
+
326
+ it "should be open as long as both input or output are open" do
327
+ @trans.should be_open
328
+ @input.stub!(:closed?).and_return(true)
329
+ @trans.should be_open
330
+ @input.stub!(:closed?).and_return(false)
331
+ @output.stub!(:closed?).and_return(true)
332
+ @trans.should be_open
333
+ @input.stub!(:closed?).and_return(true)
334
+ @trans.should_not be_open
335
+ end
336
+
337
+ it "should pass through read/write to input/output" do
338
+ @input.should_receive(:read).with(17).and_return("+ read")
339
+ @output.should_receive(:write).with("foobar").and_return("+ write")
340
+ @trans.read(17).should == "+ read"
341
+ @trans.write("foobar").should == "+ write"
342
+ end
343
+
344
+ it "should close both input and output when closed" do
345
+ @input.should_receive(:close)
346
+ @output.should_receive(:close)
347
+ @trans.close
348
+ end
349
+ end
350
+ end