Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 1 | require File.dirname(__FILE__) + '/spec_helper' |
| 2 | |
| 3 | class 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 Clark | 6c30dbb | 2008-06-18 01:15:25 +0000 | [diff] [blame] | 29 | |
| 30 | it "should alias << to write" do |
| 31 | Transport.instance_method(:<<).should == Transport.instance_method(:write) |
| 32 | end |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 33 | 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 Clark | 4bd8916 | 2008-07-08 00:47:49 +0000 | [diff] [blame] | 51 | it "should pass through everything but write/flush/read" do |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 52 | trans = mock("Transport") |
| 53 | trans.should_receive(:open?).ordered.and_return("+ open?") |
| 54 | trans.should_receive(:open).ordered.and_return("+ open") |
Kevin Clark | 091fa95 | 2008-06-26 18:10:56 +0000 | [diff] [blame] | 55 | trans.should_receive(:flush).ordered # from the close |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 56 | trans.should_receive(:close).ordered.and_return("+ close") |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 57 | btrans = BufferedTransport.new(trans) |
| 58 | btrans.open?.should == "+ open?" |
| 59 | btrans.open.should == "+ open" |
| 60 | btrans.close.should == "+ close" |
Kevin Clark | 4bd8916 | 2008-07-08 00:47:49 +0000 | [diff] [blame] | 61 | 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 Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 71 | 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 Clark | 091fa95 | 2008-06-26 18:10:56 +0000 | [diff] [blame] | 93 | # 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 Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 109 | 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 Clark | dfe22b3 | 2008-06-18 01:13:09 +0000 | [diff] [blame] | 153 | @trans.should_receive(:read_all).with(4).and_return("\000\000\000\017") |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 154 | @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 Clark | dfe22b3 | 2008-06-18 01:13:09 +0000 | [diff] [blame] | 160 | @trans.should_receive(:read_all).with(4).and_return("\000\000\000\017") |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 161 | @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 Clark | fa4a958 | 2008-06-18 01:13:18 +0000 | [diff] [blame] | 168 | it "should return nothing if asked for <= 0" do |
| 169 | FramedTransport.new(@trans).read(-2).should == "" |
| 170 | end |
| 171 | |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 172 | it "should pull a new frame when the first is exhausted" do |
| 173 | frame = "this is a frame" |
| 174 | frame2 = "yet another frame" |
Kevin Clark | dfe22b3 | 2008-06-18 01:13:09 +0000 | [diff] [blame] | 175 | @trans.should_receive(:read_all).with(4).and_return("\000\000\000\017", "\000\000\000\021") |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 176 | @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 Clark | fa4a958 | 2008-06-18 01:13:18 +0000 | [diff] [blame] | 194 | 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 Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 203 | it "should flush frames with a 4-byte header" do |
| 204 | ftrans = FramedTransport.new(@trans) |
Kevin Clark | dfe22b3 | 2008-06-18 01:13:09 +0000 | [diff] [blame] | 205 | @trans.should_receive(:write).with("\000\000\000\035one/two/three/this is a frame").ordered |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 206 | @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 Clark | dfe22b3 | 2008-06-18 01:13:09 +0000 | [diff] [blame] | 216 | @trans.should_receive(:write).with("\000\000\000\007foo/bar") |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 217 | @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 Clark | 969c04b | 2008-08-25 22:16:45 +0000 | [diff] [blame] | 224 | |
| 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 Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 258 | 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 Clark | f6aa86a | 2008-06-18 01:11:07 +0000 | [diff] [blame] | 267 | |
| 268 | describe MemoryBuffer do |
| 269 | before(:each) do |
| 270 | @buffer = MemoryBuffer.new |
| 271 | end |
| 272 | |
Kevin Clark | 6c30dbb | 2008-06-18 01:15:25 +0000 | [diff] [blame] | 273 | 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 Clark | bf2ff24 | 2008-07-08 23:20:15 +0000 | [diff] [blame] | 277 | s.slice!(-4..-1) |
| 278 | @buffer.read(@buffer.available).should == " is a " |
Kevin Clark | 6c30dbb | 2008-06-18 01:15:25 +0000 | [diff] [blame] | 279 | end |
| 280 | |
Kevin Clark | f6aa86a | 2008-06-18 01:11:07 +0000 | [diff] [blame] | 281 | 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 Clark | 6c30dbb | 2008-06-18 01:15:25 +0000 | [diff] [blame] | 308 | 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 Clark | f6aa86a | 2008-06-18 01:11:07 +0000 | [diff] [blame] | 317 | 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 Clark | f4e7008 | 2008-07-18 22:27:03 +0000 | [diff] [blame] | 330 | @input = mock("Input", :closed? => false) |
| 331 | @output = mock("Output", :closed? => false) |
Kevin Clark | f6aa86a | 2008-06-18 01:11:07 +0000 | [diff] [blame] | 332 | @trans = IOStreamTransport.new(@input, @output) |
| 333 | end |
| 334 | |
Kevin Clark | f4e7008 | 2008-07-18 22:27:03 +0000 | [diff] [blame] | 335 | it "should be open as long as both input or output are open" do |
Kevin Clark | f6aa86a | 2008-06-18 01:11:07 +0000 | [diff] [blame] | 336 | @trans.should be_open |
Kevin Clark | f4e7008 | 2008-07-18 22:27:03 +0000 | [diff] [blame] | 337 | @input.stub!(:closed?).and_return(true) |
Kevin Clark | f6aa86a | 2008-06-18 01:11:07 +0000 | [diff] [blame] | 338 | @trans.should be_open |
Kevin Clark | f4e7008 | 2008-07-18 22:27:03 +0000 | [diff] [blame] | 339 | @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 Clark | f6aa86a | 2008-06-18 01:11:07 +0000 | [diff] [blame] | 344 | 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 Clark | f4e7008 | 2008-07-18 22:27:03 +0000 | [diff] [blame] | 352 | |
| 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 Clark | f6aa86a | 2008-06-18 01:11:07 +0000 | [diff] [blame] | 358 | end |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 359 | end |