blob: ef1918fb31079b92ef4b13991eae5330be083393 [file] [log] [blame]
Kevin Clark531e0202008-06-18 01:10:17 +00001require File.dirname(__FILE__) + '/spec_helper'
2
3class ThriftTransportSpec < Spec::ExampleGroup
4 include Thrift
5
6 describe TransportException do
7 it "should make type accessible" do
8 exc = TransportException.new(TransportException::ALREADY_OPEN, "msg")
9 exc.type.should == TransportException::ALREADY_OPEN
10 exc.message.should == "msg"
11 end
12 end
13
14 describe Transport do
15 it "should read the specified size" do
16 transport = Transport.new
17 transport.should_receive(:read).with(40).ordered.and_return("10 letters")
18 transport.should_receive(:read).with(30).ordered.and_return("fifteen letters")
19 transport.should_receive(:read).with(15).ordered.and_return("more characters")
20 transport.read_all(40).should == "10 lettersfifteen lettersmore characters"
21 end
22
23 it "should stub out the rest of the methods" do
24 # can't test for stubbiness, so just make sure they're defined
25 [:open?, :open, :close, :read, :write, :flush].each do |sym|
26 Transport.method_defined?(sym).should be_true
27 end
28 end
Kevin Clark6c30dbb2008-06-18 01:15:25 +000029
30 it "should alias << to write" do
31 Transport.instance_method(:<<).should == Transport.instance_method(:write)
32 end
Kevin Clark531e0202008-06-18 01:10:17 +000033 end
34
35 describe ServerTransport do
36 it "should stub out its methods" do
37 [:listen, :accept, :close].each do |sym|
38 ServerTransport.method_defined?(sym).should be_true
39 end
40 end
41 end
42
43 describe TransportFactory do
44 it "should return the transport it's given" do
45 transport = mock("Transport")
46 TransportFactory.new.get_transport(transport).should eql(transport)
47 end
48 end
49
50 describe BufferedTransport do
Kevin Clark4bd89162008-07-08 00:47:49 +000051 it "should pass through everything but write/flush/read" do
Kevin Clark531e0202008-06-18 01:10:17 +000052 trans = mock("Transport")
53 trans.should_receive(:open?).ordered.and_return("+ open?")
54 trans.should_receive(:open).ordered.and_return("+ open")
Kevin Clark091fa952008-06-26 18:10:56 +000055 trans.should_receive(:flush).ordered # from the close
Kevin Clark531e0202008-06-18 01:10:17 +000056 trans.should_receive(:close).ordered.and_return("+ close")
Kevin Clark531e0202008-06-18 01:10:17 +000057 btrans = BufferedTransport.new(trans)
58 btrans.open?.should == "+ open?"
59 btrans.open.should == "+ open"
60 btrans.close.should == "+ close"
Kevin Clark4bd89162008-07-08 00:47:49 +000061 end
62
63 it "should buffer reads in chunks of #{BufferedTransport::DEFAULT_BUFFER}" do
64 trans = mock("Transport")
65 trans.should_receive(:read).with(BufferedTransport::DEFAULT_BUFFER).and_return("lorum ipsum dolor emet")
66 btrans = BufferedTransport.new(trans)
67 btrans.read(6).should == "lorum "
68 btrans.read(6).should == "ipsum "
69 btrans.read(6).should == "dolor "
70 btrans.read(6).should == "emet"
Kevin Clark531e0202008-06-18 01:10:17 +000071 end
72
73 it "should buffer writes and send them on flush" do
74 trans = mock("Transport")
75 btrans = BufferedTransport.new(trans)
76 btrans.write("one/")
77 btrans.write("two/")
78 btrans.write("three/")
79 trans.should_receive(:write).with("one/two/three/").ordered
80 trans.should_receive(:flush).ordered
81 btrans.flush
82 end
83
84 it "should only send buffered data once" do
85 trans = mock("Transport")
86 btrans = BufferedTransport.new(trans)
87 btrans.write("one/")
88 btrans.write("two/")
89 btrans.write("three/")
90 trans.should_receive(:write).with("one/two/three/")
91 trans.stub!(:flush)
92 btrans.flush
Kevin Clark091fa952008-06-26 18:10:56 +000093 # Nothing to flush with no data
94 btrans.flush
95 end
96
97 it "should flush on close" do
98 trans = mock("Transport")
99 trans.should_receive(:close)
100 btrans = BufferedTransport.new(trans)
101 btrans.should_receive(:flush)
102 btrans.close
103 end
104
105 it "should not write to socket if there's no data" do
106 trans = mock("Transport")
107 trans.should_receive(:flush)
108 btrans = BufferedTransport.new(trans)
Kevin Clark531e0202008-06-18 01:10:17 +0000109 btrans.flush
110 end
111 end
112
113 describe BufferedTransportFactory do
114 it "should wrap the given transport in a BufferedTransport" do
115 trans = mock("Transport")
116 btrans = mock("BufferedTransport")
117 BufferedTransport.should_receive(:new).with(trans).and_return(btrans)
118 BufferedTransportFactory.new.get_transport(trans).should == btrans
119 end
120 end
121
122 describe FramedTransport do
123 before(:each) do
124 @trans = mock("Transport")
125 end
126
127 it "should pass through open?/open/close" do
128 ftrans = FramedTransport.new(@trans)
129 @trans.should_receive(:open?).ordered.and_return("+ open?")
130 @trans.should_receive(:open).ordered.and_return("+ open")
131 @trans.should_receive(:close).ordered.and_return("+ close")
132 ftrans.open?.should == "+ open?"
133 ftrans.open.should == "+ open"
134 ftrans.close.should == "+ close"
135 end
136
137 it "should pass through read when read is turned off" do
138 ftrans = FramedTransport.new(@trans, false, true)
139 @trans.should_receive(:read).with(17).ordered.and_return("+ read")
140 ftrans.read(17).should == "+ read"
141 end
142
143 it "should pass through write/flush when write is turned off" do
144 ftrans = FramedTransport.new(@trans, true, false)
145 @trans.should_receive(:write).with("foo").ordered.and_return("+ write")
146 @trans.should_receive(:flush).ordered.and_return("+ flush")
147 ftrans.write("foo").should == "+ write"
148 ftrans.flush.should == "+ flush"
149 end
150
151 it "should return a full frame if asked for >= the frame's length" do
152 frame = "this is a frame"
Kevin Clarkdfe22b32008-06-18 01:13:09 +0000153 @trans.should_receive(:read_all).with(4).and_return("\000\000\000\017")
Kevin Clark531e0202008-06-18 01:10:17 +0000154 @trans.should_receive(:read_all).with(frame.length).and_return(frame)
155 FramedTransport.new(@trans).read(frame.length + 10).should == frame
156 end
157
158 it "should return slices of the frame when asked for < the frame's length" do
159 frame = "this is a frame"
Kevin Clarkdfe22b32008-06-18 01:13:09 +0000160 @trans.should_receive(:read_all).with(4).and_return("\000\000\000\017")
Kevin Clark531e0202008-06-18 01:10:17 +0000161 @trans.should_receive(:read_all).with(frame.length).and_return(frame)
162 ftrans = FramedTransport.new(@trans)
163 ftrans.read(4).should == "this"
164 ftrans.read(4).should == " is "
165 ftrans.read(16).should == "a frame"
166 end
167
Kevin Clarkfa4a9582008-06-18 01:13:18 +0000168 it "should return nothing if asked for <= 0" do
169 FramedTransport.new(@trans).read(-2).should == ""
170 end
171
Kevin Clark531e0202008-06-18 01:10:17 +0000172 it "should pull a new frame when the first is exhausted" do
173 frame = "this is a frame"
174 frame2 = "yet another frame"
Kevin Clarkdfe22b32008-06-18 01:13:09 +0000175 @trans.should_receive(:read_all).with(4).and_return("\000\000\000\017", "\000\000\000\021")
Kevin Clark531e0202008-06-18 01:10:17 +0000176 @trans.should_receive(:read_all).with(frame.length).and_return(frame)
177 @trans.should_receive(:read_all).with(frame2.length).and_return(frame2)
178 ftrans = FramedTransport.new(@trans)
179 ftrans.read(4).should == "this"
180 ftrans.read(8).should == " is a fr"
181 ftrans.read(6).should == "ame"
182 ftrans.read(4).should == "yet "
183 ftrans.read(16).should == "another frame"
184 end
185
186 it "should buffer writes" do
187 ftrans = FramedTransport.new(@trans)
188 @trans.should_not_receive(:write)
189 ftrans.write("foo")
190 ftrans.write("bar")
191 ftrans.write("this is a frame")
192 end
193
Kevin Clarkfa4a9582008-06-18 01:13:18 +0000194 it "should write slices of the buffer" do
195 ftrans = FramedTransport.new(@trans)
196 ftrans.write("foobar", 3)
197 ftrans.write("barfoo", 1)
198 @trans.stub!(:flush)
199 @trans.should_receive(:write).with("\000\000\000\004foob")
200 ftrans.flush
201 end
202
Kevin Clark531e0202008-06-18 01:10:17 +0000203 it "should flush frames with a 4-byte header" do
204 ftrans = FramedTransport.new(@trans)
Kevin Clarkdfe22b32008-06-18 01:13:09 +0000205 @trans.should_receive(:write).with("\000\000\000\035one/two/three/this is a frame").ordered
Kevin Clark531e0202008-06-18 01:10:17 +0000206 @trans.should_receive(:flush).ordered
207 ftrans.write("one/")
208 ftrans.write("two/")
209 ftrans.write("three/")
210 ftrans.write("this is a frame")
211 ftrans.flush
212 end
213
214 it "should not flush the same buffered data twice" do
215 ftrans = FramedTransport.new(@trans)
Kevin Clarkdfe22b32008-06-18 01:13:09 +0000216 @trans.should_receive(:write).with("\000\000\000\007foo/bar")
Kevin Clark531e0202008-06-18 01:10:17 +0000217 @trans.stub!(:flush)
218 ftrans.write("foo")
219 ftrans.write("/bar")
220 ftrans.flush
221 @trans.should_receive(:write).with("\000\000\000\000")
222 ftrans.flush
223 end
Kevin Clark969c04b2008-08-25 22:16:45 +0000224
225 it "should refill its buffer when borrow is called and it is empty" do
226 ftrans = FramedTransport.new(@trans)
227 @trans.should_receive(:read_all).with(4).and_return([10].pack("N"))
228 @trans.should_receive(:read_all).with(10).and_return("1234567890")
229 ftrans.borrow(10).should == "1234567890"
230 end
231
232 it "should not consume any data when borrow is called" do
233 ftrans = FramedTransport.new(@trans)
234 @trans.should_receive(:read_all).with(4).and_return([10].pack("N"))
235 @trans.should_receive(:read_all).with(10).and_return("1234567890")
236 ftrans.borrow(10).should == "1234567890"
237 ftrans.borrow(10).should == "1234567890"
238 end
239
240 it "should remove data from the buffer when consume! is called" do
241 ftrans = FramedTransport.new(@trans)
242 @trans.should_receive(:read_all).with(4).ordered.and_return([10].pack("N"))
243 @trans.should_receive(:read_all).with(10).ordered.and_return("1234567890")
244 ftrans.borrow(5).should == "1234567890"
245 ftrans.consume!(5).should == "12345"
246 ftrans.borrow(5).should == "67890"
247 end
248
249 it "should raise an EOFError when it is out of data and borrow is called" do
250 ftrans = FramedTransport.new(@trans)
251 @trans.should_receive(:read_all).with(4).ordered.and_return([10].pack("N"), [0].pack("N"))
252 @trans.should_receive(:read_all).with(10).ordered.and_return("1234567890")
253 @trans.should_receive(:read_all).with(0).ordered.and_return("")
254 ftrans.borrow(10).should == "1234567890"
255 ftrans.consume!(10).should == "1234567890"
256 lambda {ftrans.borrow(10)}.should raise_error(EOFError)
257 end
Kevin Clark531e0202008-06-18 01:10:17 +0000258 end
259
260 describe FramedTransportFactory do
261 it "should wrap the given transport in a FramedTransport" do
262 trans = mock("Transport")
263 FramedTransport.should_receive(:new).with(trans)
264 FramedTransportFactory.new.get_transport(trans)
265 end
266 end
Kevin Clarkf6aa86a2008-06-18 01:11:07 +0000267
268 describe MemoryBuffer do
269 before(:each) do
270 @buffer = MemoryBuffer.new
271 end
272
Kevin Clark6c30dbb2008-06-18 01:15:25 +0000273 it "should accept a buffer on input and use it directly" do
274 s = "this is a test"
275 @buffer = MemoryBuffer.new(s)
276 @buffer.read(4).should == "this"
Kevin Clarkbf2ff242008-07-08 23:20:15 +0000277 s.slice!(-4..-1)
278 @buffer.read(@buffer.available).should == " is a "
Kevin Clark6c30dbb2008-06-18 01:15:25 +0000279 end
280
Kevin Clarkf6aa86a2008-06-18 01:11:07 +0000281 it "should always remain open" do
282 @buffer.should be_open
283 @buffer.close
284 @buffer.should be_open
285 end
286
287 it "should respond to peek and available" do
288 @buffer.write "some data"
289 @buffer.peek.should be_true
290 @buffer.available.should == 9
291 @buffer.read(4)
292 @buffer.peek.should be_true
293 @buffer.available.should == 5
294 @buffer.read(16)
295 @buffer.peek.should be_false
296 @buffer.available.should == 0
297 end
298
299 it "should be able to reset the buffer" do
300 @buffer.write "test data"
301 @buffer.reset_buffer("foobar")
302 @buffer.available.should == 6
303 @buffer.read(10).should == "foobar"
304 @buffer.reset_buffer
305 @buffer.available.should == 0
306 end
307
Kevin Clark6c30dbb2008-06-18 01:15:25 +0000308 it "should copy the given string whne resetting the buffer" do
309 s = "this is a test"
310 @buffer.reset_buffer(s)
311 @buffer.available.should == 14
312 @buffer.read(10)
313 @buffer.available.should == 4
314 s.should == "this is a test"
315 end
316
Kevin Clarkf6aa86a2008-06-18 01:11:07 +0000317 it "should return from read what was given in write" do
318 @buffer.write "test data"
319 @buffer.read(4).should == "test"
320 @buffer.read(10).should == " data"
321 @buffer.read(10).should == ""
322 @buffer.write "foo"
323 @buffer.write " bar"
324 @buffer.read(10).should == "foo bar"
325 end
326 end
327
328 describe IOStreamTransport do
329 before(:each) do
Kevin Clarkf4e70082008-07-18 22:27:03 +0000330 @input = mock("Input", :closed? => false)
331 @output = mock("Output", :closed? => false)
Kevin Clarkf6aa86a2008-06-18 01:11:07 +0000332 @trans = IOStreamTransport.new(@input, @output)
333 end
334
Kevin Clarkf4e70082008-07-18 22:27:03 +0000335 it "should be open as long as both input or output are open" do
Kevin Clarkf6aa86a2008-06-18 01:11:07 +0000336 @trans.should be_open
Kevin Clarkf4e70082008-07-18 22:27:03 +0000337 @input.stub!(:closed?).and_return(true)
Kevin Clarkf6aa86a2008-06-18 01:11:07 +0000338 @trans.should be_open
Kevin Clarkf4e70082008-07-18 22:27:03 +0000339 @input.stub!(:closed?).and_return(false)
340 @output.stub!(:closed?).and_return(true)
341 @trans.should be_open
342 @input.stub!(:closed?).and_return(true)
343 @trans.should_not be_open
Kevin Clarkf6aa86a2008-06-18 01:11:07 +0000344 end
345
346 it "should pass through read/write to input/output" do
347 @input.should_receive(:read).with(17).and_return("+ read")
348 @output.should_receive(:write).with("foobar").and_return("+ write")
349 @trans.read(17).should == "+ read"
350 @trans.write("foobar").should == "+ write"
351 end
Kevin Clarkf4e70082008-07-18 22:27:03 +0000352
353 it "should close both input and output when closed" do
354 @input.should_receive(:close)
355 @output.should_receive(:close)
356 @trans.close
357 end
Kevin Clarkf6aa86a2008-06-18 01:11:07 +0000358 end
Kevin Clark531e0202008-06-18 01:10:17 +0000359end