| # |
| # 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 'spec_helper' |
| |
| describe 'BaseTransport' do |
| |
| describe Thrift::TransportException do |
| it "should make type accessible" do |
| exc = Thrift::TransportException.new(Thrift::TransportException::ALREADY_OPEN, "msg") |
| expect(exc.type).to eq(Thrift::TransportException::ALREADY_OPEN) |
| expect(exc.message).to eq("msg") |
| end |
| end |
| |
| describe Thrift::BaseTransport do |
| it "should read the specified size" do |
| transport = Thrift::BaseTransport.new |
| expect(transport).to receive(:read).with(40).ordered.and_return("10 letters") |
| expect(transport).to receive(:read).with(30).ordered.and_return("fifteen letters") |
| expect(transport).to receive(:read).with(15).ordered.and_return("more characters") |
| expect(transport.read_all(40)).to eq("10 lettersfifteen lettersmore characters") |
| end |
| |
| it "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| |
| expect(Thrift::BaseTransport.method_defined?(sym)).to be_truthy |
| end |
| end |
| |
| it "should alias << to write" do |
| expect(Thrift::BaseTransport.instance_method(:<<)).to eq(Thrift::BaseTransport.instance_method(:write)) |
| end |
| |
| it "should provide a reasonable to_s" do |
| expect(Thrift::BaseTransport.new.to_s).to eq("base") |
| end |
| end |
| |
| describe Thrift::BaseServerTransport do |
| it "should stub out its methods" do |
| [:listen, :accept, :close].each do |sym| |
| expect(Thrift::BaseServerTransport.method_defined?(sym)).to be_truthy |
| end |
| end |
| end |
| |
| describe Thrift::BaseTransportFactory do |
| it "should return the transport it's given" do |
| transport = double("Transport") |
| expect(Thrift::BaseTransportFactory.new.get_transport(transport)).to eql(transport) |
| end |
| |
| it "should provide a reasonable to_s" do |
| expect(Thrift::BaseTransportFactory.new.to_s).to eq("base") |
| end |
| end |
| |
| describe Thrift::BufferedTransport do |
| it "should provide a to_s that describes the encapsulation" do |
| trans = double("Transport") |
| expect(trans).to receive(:to_s).and_return("mock") |
| expect(Thrift::BufferedTransport.new(trans).to_s).to eq("buffered(mock)") |
| end |
| |
| it "should pass through everything but write/flush/read" do |
| trans = double("Transport") |
| expect(trans).to receive(:open?).ordered.and_return("+ open?") |
| expect(trans).to receive(:open).ordered.and_return("+ open") |
| expect(trans).to receive(:flush).ordered # from the close |
| expect(trans).to receive(:close).ordered.and_return("+ close") |
| btrans = Thrift::BufferedTransport.new(trans) |
| expect(btrans.open?).to eq("+ open?") |
| expect(btrans.open).to eq("+ open") |
| expect(btrans.close).to eq("+ close") |
| end |
| |
| it "should buffer reads in chunks of #{Thrift::BufferedTransport::DEFAULT_BUFFER}" do |
| trans = double("Transport") |
| expect(trans).to receive(:read).with(Thrift::BufferedTransport::DEFAULT_BUFFER).and_return("lorum ipsum dolor emet") |
| btrans = Thrift::BufferedTransport.new(trans) |
| expect(btrans.read(6)).to eq("lorum ") |
| expect(btrans.read(6)).to eq("ipsum ") |
| expect(btrans.read(6)).to eq("dolor ") |
| expect(btrans.read(6)).to eq("emet") |
| end |
| |
| it "should buffer writes and send them on flush" do |
| trans = double("Transport") |
| btrans = Thrift::BufferedTransport.new(trans) |
| btrans.write("one/") |
| btrans.write("two/") |
| btrans.write("three/") |
| expect(trans).to receive(:write).with("one/two/three/").ordered |
| expect(trans).to receive(:flush).ordered |
| btrans.flush |
| end |
| |
| it "should only send buffered data once" do |
| trans = double("Transport") |
| btrans = Thrift::BufferedTransport.new(trans) |
| btrans.write("one/") |
| btrans.write("two/") |
| btrans.write("three/") |
| expect(trans).to receive(:write).with("one/two/three/") |
| allow(trans).to receive(:flush) |
| btrans.flush |
| # Nothing to flush with no data |
| btrans.flush |
| end |
| |
| it "should flush on close" do |
| trans = double("Transport") |
| expect(trans).to receive(:close) |
| btrans = Thrift::BufferedTransport.new(trans) |
| expect(btrans).to receive(:flush) |
| btrans.close |
| end |
| |
| it "should not write to socket if there's no data" do |
| trans = double("Transport") |
| expect(trans).to receive(:flush) |
| btrans = Thrift::BufferedTransport.new(trans) |
| btrans.flush |
| end |
| end |
| |
| describe Thrift::BufferedTransportFactory do |
| it "should wrap the given transport in a BufferedTransport" do |
| trans = double("Transport") |
| btrans = double("BufferedTransport") |
| expect(Thrift::BufferedTransport).to receive(:new).with(trans).and_return(btrans) |
| expect(Thrift::BufferedTransportFactory.new.get_transport(trans)).to eq(btrans) |
| end |
| |
| it "should provide a reasonable to_s" do |
| expect(Thrift::BufferedTransportFactory.new.to_s).to eq("buffered") |
| end |
| end |
| |
| describe Thrift::FramedTransport do |
| before(:each) do |
| @trans = double("Transport") |
| end |
| |
| it "should provide a to_s that describes the encapsulation" do |
| trans = double("Transport") |
| expect(trans).to receive(:to_s).and_return("mock") |
| expect(Thrift::FramedTransport.new(trans).to_s).to eq("framed(mock)") |
| end |
| |
| it "should pass through open?/open/close" do |
| ftrans = Thrift::FramedTransport.new(@trans) |
| expect(@trans).to receive(:open?).ordered.and_return("+ open?") |
| expect(@trans).to receive(:open).ordered.and_return("+ open") |
| expect(@trans).to receive(:close).ordered.and_return("+ close") |
| expect(ftrans.open?).to eq("+ open?") |
| expect(ftrans.open).to eq("+ open") |
| expect(ftrans.close).to eq("+ close") |
| end |
| |
| it "should pass through read when read is turned off" do |
| ftrans = Thrift::FramedTransport.new(@trans, false, true) |
| expect(@trans).to receive(:read).with(17).ordered.and_return("+ read") |
| expect(ftrans.read(17)).to eq("+ read") |
| end |
| |
| it "should pass through write/flush when write is turned off" do |
| ftrans = Thrift::FramedTransport.new(@trans, true, false) |
| expect(@trans).to receive(:write).with("foo").ordered.and_return("+ write") |
| expect(@trans).to receive(:flush).ordered.and_return("+ flush") |
| expect(ftrans.write("foo")).to eq("+ write") |
| expect(ftrans.flush).to eq("+ flush") |
| end |
| |
| it "should return a full frame if asked for >= the frame's length" do |
| frame = "this is a frame" |
| expect(@trans).to receive(:read_all).with(4).and_return("\000\000\000\017") |
| expect(@trans).to receive(:read_all).with(frame.length).and_return(frame) |
| expect(Thrift::FramedTransport.new(@trans).read(frame.length + 10)).to eq(frame) |
| end |
| |
| it "should return slices of the frame when asked for < the frame's length" do |
| frame = "this is a frame" |
| expect(@trans).to receive(:read_all).with(4).and_return("\000\000\000\017") |
| expect(@trans).to receive(:read_all).with(frame.length).and_return(frame) |
| ftrans = Thrift::FramedTransport.new(@trans) |
| expect(ftrans.read(4)).to eq("this") |
| expect(ftrans.read(4)).to eq(" is ") |
| expect(ftrans.read(16)).to eq("a frame") |
| end |
| |
| it "should return nothing if asked for <= 0" do |
| expect(Thrift::FramedTransport.new(@trans).read(-2)).to eq("") |
| end |
| |
| it "should pull a new frame when the first is exhausted" do |
| frame = "this is a frame" |
| frame2 = "yet another frame" |
| expect(@trans).to receive(:read_all).with(4).and_return("\000\000\000\017", "\000\000\000\021") |
| expect(@trans).to receive(:read_all).with(frame.length).and_return(frame) |
| expect(@trans).to receive(:read_all).with(frame2.length).and_return(frame2) |
| ftrans = Thrift::FramedTransport.new(@trans) |
| expect(ftrans.read(4)).to eq("this") |
| expect(ftrans.read(8)).to eq(" is a fr") |
| expect(ftrans.read(6)).to eq("ame") |
| expect(ftrans.read(4)).to eq("yet ") |
| expect(ftrans.read(16)).to eq("another frame") |
| end |
| |
| it "should buffer writes" do |
| ftrans = Thrift::FramedTransport.new(@trans) |
| expect(@trans).not_to receive(:write) |
| ftrans.write("foo") |
| ftrans.write("bar") |
| ftrans.write("this is a frame") |
| end |
| |
| it "should write slices of the buffer" do |
| ftrans = Thrift::FramedTransport.new(@trans) |
| ftrans.write("foobar", 3) |
| ftrans.write("barfoo", 1) |
| allow(@trans).to receive(:flush) |
| expect(@trans).to receive(:write).with("\000\000\000\004foob") |
| ftrans.flush |
| end |
| |
| it "should flush frames with a 4-byte header" do |
| ftrans = Thrift::FramedTransport.new(@trans) |
| expect(@trans).to receive(:write).with("\000\000\000\035one/two/three/this is a frame").ordered |
| expect(@trans).to receive(:flush).ordered |
| ftrans.write("one/") |
| ftrans.write("two/") |
| ftrans.write("three/") |
| ftrans.write("this is a frame") |
| ftrans.flush |
| end |
| |
| it "should not flush the same buffered data twice" do |
| ftrans = Thrift::FramedTransport.new(@trans) |
| expect(@trans).to receive(:write).with("\000\000\000\007foo/bar") |
| allow(@trans).to receive(:flush) |
| ftrans.write("foo") |
| ftrans.write("/bar") |
| ftrans.flush |
| expect(@trans).to receive(:write).with("\000\000\000\000") |
| ftrans.flush |
| end |
| end |
| |
| describe Thrift::FramedTransportFactory do |
| it "should wrap the given transport in a FramedTransport" do |
| trans = double("Transport") |
| expect(Thrift::FramedTransport).to receive(:new).with(trans) |
| Thrift::FramedTransportFactory.new.get_transport(trans) |
| end |
| |
| it "should provide a reasonable to_s" do |
| expect(Thrift::FramedTransportFactory.new.to_s).to eq("framed") |
| end |
| end |
| |
| describe Thrift::MemoryBufferTransport do |
| before(:each) do |
| @buffer = Thrift::MemoryBufferTransport.new |
| end |
| |
| it "should provide a reasonable to_s" do |
| expect(@buffer.to_s).to eq("memory") |
| end |
| |
| it "should accept a buffer on input and use it directly" do |
| s = "this is a test" |
| @buffer = Thrift::MemoryBufferTransport.new(s) |
| expect(@buffer.read(4)).to eq("this") |
| s.slice!(-4..-1) |
| expect(@buffer.read(@buffer.available)).to eq(" is a ") |
| end |
| |
| it "should always remain open" do |
| expect(@buffer).to be_open |
| @buffer.close |
| expect(@buffer).to be_open |
| end |
| |
| it "should respond to peek and available" do |
| @buffer.write "some data" |
| expect(@buffer.peek).to be_truthy |
| expect(@buffer.available).to eq(9) |
| @buffer.read(4) |
| expect(@buffer.peek).to be_truthy |
| expect(@buffer.available).to eq(5) |
| @buffer.read(5) |
| expect(@buffer.peek).to be_falsey |
| expect(@buffer.available).to eq(0) |
| end |
| |
| it "should be able to reset the buffer" do |
| @buffer.write "test data" |
| @buffer.reset_buffer("foobar") |
| expect(@buffer.available).to eq(6) |
| expect(@buffer.read(@buffer.available)).to eq("foobar") |
| @buffer.reset_buffer |
| expect(@buffer.available).to eq(0) |
| end |
| |
| it "should copy the given string when resetting the buffer" do |
| s = "this is a test" |
| @buffer.reset_buffer(s) |
| expect(@buffer.available).to eq(14) |
| @buffer.read(10) |
| expect(@buffer.available).to eq(4) |
| expect(s).to eq("this is a test") |
| end |
| |
| it "should return from read what was given in write" do |
| @buffer.write "test data" |
| expect(@buffer.read(4)).to eq("test") |
| expect(@buffer.read(@buffer.available)).to eq(" data") |
| @buffer.write "foo" |
| @buffer.write " bar" |
| expect(@buffer.read(@buffer.available)).to eq("foo bar") |
| end |
| |
| it "should throw an EOFError when there isn't enough data in the buffer" do |
| @buffer.reset_buffer("") |
| expect{@buffer.read(1)}.to raise_error(EOFError) |
| |
| @buffer.reset_buffer("1234") |
| expect{@buffer.read(5)}.to raise_error(EOFError) |
| end |
| end |
| |
| describe Thrift::IOStreamTransport do |
| before(:each) do |
| @input = double("Input", :closed? => false) |
| @output = double("Output", :closed? => false) |
| @trans = Thrift::IOStreamTransport.new(@input, @output) |
| end |
| |
| it "should provide a reasonable to_s" do |
| expect(@input).to receive(:to_s).and_return("mock_input") |
| expect(@output).to receive(:to_s).and_return("mock_output") |
| expect(@trans.to_s).to eq("iostream(input=mock_input,output=mock_output)") |
| end |
| |
| it "should be open as long as both input or output are open" do |
| expect(@trans).to be_open |
| allow(@input).to receive(:closed?).and_return(true) |
| expect(@trans).to be_open |
| allow(@input).to receive(:closed?).and_return(false) |
| allow(@output).to receive(:closed?).and_return(true) |
| expect(@trans).to be_open |
| allow(@input).to receive(:closed?).and_return(true) |
| expect(@trans).not_to be_open |
| end |
| |
| it "should pass through read/write to input/output" do |
| expect(@input).to receive(:read).with(17).and_return("+ read") |
| expect(@output).to receive(:write).with("foobar").and_return("+ write") |
| expect(@trans.read(17)).to eq("+ read") |
| expect(@trans.write("foobar")).to eq("+ write") |
| end |
| |
| it "should close both input and output when closed" do |
| expect(@input).to receive(:close) |
| expect(@output).to receive(:close) |
| @trans.close |
| end |
| end |
| end |