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 ThriftStructSpec < Spec::ExampleGroupinclude Thriftinclude SpecNamespacedescribe Struct doit "should iterate over all fields properly" dofields = {}Foo.new.each_field { |fid,field_info| fields[fid] = field_info }fields.should == Foo::FIELDSendit "should initialize all fields to defaults" dostruct = Foo.newstruct.simple.should == 53struct.words.should == "words"struct.hello.should == Hello.new(:greeting => 'hello, world!')struct.ints.should == [1, 2, 2, 3]struct.complex.should be_nilstruct.shorts.should == Set.new([5, 17, 239])endit "should not share default values between instances" dobeginstruct = Foo.newstruct.ints << 17Foo.new.ints.should == [1,2,2,3]ensure# ensure no leakage to other testsFoo::FIELDS[4][:default] = [1,2,2,3]endendit "should properly initialize boolean values" dostruct = BoolStruct.new(:yesno => false)struct.yesno.should be_falseendit "should have proper == semantics" doFoo.new.should_not == Hello.newFoo.new.should == Foo.newFoo.new(:simple => 52).should_not == Foo.newendit "should read itself off the wire" dostruct = Foo.newprot = BaseProtocol.new(mock("transport"))prot.should_receive(:read_struct_begin).twiceprot.should_receive(:read_struct_end).twiceprot.should_receive(:read_field_begin).and_return(['complex', Types::MAP, 5], # Foo['words', Types::STRING, 2], # Foo['hello', Types::STRUCT, 3], # Foo['greeting', Types::STRING, 1], # Hello[nil, Types::STOP, 0], # Hello['simple', Types::I32, 1], # Foo['ints', Types::LIST, 4], # Foo['shorts', Types::SET, 6], # Foo[nil, Types::STOP, 0] # Hello)prot.should_receive(:read_field_end).exactly(7).timesprot.should_receive(:read_map_begin).and_return([Types::I32, Types::MAP, 2], # complex[Types::STRING, Types::DOUBLE, 2], # complex/1/value[Types::STRING, Types::DOUBLE, 1] # complex/2/value)prot.should_receive(:read_map_end).exactly(3).timesprot.should_receive(:read_list_begin).and_return([Types::I32, 4])prot.should_receive(:read_list_end)prot.should_receive(:read_set_begin).and_return([Types::I16, 2])prot.should_receive(:read_set_end)prot.should_receive(:read_i32).and_return(1, 14, # complex keys42, # simple4, 23, 4, 29 # ints)prot.should_receive(:read_string).and_return("pi", "e", "feigenbaum", "apple banana", "what's up?")prot.should_receive(:read_double).and_return(Math::PI, Math::E, 4.669201609)prot.should_receive(:read_i16).and_return(2, 3)prot.should_not_receive(:skip)struct.read(prot)struct.simple.should == 42struct.complex.should == {1 => {"pi" => Math::PI, "e" => Math::E}, 14 => {"feigenbaum" => 4.669201609}}struct.hello.should == Hello.new(:greeting => "what's up?")struct.words.should == "apple banana"struct.ints.should == [4, 23, 4, 29]struct.shorts.should == Set.new([3, 2])endit "should skip unexpected fields in structs and use default values" dostruct = Foo.newprot = BaseProtocol.new(mock("transport"))prot.should_receive(:read_struct_begin)prot.should_receive(:read_struct_end)prot.should_receive(:read_field_begin).and_return(['simple', Types::I32, 1],['complex', Types::STRUCT, 5],['thinz', Types::MAP, 7],['foobar', Types::I32, 3],['words', Types::STRING, 2],[nil, Types::STOP, 0])prot.should_receive(:read_field_end).exactly(5).timesprot.should_receive(:read_i32).and_return(42)prot.should_receive(:read_string).and_return("foobar")prot.should_receive(:skip).with(Types::STRUCT)prot.should_receive(:skip).with(Types::MAP)# prot.should_receive(:read_map_begin).and_return([Types::I32, Types::I32, 0])# prot.should_receive(:read_map_end)prot.should_receive(:skip).with(Types::I32)struct.read(prot)struct.simple.should == 42struct.complex.should be_nilstruct.words.should == "foobar"struct.hello.should == Hello.new(:greeting => 'hello, world!')struct.ints.should == [1, 2, 2, 3]struct.shorts.should == Set.new([5, 17, 239])endit "should write itself to the wire" doprot = BaseProtocol.new(mock("transport")) #mock("Protocol")prot.should_receive(:write_struct_begin).with("SpecNamespace::Foo")prot.should_receive(:write_struct_begin).with("SpecNamespace::Hello")prot.should_receive(:write_struct_end).twiceprot.should_receive(:write_field_begin).with('ints', Types::LIST, 4)prot.should_receive(:write_i32).with(1)prot.should_receive(:write_i32).with(2).twiceprot.should_receive(:write_i32).with(3)prot.should_receive(:write_field_begin).with('complex', Types::MAP, 5)prot.should_receive(:write_i32).with(5)prot.should_receive(:write_string).with('foo')prot.should_receive(:write_double).with(1.23)prot.should_receive(:write_field_begin).with('shorts', Types::SET, 6)prot.should_receive(:write_i16).with(5)prot.should_receive(:write_i16).with(17)prot.should_receive(:write_i16).with(239)prot.should_receive(:write_field_stop).twiceprot.should_receive(:write_field_end).exactly(6).timesprot.should_receive(:write_field_begin).with('simple', Types::I32, 1)prot.should_receive(:write_i32).with(53)prot.should_receive(:write_field_begin).with('hello', Types::STRUCT, 3)prot.should_receive(:write_field_begin).with('greeting', Types::STRING, 1)prot.should_receive(:write_string).with('hello, world!')prot.should_receive(:write_map_begin).with(Types::I32, Types::MAP, 1)prot.should_receive(:write_map_begin).with(Types::STRING, Types::DOUBLE, 1)prot.should_receive(:write_map_end).twiceprot.should_receive(:write_list_begin).with(Types::I32, 4)prot.should_receive(:write_list_end)prot.should_receive(:write_set_begin).with(Types::I16, 3)prot.should_receive(:write_set_end)struct = Foo.newstruct.words = nilstruct.complex = {5 => {"foo" => 1.23}}struct.write(prot)endit "should raise an exception if presented with an unknown container" do# yeah this is silly, but I'm going for code coverage herestruct = Foo.newlambda { struct.send :write_container, nil, nil, {:type => "foo"} }.should raise_error(StandardError, "Not a container type: foo")endit "should support optional type-checking in Thrift::Struct.new" doThrift.type_checking = truebeginlambda { Hello.new(:greeting => 3) }.should raise_error(TypeError, "Expected Types::STRING, received Fixnum for field greeting")ensureThrift.type_checking = falseendlambda { Hello.new(:greeting => 3) }.should_not raise_error(TypeError)endit "should support optional type-checking in field accessors" doThrift.type_checking = truebeginhello = Hello.newlambda { hello.greeting = 3 }.should raise_error(TypeError, "Expected Types::STRING, received Fixnum for field greeting")ensureThrift.type_checking = falseendlambda { hello.greeting = 3 }.should_not raise_error(TypeError)endit "should raise an exception when unknown types are given to Thrift::Struct.new" dolambda { Hello.new(:fish => 'salmon') }.should raise_error(Exception, "Unknown key given to SpecNamespace::Hello.new: fish")endit "should support `raise Xception, 'message'` for Exception structs" dobeginraise Xception, "something happened"rescue Thrift::Exception => ee.message.should == "something happened"e.code.should == 1# ensure it gets serialized properly, this is the really important partprot = BaseProtocol.new(mock("trans"))prot.should_receive(:write_struct_begin).with("SpecNamespace::Xception")prot.should_receive(:write_struct_end)prot.should_receive(:write_field_begin).with('message', Types::STRING, 1)#, "something happened")prot.should_receive(:write_string).with("something happened")prot.should_receive(:write_field_begin).with('code', Types::I32, 2)#, 1)prot.should_receive(:write_i32).with(1)prot.should_receive(:write_field_stop)prot.should_receive(:write_field_end).twicee.write(prot)endendit "should support the regular initializer for exception structs" dobeginraise Xception, :message => "something happened", :code => 5rescue Thrift::Exception => ee.message.should == "something happened"e.code.should == 5prot = BaseProtocol.new(mock("trans"))prot.should_receive(:write_struct_begin).with("SpecNamespace::Xception")prot.should_receive(:write_struct_end)prot.should_receive(:write_field_begin).with('message', Types::STRING, 1)prot.should_receive(:write_string).with("something happened")prot.should_receive(:write_field_begin).with('code', Types::I32, 2)prot.should_receive(:write_i32).with(5)prot.should_receive(:write_field_stop)prot.should_receive(:write_field_end).twicee.write(prot)endendendend