Rev 30 | Blame | Compare with Previous | Last modification | View Log | RSS feed
## Licensed to the Apache Software Foundation (ASF) under one# or more contributor license agreements. See the NOTICE file# distributed with this work for additional information# regarding copyright ownership. The ASF licenses this file# to you under the Apache License, Version 2.0 (the# "License"); you may not use this file except in compliance# with the License. You may obtain a copy of the License at## http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing,# software distributed under the License is distributed on an# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY# KIND, either express or implied. See the License for the# specific language governing permissions and limitations# under the License.#require File.dirname(__FILE__) + '/spec_helper'class ThriftBaseTransportSpec < Spec::ExampleGroupinclude Thriftdescribe TransportException doit "should make type accessible" doexc = TransportException.new(TransportException::ALREADY_OPEN, "msg")exc.type.should == TransportException::ALREADY_OPENexc.message.should == "msg"endenddescribe BaseTransport doit "should read the specified size" dotransport = BaseTransport.newtransport.should_receive(:read).with(40).ordered.and_return("10 letters")transport.should_receive(:read).with(30).ordered.and_return("fifteen letters")transport.should_receive(:read).with(15).ordered.and_return("more characters")transport.read_all(40).should == "10 lettersfifteen lettersmore characters"endit "should stub out the rest of the methods" do# can't test for stubbiness, so just make sure they're defined[:open?, :open, :close, :read, :write, :flush].each do |sym|BaseTransport.method_defined?(sym).should be_trueendendit "should alias << to write" doBaseTransport.instance_method(:<<).should == BaseTransport.instance_method(:write)endenddescribe BaseServerTransport doit "should stub out its methods" do[:listen, :accept, :close].each do |sym|BaseServerTransport.method_defined?(sym).should be_trueendendenddescribe BaseTransportFactory doit "should return the transport it's given" dotransport = mock("Transport")BaseTransportFactory.new.get_transport(transport).should eql(transport)endenddescribe BufferedTransport doit "should pass through everything but write/flush/read" dotrans = mock("Transport")trans.should_receive(:open?).ordered.and_return("+ open?")trans.should_receive(:open).ordered.and_return("+ open")trans.should_receive(:flush).ordered # from the closetrans.should_receive(:close).ordered.and_return("+ close")btrans = BufferedTransport.new(trans)btrans.open?.should == "+ open?"btrans.open.should == "+ open"btrans.close.should == "+ close"endit "should buffer reads in chunks of #{BufferedTransport::DEFAULT_BUFFER}" dotrans = mock("Transport")trans.should_receive(:read).with(BufferedTransport::DEFAULT_BUFFER).and_return("lorum ipsum dolor emet")btrans = BufferedTransport.new(trans)btrans.read(6).should == "lorum "btrans.read(6).should == "ipsum "btrans.read(6).should == "dolor "btrans.read(6).should == "emet"endit "should buffer writes and send them on flush" dotrans = mock("Transport")btrans = BufferedTransport.new(trans)btrans.write("one/")btrans.write("two/")btrans.write("three/")trans.should_receive(:write).with("one/two/three/").orderedtrans.should_receive(:flush).orderedbtrans.flushendit "should only send buffered data once" dotrans = mock("Transport")btrans = BufferedTransport.new(trans)btrans.write("one/")btrans.write("two/")btrans.write("three/")trans.should_receive(:write).with("one/two/three/")trans.stub!(:flush)btrans.flush# Nothing to flush with no databtrans.flushendit "should flush on close" dotrans = mock("Transport")trans.should_receive(:close)btrans = BufferedTransport.new(trans)btrans.should_receive(:flush)btrans.closeendit "should not write to socket if there's no data" dotrans = mock("Transport")trans.should_receive(:flush)btrans = BufferedTransport.new(trans)btrans.flushendenddescribe BufferedTransportFactory doit "should wrap the given transport in a BufferedTransport" dotrans = mock("Transport")btrans = mock("BufferedTransport")BufferedTransport.should_receive(:new).with(trans).and_return(btrans)BufferedTransportFactory.new.get_transport(trans).should == btransendenddescribe FramedTransport dobefore(:each) do@trans = mock("Transport")endit "should pass through open?/open/close" doftrans = FramedTransport.new(@trans)@trans.should_receive(:open?).ordered.and_return("+ open?")@trans.should_receive(:open).ordered.and_return("+ open")@trans.should_receive(:close).ordered.and_return("+ close")ftrans.open?.should == "+ open?"ftrans.open.should == "+ open"ftrans.close.should == "+ close"endit "should pass through read when read is turned off" doftrans = FramedTransport.new(@trans, false, true)@trans.should_receive(:read).with(17).ordered.and_return("+ read")ftrans.read(17).should == "+ read"endit "should pass through write/flush when write is turned off" doftrans = FramedTransport.new(@trans, true, false)@trans.should_receive(:write).with("foo").ordered.and_return("+ write")@trans.should_receive(:flush).ordered.and_return("+ flush")ftrans.write("foo").should == "+ write"ftrans.flush.should == "+ flush"endit "should return a full frame if asked for >= the frame's length" doframe = "this is a frame"@trans.should_receive(:read_all).with(4).and_return("\000\000\000\017")@trans.should_receive(:read_all).with(frame.length).and_return(frame)FramedTransport.new(@trans).read(frame.length + 10).should == frameendit "should return slices of the frame when asked for < the frame's length" doframe = "this is a frame"@trans.should_receive(:read_all).with(4).and_return("\000\000\000\017")@trans.should_receive(:read_all).with(frame.length).and_return(frame)ftrans = FramedTransport.new(@trans)ftrans.read(4).should == "this"ftrans.read(4).should == " is "ftrans.read(16).should == "a frame"endit "should return nothing if asked for <= 0" doFramedTransport.new(@trans).read(-2).should == ""endit "should pull a new frame when the first is exhausted" doframe = "this is a frame"frame2 = "yet another frame"@trans.should_receive(:read_all).with(4).and_return("\000\000\000\017", "\000\000\000\021")@trans.should_receive(:read_all).with(frame.length).and_return(frame)@trans.should_receive(:read_all).with(frame2.length).and_return(frame2)ftrans = FramedTransport.new(@trans)ftrans.read(4).should == "this"ftrans.read(8).should == " is a fr"ftrans.read(6).should == "ame"ftrans.read(4).should == "yet "ftrans.read(16).should == "another frame"endit "should buffer writes" doftrans = FramedTransport.new(@trans)@trans.should_not_receive(:write)ftrans.write("foo")ftrans.write("bar")ftrans.write("this is a frame")endit "should write slices of the buffer" doftrans = FramedTransport.new(@trans)ftrans.write("foobar", 3)ftrans.write("barfoo", 1)@trans.stub!(:flush)@trans.should_receive(:write).with("\000\000\000\004foob")ftrans.flushendit "should flush frames with a 4-byte header" doftrans = FramedTransport.new(@trans)@trans.should_receive(:write).with("\000\000\000\035one/two/three/this is a frame").ordered@trans.should_receive(:flush).orderedftrans.write("one/")ftrans.write("two/")ftrans.write("three/")ftrans.write("this is a frame")ftrans.flushendit "should not flush the same buffered data twice" doftrans = FramedTransport.new(@trans)@trans.should_receive(:write).with("\000\000\000\007foo/bar")@trans.stub!(:flush)ftrans.write("foo")ftrans.write("/bar")ftrans.flush@trans.should_receive(:write).with("\000\000\000\000")ftrans.flushendenddescribe FramedTransportFactory doit "should wrap the given transport in a FramedTransport" dotrans = mock("Transport")FramedTransport.should_receive(:new).with(trans)FramedTransportFactory.new.get_transport(trans)endenddescribe MemoryBufferTransport dobefore(:each) do@buffer = MemoryBufferTransport.newendit "should accept a buffer on input and use it directly" dos = "this is a test"@buffer = MemoryBufferTransport.new(s)@buffer.read(4).should == "this"s.slice!(-4..-1)@buffer.read(@buffer.available).should == " is a "endit "should always remain open" do@buffer.should be_open@buffer.close@buffer.should be_openendit "should respond to peek and available" do@buffer.write "some data"@buffer.peek.should be_true@buffer.available.should == 9@buffer.read(4)@buffer.peek.should be_true@buffer.available.should == 5@buffer.read(5)@buffer.peek.should be_false@buffer.available.should == 0endit "should be able to reset the buffer" do@buffer.write "test data"@buffer.reset_buffer("foobar")@buffer.available.should == 6@buffer.read(@buffer.available).should == "foobar"@buffer.reset_buffer@buffer.available.should == 0endit "should copy the given string when resetting the buffer" dos = "this is a test"@buffer.reset_buffer(s)@buffer.available.should == 14@buffer.read(10)@buffer.available.should == 4s.should == "this is a test"endit "should return from read what was given in write" do@buffer.write "test data"@buffer.read(4).should == "test"@buffer.read(@buffer.available).should == " data"@buffer.write "foo"@buffer.write " bar"@buffer.read(@buffer.available).should == "foo bar"endit "should throw an EOFError when there isn't enough data in the buffer" do@buffer.reset_buffer("")lambda{@buffer.read(1)}.should raise_error(EOFError)@buffer.reset_buffer("1234")lambda{@buffer.read(5)}.should raise_error(EOFError)endenddescribe IOStreamTransport dobefore(:each) do@input = mock("Input", :closed? => false)@output = mock("Output", :closed? => false)@trans = IOStreamTransport.new(@input, @output)endit "should be open as long as both input or output are open" do@trans.should be_open@input.stub!(:closed?).and_return(true)@trans.should be_open@input.stub!(:closed?).and_return(false)@output.stub!(:closed?).and_return(true)@trans.should be_open@input.stub!(:closed?).and_return(true)@trans.should_not be_openendit "should pass through read/write to input/output" do@input.should_receive(:read).with(17).and_return("+ read")@output.should_receive(:write).with("foobar").and_return("+ write")@trans.read(17).should == "+ read"@trans.write("foobar").should == "+ write"endit "should close both input and output when closed" do@input.should_receive(:close)@output.should_receive(:close)@trans.closeendendend