blob: 709a93eb610a6cc4d5d12ad6833fcd3d159e0e44 [file] [log] [blame]
David Reissea2cba82009-03-30 21:35:00 +00001#
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
Kevin Clark531e0202008-06-18 01:10:17 +000020require File.dirname(__FILE__) + '/spec_helper'
21
22class ThriftTransportSpec < Spec::ExampleGroup
23 include Thrift
24
25 describe TransportException do
26 it "should make type accessible" do
27 exc = TransportException.new(TransportException::ALREADY_OPEN, "msg")
28 exc.type.should == TransportException::ALREADY_OPEN
29 exc.message.should == "msg"
30 end
31 end
32
33 describe Transport do
34 it "should read the specified size" do
35 transport = Transport.new
36 transport.should_receive(:read).with(40).ordered.and_return("10 letters")
37 transport.should_receive(:read).with(30).ordered.and_return("fifteen letters")
38 transport.should_receive(:read).with(15).ordered.and_return("more characters")
39 transport.read_all(40).should == "10 lettersfifteen lettersmore characters"
40 end
41
42 it "should stub out the rest of the methods" do
43 # can't test for stubbiness, so just make sure they're defined
44 [:open?, :open, :close, :read, :write, :flush].each do |sym|
45 Transport.method_defined?(sym).should be_true
46 end
47 end
Kevin Clark6c30dbb2008-06-18 01:15:25 +000048
49 it "should alias << to write" do
50 Transport.instance_method(:<<).should == Transport.instance_method(:write)
51 end
Kevin Clark531e0202008-06-18 01:10:17 +000052 end
53
54 describe ServerTransport do
55 it "should stub out its methods" do
56 [:listen, :accept, :close].each do |sym|
57 ServerTransport.method_defined?(sym).should be_true
58 end
59 end
60 end
61
62 describe TransportFactory do
63 it "should return the transport it's given" do
64 transport = mock("Transport")
65 TransportFactory.new.get_transport(transport).should eql(transport)
66 end
67 end
68
69 describe BufferedTransport do
Kevin Clark4bd89162008-07-08 00:47:49 +000070 it "should pass through everything but write/flush/read" do
Kevin Clark531e0202008-06-18 01:10:17 +000071 trans = mock("Transport")
72 trans.should_receive(:open?).ordered.and_return("+ open?")
73 trans.should_receive(:open).ordered.and_return("+ open")
Kevin Clark091fa952008-06-26 18:10:56 +000074 trans.should_receive(:flush).ordered # from the close
Kevin Clark531e0202008-06-18 01:10:17 +000075 trans.should_receive(:close).ordered.and_return("+ close")
Kevin Clark531e0202008-06-18 01:10:17 +000076 btrans = BufferedTransport.new(trans)
77 btrans.open?.should == "+ open?"
78 btrans.open.should == "+ open"
79 btrans.close.should == "+ close"
Kevin Clark4bd89162008-07-08 00:47:49 +000080 end
81
82 it "should buffer reads in chunks of #{BufferedTransport::DEFAULT_BUFFER}" do
83 trans = mock("Transport")
84 trans.should_receive(:read).with(BufferedTransport::DEFAULT_BUFFER).and_return("lorum ipsum dolor emet")
85 btrans = BufferedTransport.new(trans)
86 btrans.read(6).should == "lorum "
87 btrans.read(6).should == "ipsum "
88 btrans.read(6).should == "dolor "
89 btrans.read(6).should == "emet"
Kevin Clark531e0202008-06-18 01:10:17 +000090 end
91
92 it "should buffer writes and send them on flush" do
93 trans = mock("Transport")
94 btrans = BufferedTransport.new(trans)
95 btrans.write("one/")
96 btrans.write("two/")
97 btrans.write("three/")
98 trans.should_receive(:write).with("one/two/three/").ordered
99 trans.should_receive(:flush).ordered
100 btrans.flush
101 end
102
103 it "should only send buffered data once" do
104 trans = mock("Transport")
105 btrans = BufferedTransport.new(trans)
106 btrans.write("one/")
107 btrans.write("two/")
108 btrans.write("three/")
109 trans.should_receive(:write).with("one/two/three/")
110 trans.stub!(:flush)
111 btrans.flush
Kevin Clark091fa952008-06-26 18:10:56 +0000112 # Nothing to flush with no data
113 btrans.flush
114 end
115
116 it "should flush on close" do
117 trans = mock("Transport")
118 trans.should_receive(:close)
119 btrans = BufferedTransport.new(trans)
120 btrans.should_receive(:flush)
121 btrans.close
122 end
123
124 it "should not write to socket if there's no data" do
125 trans = mock("Transport")
126 trans.should_receive(:flush)
127 btrans = BufferedTransport.new(trans)
Kevin Clark531e0202008-06-18 01:10:17 +0000128 btrans.flush
129 end
130 end
131
132 describe BufferedTransportFactory do
133 it "should wrap the given transport in a BufferedTransport" do
134 trans = mock("Transport")
135 btrans = mock("BufferedTransport")
136 BufferedTransport.should_receive(:new).with(trans).and_return(btrans)
137 BufferedTransportFactory.new.get_transport(trans).should == btrans
138 end
139 end
140
141 describe FramedTransport do
142 before(:each) do
143 @trans = mock("Transport")
144 end
145
146 it "should pass through open?/open/close" do
147 ftrans = FramedTransport.new(@trans)
148 @trans.should_receive(:open?).ordered.and_return("+ open?")
149 @trans.should_receive(:open).ordered.and_return("+ open")
150 @trans.should_receive(:close).ordered.and_return("+ close")
151 ftrans.open?.should == "+ open?"
152 ftrans.open.should == "+ open"
153 ftrans.close.should == "+ close"
154 end
155
156 it "should pass through read when read is turned off" do
157 ftrans = FramedTransport.new(@trans, false, true)
158 @trans.should_receive(:read).with(17).ordered.and_return("+ read")
159 ftrans.read(17).should == "+ read"
160 end
161
162 it "should pass through write/flush when write is turned off" do
163 ftrans = FramedTransport.new(@trans, true, false)
164 @trans.should_receive(:write).with("foo").ordered.and_return("+ write")
165 @trans.should_receive(:flush).ordered.and_return("+ flush")
166 ftrans.write("foo").should == "+ write"
167 ftrans.flush.should == "+ flush"
168 end
169
170 it "should return a full frame if asked for >= the frame's length" do
171 frame = "this is a frame"
Kevin Clarkdfe22b32008-06-18 01:13:09 +0000172 @trans.should_receive(:read_all).with(4).and_return("\000\000\000\017")
Kevin Clark531e0202008-06-18 01:10:17 +0000173 @trans.should_receive(:read_all).with(frame.length).and_return(frame)
174 FramedTransport.new(@trans).read(frame.length + 10).should == frame
175 end
176
177 it "should return slices of the frame when asked for < the frame's length" do
178 frame = "this is a frame"
Kevin Clarkdfe22b32008-06-18 01:13:09 +0000179 @trans.should_receive(:read_all).with(4).and_return("\000\000\000\017")
Kevin Clark531e0202008-06-18 01:10:17 +0000180 @trans.should_receive(:read_all).with(frame.length).and_return(frame)
181 ftrans = FramedTransport.new(@trans)
182 ftrans.read(4).should == "this"
183 ftrans.read(4).should == " is "
184 ftrans.read(16).should == "a frame"
185 end
186
Kevin Clarkfa4a9582008-06-18 01:13:18 +0000187 it "should return nothing if asked for <= 0" do
188 FramedTransport.new(@trans).read(-2).should == ""
189 end
190
Kevin Clark531e0202008-06-18 01:10:17 +0000191 it "should pull a new frame when the first is exhausted" do
192 frame = "this is a frame"
193 frame2 = "yet another frame"
Kevin Clarkdfe22b32008-06-18 01:13:09 +0000194 @trans.should_receive(:read_all).with(4).and_return("\000\000\000\017", "\000\000\000\021")
Kevin Clark531e0202008-06-18 01:10:17 +0000195 @trans.should_receive(:read_all).with(frame.length).and_return(frame)
196 @trans.should_receive(:read_all).with(frame2.length).and_return(frame2)
197 ftrans = FramedTransport.new(@trans)
198 ftrans.read(4).should == "this"
199 ftrans.read(8).should == " is a fr"
200 ftrans.read(6).should == "ame"
201 ftrans.read(4).should == "yet "
202 ftrans.read(16).should == "another frame"
203 end
204
205 it "should buffer writes" do
206 ftrans = FramedTransport.new(@trans)
207 @trans.should_not_receive(:write)
208 ftrans.write("foo")
209 ftrans.write("bar")
210 ftrans.write("this is a frame")
211 end
212
Kevin Clarkfa4a9582008-06-18 01:13:18 +0000213 it "should write slices of the buffer" do
214 ftrans = FramedTransport.new(@trans)
215 ftrans.write("foobar", 3)
216 ftrans.write("barfoo", 1)
217 @trans.stub!(:flush)
218 @trans.should_receive(:write).with("\000\000\000\004foob")
219 ftrans.flush
220 end
221
Kevin Clark531e0202008-06-18 01:10:17 +0000222 it "should flush frames with a 4-byte header" do
223 ftrans = FramedTransport.new(@trans)
Kevin Clarkdfe22b32008-06-18 01:13:09 +0000224 @trans.should_receive(:write).with("\000\000\000\035one/two/three/this is a frame").ordered
Kevin Clark531e0202008-06-18 01:10:17 +0000225 @trans.should_receive(:flush).ordered
226 ftrans.write("one/")
227 ftrans.write("two/")
228 ftrans.write("three/")
229 ftrans.write("this is a frame")
230 ftrans.flush
231 end
232
233 it "should not flush the same buffered data twice" do
234 ftrans = FramedTransport.new(@trans)
Kevin Clarkdfe22b32008-06-18 01:13:09 +0000235 @trans.should_receive(:write).with("\000\000\000\007foo/bar")
Kevin Clark531e0202008-06-18 01:10:17 +0000236 @trans.stub!(:flush)
237 ftrans.write("foo")
238 ftrans.write("/bar")
239 ftrans.flush
240 @trans.should_receive(:write).with("\000\000\000\000")
241 ftrans.flush
242 end
243 end
244
245 describe FramedTransportFactory do
246 it "should wrap the given transport in a FramedTransport" do
247 trans = mock("Transport")
248 FramedTransport.should_receive(:new).with(trans)
249 FramedTransportFactory.new.get_transport(trans)
250 end
251 end
Kevin Clarkf6aa86a2008-06-18 01:11:07 +0000252
253 describe MemoryBuffer do
254 before(:each) do
255 @buffer = MemoryBuffer.new
256 end
257
Kevin Clark6c30dbb2008-06-18 01:15:25 +0000258 it "should accept a buffer on input and use it directly" do
259 s = "this is a test"
260 @buffer = MemoryBuffer.new(s)
261 @buffer.read(4).should == "this"
Kevin Clarkbf2ff242008-07-08 23:20:15 +0000262 s.slice!(-4..-1)
263 @buffer.read(@buffer.available).should == " is a "
Kevin Clark6c30dbb2008-06-18 01:15:25 +0000264 end
265
Kevin Clarkf6aa86a2008-06-18 01:11:07 +0000266 it "should always remain open" do
267 @buffer.should be_open
268 @buffer.close
269 @buffer.should be_open
270 end
271
272 it "should respond to peek and available" do
273 @buffer.write "some data"
274 @buffer.peek.should be_true
275 @buffer.available.should == 9
276 @buffer.read(4)
277 @buffer.peek.should be_true
278 @buffer.available.should == 5
279 @buffer.read(16)
280 @buffer.peek.should be_false
281 @buffer.available.should == 0
282 end
283
284 it "should be able to reset the buffer" do
285 @buffer.write "test data"
286 @buffer.reset_buffer("foobar")
287 @buffer.available.should == 6
288 @buffer.read(10).should == "foobar"
289 @buffer.reset_buffer
290 @buffer.available.should == 0
291 end
292
Kevin Clark6c30dbb2008-06-18 01:15:25 +0000293 it "should copy the given string whne resetting the buffer" do
294 s = "this is a test"
295 @buffer.reset_buffer(s)
296 @buffer.available.should == 14
297 @buffer.read(10)
298 @buffer.available.should == 4
299 s.should == "this is a test"
300 end
301
Kevin Clarkf6aa86a2008-06-18 01:11:07 +0000302 it "should return from read what was given in write" do
303 @buffer.write "test data"
304 @buffer.read(4).should == "test"
305 @buffer.read(10).should == " data"
306 @buffer.read(10).should == ""
307 @buffer.write "foo"
308 @buffer.write " bar"
309 @buffer.read(10).should == "foo bar"
310 end
311 end
312
313 describe IOStreamTransport do
314 before(:each) do
Kevin Clarkf4e70082008-07-18 22:27:03 +0000315 @input = mock("Input", :closed? => false)
316 @output = mock("Output", :closed? => false)
Kevin Clarkf6aa86a2008-06-18 01:11:07 +0000317 @trans = IOStreamTransport.new(@input, @output)
318 end
319
Kevin Clarkf4e70082008-07-18 22:27:03 +0000320 it "should be open as long as both input or output are open" do
Kevin Clarkf6aa86a2008-06-18 01:11:07 +0000321 @trans.should be_open
Kevin Clarkf4e70082008-07-18 22:27:03 +0000322 @input.stub!(:closed?).and_return(true)
Kevin Clarkf6aa86a2008-06-18 01:11:07 +0000323 @trans.should be_open
Kevin Clarkf4e70082008-07-18 22:27:03 +0000324 @input.stub!(:closed?).and_return(false)
325 @output.stub!(:closed?).and_return(true)
326 @trans.should be_open
327 @input.stub!(:closed?).and_return(true)
328 @trans.should_not be_open
Kevin Clarkf6aa86a2008-06-18 01:11:07 +0000329 end
330
331 it "should pass through read/write to input/output" do
332 @input.should_receive(:read).with(17).and_return("+ read")
333 @output.should_receive(:write).with("foobar").and_return("+ write")
334 @trans.read(17).should == "+ read"
335 @trans.write("foobar").should == "+ write"
336 end
Kevin Clarkf4e70082008-07-18 22:27:03 +0000337
338 it "should close both input and output when closed" do
339 @input.should_receive(:close)
340 @output.should_receive(:close)
341 @trans.close
342 end
Kevin Clarkf6aa86a2008-06-18 01:11:07 +0000343 end
Kevin Clark531e0202008-06-18 01:10:17 +0000344end