slayer-thrift 0.7.0

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 (105) hide show
  1. data/CHANGELOG +1 -0
  2. data/InstalledFiles +1 -0
  3. data/Makefile +512 -0
  4. data/Makefile.am +49 -0
  5. data/Makefile.in +512 -0
  6. data/Manifest +103 -0
  7. data/README +43 -0
  8. data/Rakefile +102 -0
  9. data/benchmark/Benchmark.thrift +24 -0
  10. data/benchmark/benchmark.rb +271 -0
  11. data/benchmark/client.rb +74 -0
  12. data/benchmark/gen-rb/benchmark_constants.rb +10 -0
  13. data/benchmark/gen-rb/benchmark_service.rb +80 -0
  14. data/benchmark/gen-rb/benchmark_types.rb +9 -0
  15. data/benchmark/server.rb +82 -0
  16. data/benchmark/thin_server.rb +44 -0
  17. data/debug_proto_test/gen-rb/debug_proto_test_constants.rb +273 -0
  18. data/debug_proto_test/gen-rb/debug_proto_test_types.rb +705 -0
  19. data/debug_proto_test/gen-rb/empty_service.rb +24 -0
  20. data/debug_proto_test/gen-rb/inherited.rb +79 -0
  21. data/debug_proto_test/gen-rb/reverse_order_service.rb +82 -0
  22. data/debug_proto_test/gen-rb/service_for_exception_with_a_map.rb +81 -0
  23. data/debug_proto_test/gen-rb/srv.rb +330 -0
  24. data/ext/binary_protocol_accelerated.c +441 -0
  25. data/ext/binary_protocol_accelerated.h +20 -0
  26. data/ext/compact_protocol.c +618 -0
  27. data/ext/compact_protocol.h +20 -0
  28. data/ext/constants.h +96 -0
  29. data/ext/extconf.rb +30 -0
  30. data/ext/macros.h +41 -0
  31. data/ext/memory_buffer.c +131 -0
  32. data/ext/memory_buffer.h +20 -0
  33. data/ext/protocol.c +185 -0
  34. data/ext/protocol.h +20 -0
  35. data/ext/struct.c +716 -0
  36. data/ext/struct.h +25 -0
  37. data/ext/thrift_native.c +196 -0
  38. data/lib/thrift.rb +64 -0
  39. data/lib/thrift/client.rb +62 -0
  40. data/lib/thrift/core_ext.rb +23 -0
  41. data/lib/thrift/core_ext/fixnum.rb +29 -0
  42. data/lib/thrift/exceptions.rb +84 -0
  43. data/lib/thrift/processor.rb +57 -0
  44. data/lib/thrift/protocol/base_protocol.rb +290 -0
  45. data/lib/thrift/protocol/binary_protocol.rb +229 -0
  46. data/lib/thrift/protocol/binary_protocol_accelerated.rb +39 -0
  47. data/lib/thrift/protocol/compact_protocol.rb +426 -0
  48. data/lib/thrift/serializer/deserializer.rb +33 -0
  49. data/lib/thrift/serializer/serializer.rb +34 -0
  50. data/lib/thrift/server/base_server.rb +31 -0
  51. data/lib/thrift/server/mongrel_http_server.rb +58 -0
  52. data/lib/thrift/server/nonblocking_server.rb +305 -0
  53. data/lib/thrift/server/simple_server.rb +43 -0
  54. data/lib/thrift/server/thread_pool_server.rb +75 -0
  55. data/lib/thrift/server/threaded_server.rb +47 -0
  56. data/lib/thrift/struct.rb +237 -0
  57. data/lib/thrift/struct_union.rb +192 -0
  58. data/lib/thrift/thrift_native.rb +24 -0
  59. data/lib/thrift/transport/base_server_transport.rb +37 -0
  60. data/lib/thrift/transport/base_transport.rb +107 -0
  61. data/lib/thrift/transport/buffered_transport.rb +108 -0
  62. data/lib/thrift/transport/framed_transport.rb +116 -0
  63. data/lib/thrift/transport/http_client_transport.rb +51 -0
  64. data/lib/thrift/transport/io_stream_transport.rb +39 -0
  65. data/lib/thrift/transport/memory_buffer_transport.rb +125 -0
  66. data/lib/thrift/transport/server_socket.rb +63 -0
  67. data/lib/thrift/transport/socket.rb +137 -0
  68. data/lib/thrift/transport/unix_server_socket.rb +60 -0
  69. data/lib/thrift/transport/unix_socket.rb +40 -0
  70. data/lib/thrift/types.rb +101 -0
  71. data/lib/thrift/union.rb +179 -0
  72. data/script/proto_benchmark.rb +121 -0
  73. data/script/read_struct.rb +43 -0
  74. data/script/write_struct.rb +30 -0
  75. data/setup.rb +1585 -0
  76. data/slayer-thrift.gemspec +30 -0
  77. data/spec/ThriftSpec.thrift +132 -0
  78. data/spec/base_protocol_spec.rb +160 -0
  79. data/spec/base_transport_spec.rb +351 -0
  80. data/spec/binary_protocol_accelerated_spec.rb +46 -0
  81. data/spec/binary_protocol_spec.rb +61 -0
  82. data/spec/binary_protocol_spec_shared.rb +375 -0
  83. data/spec/client_spec.rb +100 -0
  84. data/spec/compact_protocol_spec.rb +133 -0
  85. data/spec/exception_spec.rb +142 -0
  86. data/spec/gen-rb/nonblocking_service.rb +272 -0
  87. data/spec/gen-rb/thrift_spec_constants.rb +10 -0
  88. data/spec/gen-rb/thrift_spec_types.rb +345 -0
  89. data/spec/http_client_spec.rb +64 -0
  90. data/spec/mongrel_http_server_spec.rb +117 -0
  91. data/spec/nonblocking_server_spec.rb +265 -0
  92. data/spec/processor_spec.rb +83 -0
  93. data/spec/serializer_spec.rb +69 -0
  94. data/spec/server_socket_spec.rb +80 -0
  95. data/spec/server_spec.rb +160 -0
  96. data/spec/socket_spec.rb +61 -0
  97. data/spec/socket_spec_shared.rb +104 -0
  98. data/spec/spec_helper.rb +58 -0
  99. data/spec/struct_spec.rb +295 -0
  100. data/spec/types_spec.rb +116 -0
  101. data/spec/union_spec.rb +193 -0
  102. data/spec/unix_socket_spec.rb +108 -0
  103. data/thrift.gemspec +30 -0
  104. data/tmp/thrift-0.7.0.gem +0 -0
  105. metadata +207 -0
@@ -0,0 +1,160 @@
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.dirname(__FILE__) + '/spec_helper'
21
+
22
+ class ThriftServerSpec < Spec::ExampleGroup
23
+ include Thrift
24
+
25
+ describe BaseServer do
26
+ it "should default to BaseTransportFactory and BinaryProtocolFactory when not specified" do
27
+ server = BaseServer.new(mock("Processor"), mock("BaseServerTransport"))
28
+ server.instance_variable_get(:'@transport_factory').should be_an_instance_of(BaseTransportFactory)
29
+ server.instance_variable_get(:'@protocol_factory').should be_an_instance_of(BinaryProtocolFactory)
30
+ end
31
+
32
+ # serve is a noop, so can't test that
33
+ end
34
+
35
+ shared_examples_for "servers" do
36
+ before(:each) do
37
+ @processor = mock("Processor")
38
+ @serverTrans = mock("ServerTransport")
39
+ @trans = mock("BaseTransport")
40
+ @prot = mock("BaseProtocol")
41
+ @client = mock("Client")
42
+ @server = server_type.new(@processor, @serverTrans, @trans, @prot)
43
+ end
44
+ end
45
+
46
+ describe SimpleServer do
47
+ it_should_behave_like "servers"
48
+
49
+ def server_type
50
+ SimpleServer
51
+ end
52
+
53
+ it "should serve in the main thread" do
54
+ @serverTrans.should_receive(:listen).ordered
55
+ @serverTrans.should_receive(:accept).exactly(3).times.and_return(@client)
56
+ @trans.should_receive(:get_transport).exactly(3).times.with(@client).and_return(@trans)
57
+ @prot.should_receive(:get_protocol).exactly(3).times.with(@trans).and_return(@prot)
58
+ x = 0
59
+ @processor.should_receive(:process).exactly(3).times.with(@prot, @prot).and_return do
60
+ case (x += 1)
61
+ when 1 then raise Thrift::TransportException
62
+ when 2 then raise Thrift::ProtocolException
63
+ when 3 then throw :stop
64
+ end
65
+ end
66
+ @trans.should_receive(:close).exactly(3).times
67
+ @serverTrans.should_receive(:close).ordered
68
+ lambda { @server.serve }.should throw_symbol(:stop)
69
+ end
70
+ end
71
+
72
+ describe ThreadedServer do
73
+ it_should_behave_like "servers"
74
+
75
+ def server_type
76
+ ThreadedServer
77
+ end
78
+
79
+ it "should serve using threads" do
80
+ @serverTrans.should_receive(:listen).ordered
81
+ @serverTrans.should_receive(:accept).exactly(3).times.and_return(@client)
82
+ @trans.should_receive(:get_transport).exactly(3).times.with(@client).and_return(@trans)
83
+ @prot.should_receive(:get_protocol).exactly(3).times.with(@trans).and_return(@prot)
84
+ Thread.should_receive(:new).with(@prot, @trans).exactly(3).times.and_yield(@prot, @trans)
85
+ x = 0
86
+ @processor.should_receive(:process).exactly(3).times.with(@prot, @prot).and_return do
87
+ case (x += 1)
88
+ when 1 then raise Thrift::TransportException
89
+ when 2 then raise Thrift::ProtocolException
90
+ when 3 then throw :stop
91
+ end
92
+ end
93
+ @trans.should_receive(:close).exactly(3).times
94
+ @serverTrans.should_receive(:close).ordered
95
+ lambda { @server.serve }.should throw_symbol(:stop)
96
+ end
97
+ end
98
+
99
+ describe ThreadPoolServer do
100
+ it_should_behave_like "servers"
101
+
102
+ def server_type
103
+ # put this stuff here so it runs before the server is created
104
+ @threadQ = mock("SizedQueue")
105
+ SizedQueue.should_receive(:new).with(20).and_return(@threadQ)
106
+ @excQ = mock("Queue")
107
+ Queue.should_receive(:new).and_return(@excQ)
108
+ ThreadPoolServer
109
+ end
110
+
111
+ it "should set up the queues" do
112
+ @server.instance_variable_get(:'@thread_q').should be(@threadQ)
113
+ @server.instance_variable_get(:'@exception_q').should be(@excQ)
114
+ end
115
+
116
+ it "should serve inside a thread" do
117
+ Thread.should_receive(:new).and_return do |block|
118
+ @server.should_receive(:serve)
119
+ block.call
120
+ @server.rspec_verify
121
+ end
122
+ @excQ.should_receive(:pop).and_throw(:popped)
123
+ lambda { @server.rescuable_serve }.should throw_symbol(:popped)
124
+ end
125
+
126
+ it "should avoid running the server twice when retrying rescuable_serve" do
127
+ Thread.should_receive(:new).and_return do |block|
128
+ @server.should_receive(:serve)
129
+ block.call
130
+ @server.rspec_verify
131
+ end
132
+ @excQ.should_receive(:pop).twice.and_throw(:popped)
133
+ lambda { @server.rescuable_serve }.should throw_symbol(:popped)
134
+ lambda { @server.rescuable_serve }.should throw_symbol(:popped)
135
+ end
136
+
137
+ it "should serve using a thread pool" do
138
+ @serverTrans.should_receive(:listen).ordered
139
+ @threadQ.should_receive(:push).with(:token)
140
+ @threadQ.should_receive(:pop)
141
+ Thread.should_receive(:new).and_yield
142
+ @serverTrans.should_receive(:accept).exactly(3).times.and_return(@client)
143
+ @trans.should_receive(:get_transport).exactly(3).times.and_return(@trans)
144
+ @prot.should_receive(:get_protocol).exactly(3).times.and_return(@prot)
145
+ x = 0
146
+ error = RuntimeError.new("Stopped")
147
+ @processor.should_receive(:process).exactly(3).times.with(@prot, @prot).and_return do
148
+ case (x += 1)
149
+ when 1 then raise Thrift::TransportException
150
+ when 2 then raise Thrift::ProtocolException
151
+ when 3 then raise error
152
+ end
153
+ end
154
+ @trans.should_receive(:close).exactly(3).times
155
+ @excQ.should_receive(:push).with(error).and_throw(:stop)
156
+ @serverTrans.should_receive(:close)
157
+ lambda { @server.serve }.should throw_symbol(:stop)
158
+ end
159
+ end
160
+ 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.dirname(__FILE__) + '/spec_helper'
21
+ require 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.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[.. debug_proto_test 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.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