sessionm-thrift 0.8.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. data/CHANGELOG +1 -0
  2. data/README +43 -0
  3. data/benchmark/Benchmark.thrift +24 -0
  4. data/benchmark/benchmark.rb +271 -0
  5. data/benchmark/client.rb +74 -0
  6. data/benchmark/server.rb +82 -0
  7. data/benchmark/thin_server.rb +44 -0
  8. data/ext/binary_protocol_accelerated.c +441 -0
  9. data/ext/binary_protocol_accelerated.h +20 -0
  10. data/ext/compact_protocol.c +618 -0
  11. data/ext/compact_protocol.h +20 -0
  12. data/ext/constants.h +96 -0
  13. data/ext/extconf.rb +30 -0
  14. data/ext/macros.h +41 -0
  15. data/ext/memory_buffer.c +131 -0
  16. data/ext/memory_buffer.h +20 -0
  17. data/ext/protocol.c +185 -0
  18. data/ext/protocol.h +20 -0
  19. data/ext/strlcpy.c +41 -0
  20. data/ext/strlcpy.h +30 -0
  21. data/ext/struct.c +691 -0
  22. data/ext/struct.h +25 -0
  23. data/ext/thrift_native.c +196 -0
  24. data/lib/thrift.rb +64 -0
  25. data/lib/thrift/client.rb +62 -0
  26. data/lib/thrift/core_ext.rb +23 -0
  27. data/lib/thrift/core_ext/fixnum.rb +29 -0
  28. data/lib/thrift/exceptions.rb +84 -0
  29. data/lib/thrift/processor.rb +57 -0
  30. data/lib/thrift/protocol/base_protocol.rb +290 -0
  31. data/lib/thrift/protocol/binary_protocol.rb +229 -0
  32. data/lib/thrift/protocol/binary_protocol_accelerated.rb +39 -0
  33. data/lib/thrift/protocol/compact_protocol.rb +426 -0
  34. data/lib/thrift/serializer/deserializer.rb +33 -0
  35. data/lib/thrift/serializer/serializer.rb +34 -0
  36. data/lib/thrift/server/base_server.rb +31 -0
  37. data/lib/thrift/server/mongrel_http_server.rb +58 -0
  38. data/lib/thrift/server/nonblocking_server.rb +305 -0
  39. data/lib/thrift/server/simple_server.rb +43 -0
  40. data/lib/thrift/server/thread_pool_server.rb +75 -0
  41. data/lib/thrift/server/threaded_server.rb +47 -0
  42. data/lib/thrift/struct.rb +237 -0
  43. data/lib/thrift/struct_union.rb +192 -0
  44. data/lib/thrift/thrift_native.rb +24 -0
  45. data/lib/thrift/transport/base_server_transport.rb +37 -0
  46. data/lib/thrift/transport/base_transport.rb +107 -0
  47. data/lib/thrift/transport/buffered_transport.rb +108 -0
  48. data/lib/thrift/transport/framed_transport.rb +116 -0
  49. data/lib/thrift/transport/http_client_transport.rb +51 -0
  50. data/lib/thrift/transport/io_stream_transport.rb +39 -0
  51. data/lib/thrift/transport/memory_buffer_transport.rb +125 -0
  52. data/lib/thrift/transport/server_socket.rb +63 -0
  53. data/lib/thrift/transport/socket.rb +137 -0
  54. data/lib/thrift/transport/unix_server_socket.rb +60 -0
  55. data/lib/thrift/transport/unix_socket.rb +40 -0
  56. data/lib/thrift/types.rb +101 -0
  57. data/lib/thrift/union.rb +179 -0
  58. data/spec/ThriftSpec.thrift +132 -0
  59. data/spec/base_protocol_spec.rb +160 -0
  60. data/spec/base_transport_spec.rb +351 -0
  61. data/spec/binary_protocol_accelerated_spec.rb +46 -0
  62. data/spec/binary_protocol_spec.rb +61 -0
  63. data/spec/binary_protocol_spec_shared.rb +375 -0
  64. data/spec/client_spec.rb +100 -0
  65. data/spec/compact_protocol_spec.rb +144 -0
  66. data/spec/exception_spec.rb +142 -0
  67. data/spec/http_client_spec.rb +64 -0
  68. data/spec/mongrel_http_server_spec.rb +117 -0
  69. data/spec/nonblocking_server_spec.rb +265 -0
  70. data/spec/processor_spec.rb +83 -0
  71. data/spec/serializer_spec.rb +69 -0
  72. data/spec/server_socket_spec.rb +80 -0
  73. data/spec/server_spec.rb +159 -0
  74. data/spec/socket_spec.rb +61 -0
  75. data/spec/socket_spec_shared.rb +104 -0
  76. data/spec/spec_helper.rb +58 -0
  77. data/spec/struct_spec.rb +295 -0
  78. data/spec/types_spec.rb +116 -0
  79. data/spec/union_spec.rb +193 -0
  80. data/spec/unix_socket_spec.rb +108 -0
  81. metadata +247 -0
@@ -0,0 +1,159 @@
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
+ require File.expand_path("#{File.dirname(__FILE__)}/spec_helper")
20
+
21
+ class ThriftServerSpec < Spec::ExampleGroup
22
+ include Thrift
23
+
24
+ describe BaseServer do
25
+ it "should default to BaseTransportFactory and BinaryProtocolFactory when not specified" do
26
+ server = BaseServer.new(mock("Processor"), mock("BaseServerTransport"))
27
+ server.instance_variable_get(:'@transport_factory').should be_an_instance_of(BaseTransportFactory)
28
+ server.instance_variable_get(:'@protocol_factory').should be_an_instance_of(BinaryProtocolFactory)
29
+ end
30
+
31
+ # serve is a noop, so can't test that
32
+ end
33
+
34
+ shared_examples_for "servers" do
35
+ before(:each) do
36
+ @processor = mock("Processor")
37
+ @serverTrans = mock("ServerTransport")
38
+ @trans = mock("BaseTransport")
39
+ @prot = mock("BaseProtocol")
40
+ @client = mock("Client")
41
+ @server = server_type.new(@processor, @serverTrans, @trans, @prot)
42
+ end
43
+ end
44
+
45
+ describe SimpleServer do
46
+ it_should_behave_like "servers"
47
+
48
+ def server_type
49
+ SimpleServer
50
+ end
51
+
52
+ it "should serve in the main thread" do
53
+ @serverTrans.should_receive(:listen).ordered
54
+ @serverTrans.should_receive(:accept).exactly(3).times.and_return(@client)
55
+ @trans.should_receive(:get_transport).exactly(3).times.with(@client).and_return(@trans)
56
+ @prot.should_receive(:get_protocol).exactly(3).times.with(@trans).and_return(@prot)
57
+ x = 0
58
+ @processor.should_receive(:process).exactly(3).times.with(@prot, @prot).and_return do
59
+ case (x += 1)
60
+ when 1 then raise Thrift::TransportException
61
+ when 2 then raise Thrift::ProtocolException
62
+ when 3 then throw :stop
63
+ end
64
+ end
65
+ @trans.should_receive(:close).exactly(3).times
66
+ @serverTrans.should_receive(:close).ordered
67
+ lambda { @server.serve }.should throw_symbol(:stop)
68
+ end
69
+ end
70
+
71
+ describe ThreadedServer do
72
+ it_should_behave_like "servers"
73
+
74
+ def server_type
75
+ ThreadedServer
76
+ end
77
+
78
+ it "should serve using threads" do
79
+ @serverTrans.should_receive(:listen).ordered
80
+ @serverTrans.should_receive(:accept).exactly(3).times.and_return(@client)
81
+ @trans.should_receive(:get_transport).exactly(3).times.with(@client).and_return(@trans)
82
+ @prot.should_receive(:get_protocol).exactly(3).times.with(@trans).and_return(@prot)
83
+ Thread.should_receive(:new).with(@prot, @trans).exactly(3).times.and_yield(@prot, @trans)
84
+ x = 0
85
+ @processor.should_receive(:process).exactly(3).times.with(@prot, @prot).and_return do
86
+ case (x += 1)
87
+ when 1 then raise Thrift::TransportException
88
+ when 2 then raise Thrift::ProtocolException
89
+ when 3 then throw :stop
90
+ end
91
+ end
92
+ @trans.should_receive(:close).exactly(3).times
93
+ @serverTrans.should_receive(:close).ordered
94
+ lambda { @server.serve }.should throw_symbol(:stop)
95
+ end
96
+ end
97
+
98
+ describe ThreadPoolServer do
99
+ it_should_behave_like "servers"
100
+
101
+ def server_type
102
+ # put this stuff here so it runs before the server is created
103
+ @threadQ = mock("SizedQueue")
104
+ SizedQueue.should_receive(:new).with(20).and_return(@threadQ)
105
+ @excQ = mock("Queue")
106
+ Queue.should_receive(:new).and_return(@excQ)
107
+ ThreadPoolServer
108
+ end
109
+
110
+ it "should set up the queues" do
111
+ @server.instance_variable_get(:'@thread_q').should be(@threadQ)
112
+ @server.instance_variable_get(:'@exception_q').should be(@excQ)
113
+ end
114
+
115
+ it "should serve inside a thread" do
116
+ Thread.should_receive(:new).and_return do |block|
117
+ @server.should_receive(:serve)
118
+ block.call
119
+ @server.rspec_verify
120
+ end
121
+ @excQ.should_receive(:pop).and_throw(:popped)
122
+ lambda { @server.rescuable_serve }.should throw_symbol(:popped)
123
+ end
124
+
125
+ it "should avoid running the server twice when retrying rescuable_serve" do
126
+ Thread.should_receive(:new).and_return do |block|
127
+ @server.should_receive(:serve)
128
+ block.call
129
+ @server.rspec_verify
130
+ end
131
+ @excQ.should_receive(:pop).twice.and_throw(:popped)
132
+ lambda { @server.rescuable_serve }.should throw_symbol(:popped)
133
+ lambda { @server.rescuable_serve }.should throw_symbol(:popped)
134
+ end
135
+
136
+ it "should serve using a thread pool" do
137
+ @serverTrans.should_receive(:listen).ordered
138
+ @threadQ.should_receive(:push).with(:token)
139
+ @threadQ.should_receive(:pop)
140
+ Thread.should_receive(:new).and_yield
141
+ @serverTrans.should_receive(:accept).exactly(3).times.and_return(@client)
142
+ @trans.should_receive(:get_transport).exactly(3).times.and_return(@trans)
143
+ @prot.should_receive(:get_protocol).exactly(3).times.and_return(@prot)
144
+ x = 0
145
+ error = RuntimeError.new("Stopped")
146
+ @processor.should_receive(:process).exactly(3).times.with(@prot, @prot).and_return do
147
+ case (x += 1)
148
+ when 1 then raise Thrift::TransportException
149
+ when 2 then raise Thrift::ProtocolException
150
+ when 3 then raise error
151
+ end
152
+ end
153
+ @trans.should_receive(:close).exactly(3).times
154
+ @excQ.should_receive(:push).with(error).and_throw(:stop)
155
+ @serverTrans.should_receive(:close)
156
+ lambda { @server.serve }.should throw_symbol(:stop)
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,61 @@
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
+
20
+ require File.expand_path("#{File.dirname(__FILE__)}/spec_helper")
21
+ require File.expand_path("#{File.dirname(__FILE__)}/socket_spec_shared")
22
+
23
+ class ThriftSocketSpec < Spec::ExampleGroup
24
+ include Thrift
25
+
26
+ describe Socket do
27
+ before(:each) do
28
+ @socket = Socket.new
29
+ @handle = mock("Handle", :closed? => false)
30
+ @handle.stub!(:close)
31
+ @handle.stub!(:connect_nonblock)
32
+ ::Socket.stub!(:new).and_return(@handle)
33
+ end
34
+
35
+ it_should_behave_like "a socket"
36
+
37
+ it "should raise a TransportException when it cannot open a socket" do
38
+ ::Socket.should_receive(:new).and_raise(StandardError)
39
+ lambda { @socket.open }.should raise_error(Thrift::TransportException) { |e| e.type.should == Thrift::TransportException::NOT_OPEN }
40
+ end
41
+
42
+ it "should open a ::Socket with default args" do
43
+ ::Socket.should_receive(:new).and_return(mock("Handle", :connect_nonblock => true))
44
+ ::Socket.should_receive(:getaddrinfo).with("localhost", 9090).and_return([[]])
45
+ ::Socket.should_receive(:sockaddr_in)
46
+ @socket.open
47
+ end
48
+
49
+ it "should accept host/port options" do
50
+ ::Socket.should_receive(:new).and_return(mock("Handle", :connect_nonblock => true))
51
+ ::Socket.should_receive(:getaddrinfo).with("my.domain", 1234).and_return([[]])
52
+ ::Socket.should_receive(:sockaddr_in)
53
+ Socket.new('my.domain', 1234).open
54
+ end
55
+
56
+ it "should accept an optional timeout" do
57
+ ::Socket.stub!(:new)
58
+ Socket.new('localhost', 8080, 5).timeout.should == 5
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,104 @@
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
+
20
+ require File.expand_path("#{File.dirname(__FILE__)}/spec_helper")
21
+
22
+ shared_examples_for "a socket" do
23
+ it "should open a socket" do
24
+ @socket.open.should == @handle
25
+ end
26
+
27
+ it "should be open whenever it has a handle" do
28
+ @socket.should_not be_open
29
+ @socket.open
30
+ @socket.should be_open
31
+ @socket.handle = nil
32
+ @socket.should_not be_open
33
+ @socket.handle = @handle
34
+ @socket.close
35
+ @socket.should_not be_open
36
+ end
37
+
38
+ it "should write data to the handle" do
39
+ @socket.open
40
+ @handle.should_receive(:write).with("foobar")
41
+ @socket.write("foobar")
42
+ @handle.should_receive(:write).with("fail").and_raise(StandardError)
43
+ lambda { @socket.write("fail") }.should raise_error(Thrift::TransportException) { |e| e.type.should == Thrift::TransportException::NOT_OPEN }
44
+ end
45
+
46
+ it "should raise an error when it cannot read from the handle" do
47
+ @socket.open
48
+ @handle.should_receive(:readpartial).with(17).and_raise(StandardError)
49
+ lambda { @socket.read(17) }.should raise_error(Thrift::TransportException) { |e| e.type.should == Thrift::TransportException::NOT_OPEN }
50
+ end
51
+
52
+ it "should return the data read when reading from the handle works" do
53
+ @socket.open
54
+ @handle.should_receive(:readpartial).with(17).and_return("test data")
55
+ @socket.read(17).should == "test data"
56
+ end
57
+
58
+ it "should declare itself as closed when it has an error" do
59
+ @socket.open
60
+ @handle.should_receive(:write).with("fail").and_raise(StandardError)
61
+ @socket.should be_open
62
+ lambda { @socket.write("fail") }.should raise_error
63
+ @socket.should_not be_open
64
+ end
65
+
66
+ it "should raise an error when the stream is closed" do
67
+ @socket.open
68
+ @handle.stub!(:closed?).and_return(true)
69
+ @socket.should_not be_open
70
+ lambda { @socket.write("fail") }.should raise_error(IOError, "closed stream")
71
+ lambda { @socket.read(10) }.should raise_error(IOError, "closed stream")
72
+ end
73
+
74
+ it "should support the timeout accessor for read" do
75
+ @socket.timeout = 3
76
+ @socket.open
77
+ IO.should_receive(:select).with([@handle], nil, nil, 3).and_return([[@handle], [], []])
78
+ @handle.should_receive(:readpartial).with(17).and_return("test data")
79
+ @socket.read(17).should == "test data"
80
+ end
81
+
82
+ it "should support the timeout accessor for write" do
83
+ @socket.timeout = 3
84
+ @socket.open
85
+ IO.should_receive(:select).with(nil, [@handle], nil, 3).twice.and_return([[], [@handle], []])
86
+ @handle.should_receive(:write_nonblock).with("test data").and_return(4)
87
+ @handle.should_receive(:write_nonblock).with(" data").and_return(5)
88
+ @socket.write("test data").should == 9
89
+ end
90
+
91
+ it "should raise an error when read times out" do
92
+ @socket.timeout = 0.5
93
+ @socket.open
94
+ IO.should_receive(:select).once {sleep(0.5); nil}
95
+ lambda { @socket.read(17) }.should raise_error(Thrift::TransportException) { |e| e.type.should == Thrift::TransportException::TIMED_OUT }
96
+ end
97
+
98
+ it "should raise an error when write times out" do
99
+ @socket.timeout = 0.5
100
+ @socket.open
101
+ IO.should_receive(:select).with(nil, [@handle], nil, 0.5).any_number_of_times.and_return(nil)
102
+ lambda { @socket.write("test data") }.should raise_error(Thrift::TransportException) { |e| e.type.should == Thrift::TransportException::TIMED_OUT }
103
+ end
104
+ end
@@ -0,0 +1,58 @@
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
+
20
+ require 'rubygems'
21
+ require 'spec'
22
+
23
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. ext])
24
+
25
+ # pretend we already loaded fastthread, otherwise the nonblocking_server_spec
26
+ # will get screwed up
27
+ # $" << 'fastthread.bundle'
28
+
29
+ require File.dirname(__FILE__) + '/../lib/thrift'
30
+
31
+ class Object
32
+ # tee is a useful method, so let's let our tests have it
33
+ def tee(&block)
34
+ block.call(self)
35
+ self
36
+ end
37
+ end
38
+
39
+ Spec::Runner.configure do |configuration|
40
+ configuration.before(:each) do
41
+ Thrift.type_checking = true
42
+ end
43
+ end
44
+
45
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. test debug_proto gen-rb])
46
+ require "srv"
47
+ require "debug_proto_test_constants"
48
+
49
+ $:.unshift File.join(File.dirname(__FILE__), *%w[gen-rb])
50
+ require 'thrift_spec_types'
51
+ require 'nonblocking_service'
52
+
53
+ module Fixtures
54
+ COMPACT_PROTOCOL_TEST_STRUCT = COMPACT_TEST.dup
55
+ COMPACT_PROTOCOL_TEST_STRUCT.a_binary = [0,1,2,3,4,5,6,7,8].pack('c*')
56
+ COMPACT_PROTOCOL_TEST_STRUCT.set_byte_map = nil
57
+ COMPACT_PROTOCOL_TEST_STRUCT.map_byte_map = nil
58
+ end
@@ -0,0 +1,295 @@
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
+
20
+ require File.expand_path("#{File.dirname(__FILE__)}/spec_helper")
21
+
22
+ class ThriftStructSpec < Spec::ExampleGroup
23
+ include Thrift
24
+ include SpecNamespace
25
+
26
+ describe Struct do
27
+ it "should iterate over all fields properly" do
28
+ fields = {}
29
+ Foo.new.each_field { |fid,field_info| fields[fid] = field_info }
30
+ fields.should == Foo::FIELDS
31
+ end
32
+
33
+ it "should initialize all fields to defaults" do
34
+ validate_default_arguments(Foo.new)
35
+ end
36
+
37
+ it "should initialize all fields to defaults and accept a block argument" do
38
+ Foo.new do |f|
39
+ validate_default_arguments(f)
40
+ end
41
+ end
42
+
43
+ def validate_default_arguments(object)
44
+ object.simple.should == 53
45
+ object.words.should == "words"
46
+ object.hello.should == Hello.new(:greeting => 'hello, world!')
47
+ object.ints.should == [1, 2, 2, 3]
48
+ object.complex.should be_nil
49
+ object.shorts.should == Set.new([5, 17, 239])
50
+ end
51
+
52
+ it "should not share default values between instances" do
53
+ begin
54
+ struct = Foo.new
55
+ struct.ints << 17
56
+ Foo.new.ints.should == [1,2,2,3]
57
+ ensure
58
+ # ensure no leakage to other tests
59
+ Foo::FIELDS[4][:default] = [1,2,2,3]
60
+ end
61
+ end
62
+
63
+ it "should properly initialize boolean values" do
64
+ struct = BoolStruct.new(:yesno => false)
65
+ struct.yesno.should be_false
66
+ end
67
+
68
+ it "should have proper == semantics" do
69
+ Foo.new.should_not == Hello.new
70
+ Foo.new.should == Foo.new
71
+ Foo.new(:simple => 52).should_not == Foo.new
72
+ end
73
+
74
+ it "should print enum value names in inspect" do
75
+ StructWithSomeEnum.new(:some_enum => SomeEnum::ONE).inspect.should == "<SpecNamespace::StructWithSomeEnum some_enum:ONE (0)>"
76
+
77
+ StructWithEnumMap.new(:my_map => {SomeEnum::ONE => [SomeEnum::TWO]}).inspect.should == "<SpecNamespace::StructWithEnumMap my_map:{ONE (0): [TWO (1)]}>"
78
+ end
79
+
80
+ it "should pretty print binary fields" do
81
+ Foo2.new(:my_binary => "\001\002\003").inspect.should == "<SpecNamespace::Foo2 my_binary:010203>"
82
+ end
83
+
84
+ it "should offer field? methods" do
85
+ Foo.new.opt_string?.should be_false
86
+ Foo.new(:simple => 52).simple?.should be_true
87
+ Foo.new(:my_bool => false).my_bool?.should be_true
88
+ Foo.new(:my_bool => true).my_bool?.should be_true
89
+ end
90
+
91
+ it "should be comparable" do
92
+ s1 = StructWithSomeEnum.new(:some_enum => SomeEnum::ONE)
93
+ s2 = StructWithSomeEnum.new(:some_enum => SomeEnum::TWO)
94
+
95
+ (s1 <=> s2).should == -1
96
+ (s2 <=> s1).should == 1
97
+ (s1 <=> s1).should == 0
98
+ (s1 <=> StructWithSomeEnum.new()).should == -1
99
+ end
100
+
101
+ it "should read itself off the wire" do
102
+ struct = Foo.new
103
+ prot = BaseProtocol.new(mock("transport"))
104
+ prot.should_receive(:read_struct_begin).twice
105
+ prot.should_receive(:read_struct_end).twice
106
+ prot.should_receive(:read_field_begin).and_return(
107
+ ['complex', Types::MAP, 5], # Foo
108
+ ['words', Types::STRING, 2], # Foo
109
+ ['hello', Types::STRUCT, 3], # Foo
110
+ ['greeting', Types::STRING, 1], # Hello
111
+ [nil, Types::STOP, 0], # Hello
112
+ ['simple', Types::I32, 1], # Foo
113
+ ['ints', Types::LIST, 4], # Foo
114
+ ['shorts', Types::SET, 6], # Foo
115
+ [nil, Types::STOP, 0] # Hello
116
+ )
117
+ prot.should_receive(:read_field_end).exactly(7).times
118
+ prot.should_receive(:read_map_begin).and_return(
119
+ [Types::I32, Types::MAP, 2], # complex
120
+ [Types::STRING, Types::DOUBLE, 2], # complex/1/value
121
+ [Types::STRING, Types::DOUBLE, 1] # complex/2/value
122
+ )
123
+ prot.should_receive(:read_map_end).exactly(3).times
124
+ prot.should_receive(:read_list_begin).and_return([Types::I32, 4])
125
+ prot.should_receive(:read_list_end)
126
+ prot.should_receive(:read_set_begin).and_return([Types::I16, 2])
127
+ prot.should_receive(:read_set_end)
128
+ prot.should_receive(:read_i32).and_return(
129
+ 1, 14, # complex keys
130
+ 42, # simple
131
+ 4, 23, 4, 29 # ints
132
+ )
133
+ prot.should_receive(:read_string).and_return("pi", "e", "feigenbaum", "apple banana", "what's up?")
134
+ prot.should_receive(:read_double).and_return(Math::PI, Math::E, 4.669201609)
135
+ prot.should_receive(:read_i16).and_return(2, 3)
136
+ prot.should_not_receive(:skip)
137
+ struct.read(prot)
138
+
139
+ struct.simple.should == 42
140
+ struct.complex.should == {1 => {"pi" => Math::PI, "e" => Math::E}, 14 => {"feigenbaum" => 4.669201609}}
141
+ struct.hello.should == Hello.new(:greeting => "what's up?")
142
+ struct.words.should == "apple banana"
143
+ struct.ints.should == [4, 23, 4, 29]
144
+ struct.shorts.should == Set.new([3, 2])
145
+ end
146
+
147
+ it "should serialize false boolean fields correctly" do
148
+ b = BoolStruct.new(:yesno => false)
149
+ prot = BinaryProtocol.new(MemoryBufferTransport.new)
150
+ prot.should_receive(:write_bool).with(false)
151
+ b.write(prot)
152
+ end
153
+
154
+ it "should skip unexpected fields in structs and use default values" do
155
+ struct = Foo.new
156
+ prot = BaseProtocol.new(mock("transport"))
157
+ prot.should_receive(:read_struct_begin)
158
+ prot.should_receive(:read_struct_end)
159
+ prot.should_receive(:read_field_begin).and_return(
160
+ ['simple', Types::I32, 1],
161
+ ['complex', Types::STRUCT, 5],
162
+ ['thinz', Types::MAP, 7],
163
+ ['foobar', Types::I32, 3],
164
+ ['words', Types::STRING, 2],
165
+ [nil, Types::STOP, 0]
166
+ )
167
+ prot.should_receive(:read_field_end).exactly(5).times
168
+ prot.should_receive(:read_i32).and_return(42)
169
+ prot.should_receive(:read_string).and_return("foobar")
170
+ prot.should_receive(:skip).with(Types::STRUCT)
171
+ prot.should_receive(:skip).with(Types::MAP)
172
+ # prot.should_receive(:read_map_begin).and_return([Types::I32, Types::I32, 0])
173
+ # prot.should_receive(:read_map_end)
174
+ prot.should_receive(:skip).with(Types::I32)
175
+ struct.read(prot)
176
+
177
+ struct.simple.should == 42
178
+ struct.complex.should be_nil
179
+ struct.words.should == "foobar"
180
+ struct.hello.should == Hello.new(:greeting => 'hello, world!')
181
+ struct.ints.should == [1, 2, 2, 3]
182
+ struct.shorts.should == Set.new([5, 17, 239])
183
+ end
184
+
185
+ it "should write itself to the wire" do
186
+ prot = BaseProtocol.new(mock("transport")) #mock("Protocol")
187
+ prot.should_receive(:write_struct_begin).with("SpecNamespace::Foo")
188
+ prot.should_receive(:write_struct_begin).with("SpecNamespace::Hello")
189
+ prot.should_receive(:write_struct_end).twice
190
+ prot.should_receive(:write_field_begin).with('ints', Types::LIST, 4)
191
+ prot.should_receive(:write_i32).with(1)
192
+ prot.should_receive(:write_i32).with(2).twice
193
+ prot.should_receive(:write_i32).with(3)
194
+ prot.should_receive(:write_field_begin).with('complex', Types::MAP, 5)
195
+ prot.should_receive(:write_i32).with(5)
196
+ prot.should_receive(:write_string).with('foo')
197
+ prot.should_receive(:write_double).with(1.23)
198
+ prot.should_receive(:write_field_begin).with('shorts', Types::SET, 6)
199
+ prot.should_receive(:write_i16).with(5)
200
+ prot.should_receive(:write_i16).with(17)
201
+ prot.should_receive(:write_i16).with(239)
202
+ prot.should_receive(:write_field_stop).twice
203
+ prot.should_receive(:write_field_end).exactly(6).times
204
+ prot.should_receive(:write_field_begin).with('simple', Types::I32, 1)
205
+ prot.should_receive(:write_i32).with(53)
206
+ prot.should_receive(:write_field_begin).with('hello', Types::STRUCT, 3)
207
+ prot.should_receive(:write_field_begin).with('greeting', Types::STRING, 1)
208
+ prot.should_receive(:write_string).with('hello, world!')
209
+ prot.should_receive(:write_map_begin).with(Types::I32, Types::MAP, 1)
210
+ prot.should_receive(:write_map_begin).with(Types::STRING, Types::DOUBLE, 1)
211
+ prot.should_receive(:write_map_end).twice
212
+ prot.should_receive(:write_list_begin).with(Types::I32, 4)
213
+ prot.should_receive(:write_list_end)
214
+ prot.should_receive(:write_set_begin).with(Types::I16, 3)
215
+ prot.should_receive(:write_set_end)
216
+
217
+ struct = Foo.new
218
+ struct.words = nil
219
+ struct.complex = {5 => {"foo" => 1.23}}
220
+ struct.write(prot)
221
+ end
222
+
223
+ it "should raise an exception if presented with an unknown container" do
224
+ # yeah this is silly, but I'm going for code coverage here
225
+ struct = Foo.new
226
+ lambda { struct.send :write_container, nil, nil, {:type => "foo"} }.should raise_error(StandardError, "Not a container type: foo")
227
+ end
228
+
229
+ it "should support optional type-checking in Thrift::Struct.new" do
230
+ Thrift.type_checking = true
231
+ begin
232
+ lambda { Hello.new(:greeting => 3) }.should raise_error(TypeError, "Expected Types::STRING, received Fixnum for field greeting")
233
+ ensure
234
+ Thrift.type_checking = false
235
+ end
236
+ lambda { Hello.new(:greeting => 3) }.should_not raise_error(TypeError)
237
+ end
238
+
239
+ it "should support optional type-checking in field accessors" do
240
+ Thrift.type_checking = true
241
+ begin
242
+ hello = Hello.new
243
+ lambda { hello.greeting = 3 }.should raise_error(TypeError, "Expected Types::STRING, received Fixnum for field greeting")
244
+ ensure
245
+ Thrift.type_checking = false
246
+ end
247
+ lambda { hello.greeting = 3 }.should_not raise_error(TypeError)
248
+ end
249
+
250
+ it "should raise an exception when unknown types are given to Thrift::Struct.new" do
251
+ lambda { Hello.new(:fish => 'salmon') }.should raise_error(Exception, "Unknown key given to SpecNamespace::Hello.new: fish")
252
+ end
253
+
254
+ it "should support `raise Xception, 'message'` for Exception structs" do
255
+ begin
256
+ raise Xception, "something happened"
257
+ rescue Thrift::Exception => e
258
+ e.message.should == "something happened"
259
+ e.code.should == 1
260
+ # ensure it gets serialized properly, this is the really important part
261
+ prot = BaseProtocol.new(mock("trans"))
262
+ prot.should_receive(:write_struct_begin).with("SpecNamespace::Xception")
263
+ prot.should_receive(:write_struct_end)
264
+ prot.should_receive(:write_field_begin).with('message', Types::STRING, 1)#, "something happened")
265
+ prot.should_receive(:write_string).with("something happened")
266
+ prot.should_receive(:write_field_begin).with('code', Types::I32, 2)#, 1)
267
+ prot.should_receive(:write_i32).with(1)
268
+ prot.should_receive(:write_field_stop)
269
+ prot.should_receive(:write_field_end).twice
270
+
271
+ e.write(prot)
272
+ end
273
+ end
274
+
275
+ it "should support the regular initializer for exception structs" do
276
+ begin
277
+ raise Xception, :message => "something happened", :code => 5
278
+ rescue Thrift::Exception => e
279
+ e.message.should == "something happened"
280
+ e.code.should == 5
281
+ prot = BaseProtocol.new(mock("trans"))
282
+ prot.should_receive(:write_struct_begin).with("SpecNamespace::Xception")
283
+ prot.should_receive(:write_struct_end)
284
+ prot.should_receive(:write_field_begin).with('message', Types::STRING, 1)
285
+ prot.should_receive(:write_string).with("something happened")
286
+ prot.should_receive(:write_field_begin).with('code', Types::I32, 2)
287
+ prot.should_receive(:write_i32).with(5)
288
+ prot.should_receive(:write_field_stop)
289
+ prot.should_receive(:write_field_end).twice
290
+
291
+ e.write(prot)
292
+ end
293
+ end
294
+ end
295
+ end