David Reiss | ea2cba8 | 2009-03-30 21:35:00 +0000 | [diff] [blame^] | 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 | |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 20 | require File.dirname(__FILE__) + '/spec_helper' |
| 21 | |
| 22 | class 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 Clark | 6c30dbb | 2008-06-18 01:15:25 +0000 | [diff] [blame] | 48 | |
| 49 | it "should alias << to write" do |
| 50 | Transport.instance_method(:<<).should == Transport.instance_method(:write) |
| 51 | end |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 52 | 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 Clark | 4bd8916 | 2008-07-08 00:47:49 +0000 | [diff] [blame] | 70 | it "should pass through everything but write/flush/read" do |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 71 | trans = mock("Transport") |
| 72 | trans.should_receive(:open?).ordered.and_return("+ open?") |
| 73 | trans.should_receive(:open).ordered.and_return("+ open") |
Kevin Clark | 091fa95 | 2008-06-26 18:10:56 +0000 | [diff] [blame] | 74 | trans.should_receive(:flush).ordered # from the close |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 75 | trans.should_receive(:close).ordered.and_return("+ close") |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 76 | btrans = BufferedTransport.new(trans) |
| 77 | btrans.open?.should == "+ open?" |
| 78 | btrans.open.should == "+ open" |
| 79 | btrans.close.should == "+ close" |
Kevin Clark | 4bd8916 | 2008-07-08 00:47:49 +0000 | [diff] [blame] | 80 | 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 Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 90 | 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 Clark | 091fa95 | 2008-06-26 18:10:56 +0000 | [diff] [blame] | 112 | # 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 Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 128 | 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 Clark | dfe22b3 | 2008-06-18 01:13:09 +0000 | [diff] [blame] | 172 | @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] | 173 | @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 Clark | dfe22b3 | 2008-06-18 01:13:09 +0000 | [diff] [blame] | 179 | @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] | 180 | @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 Clark | fa4a958 | 2008-06-18 01:13:18 +0000 | [diff] [blame] | 187 | it "should return nothing if asked for <= 0" do |
| 188 | FramedTransport.new(@trans).read(-2).should == "" |
| 189 | end |
| 190 | |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 191 | it "should pull a new frame when the first is exhausted" do |
| 192 | frame = "this is a frame" |
| 193 | frame2 = "yet another frame" |
Kevin Clark | dfe22b3 | 2008-06-18 01:13:09 +0000 | [diff] [blame] | 194 | @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] | 195 | @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 Clark | fa4a958 | 2008-06-18 01:13:18 +0000 | [diff] [blame] | 213 | 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 Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 222 | it "should flush frames with a 4-byte header" do |
| 223 | ftrans = FramedTransport.new(@trans) |
Kevin Clark | dfe22b3 | 2008-06-18 01:13:09 +0000 | [diff] [blame] | 224 | @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] | 225 | @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 Clark | dfe22b3 | 2008-06-18 01:13:09 +0000 | [diff] [blame] | 235 | @trans.should_receive(:write).with("\000\000\000\007foo/bar") |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 236 | @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 Clark | f6aa86a | 2008-06-18 01:11:07 +0000 | [diff] [blame] | 252 | |
| 253 | describe MemoryBuffer do |
| 254 | before(:each) do |
| 255 | @buffer = MemoryBuffer.new |
| 256 | end |
| 257 | |
Kevin Clark | 6c30dbb | 2008-06-18 01:15:25 +0000 | [diff] [blame] | 258 | 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 Clark | bf2ff24 | 2008-07-08 23:20:15 +0000 | [diff] [blame] | 262 | s.slice!(-4..-1) |
| 263 | @buffer.read(@buffer.available).should == " is a " |
Kevin Clark | 6c30dbb | 2008-06-18 01:15:25 +0000 | [diff] [blame] | 264 | end |
| 265 | |
Kevin Clark | f6aa86a | 2008-06-18 01:11:07 +0000 | [diff] [blame] | 266 | 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 Clark | 6c30dbb | 2008-06-18 01:15:25 +0000 | [diff] [blame] | 293 | 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 Clark | f6aa86a | 2008-06-18 01:11:07 +0000 | [diff] [blame] | 302 | 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 Clark | f4e7008 | 2008-07-18 22:27:03 +0000 | [diff] [blame] | 315 | @input = mock("Input", :closed? => false) |
| 316 | @output = mock("Output", :closed? => false) |
Kevin Clark | f6aa86a | 2008-06-18 01:11:07 +0000 | [diff] [blame] | 317 | @trans = IOStreamTransport.new(@input, @output) |
| 318 | end |
| 319 | |
Kevin Clark | f4e7008 | 2008-07-18 22:27:03 +0000 | [diff] [blame] | 320 | 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] | 321 | @trans.should be_open |
Kevin Clark | f4e7008 | 2008-07-18 22:27:03 +0000 | [diff] [blame] | 322 | @input.stub!(:closed?).and_return(true) |
Kevin Clark | f6aa86a | 2008-06-18 01:11:07 +0000 | [diff] [blame] | 323 | @trans.should be_open |
Kevin Clark | f4e7008 | 2008-07-18 22:27:03 +0000 | [diff] [blame] | 324 | @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 Clark | f6aa86a | 2008-06-18 01:11:07 +0000 | [diff] [blame] | 329 | 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 Clark | f4e7008 | 2008-07-18 22:27:03 +0000 | [diff] [blame] | 337 | |
| 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 Clark | f6aa86a | 2008-06-18 01:11:07 +0000 | [diff] [blame] | 343 | end |
Kevin Clark | 531e020 | 2008-06-18 01:10:17 +0000 | [diff] [blame] | 344 | end |