OLD | NEW |
(Empty) | |
| 1 # Copyright 2015, Google Inc. |
| 2 # All rights reserved. |
| 3 # |
| 4 # Redistribution and use in source and binary forms, with or without |
| 5 # modification, are permitted provided that the following conditions are |
| 6 # met: |
| 7 # |
| 8 # * Redistributions of source code must retain the above copyright |
| 9 # notice, this list of conditions and the following disclaimer. |
| 10 # * Redistributions in binary form must reproduce the above |
| 11 # copyright notice, this list of conditions and the following disclaimer |
| 12 # in the documentation and/or other materials provided with the |
| 13 # distribution. |
| 14 # * Neither the name of Google Inc. nor the names of its |
| 15 # contributors may be used to endorse or promote products derived from |
| 16 # this software without specific prior written permission. |
| 17 # |
| 18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 |
| 30 require 'grpc' |
| 31 |
| 32 include GRPC::Core::StatusCodes |
| 33 |
| 34 describe GRPC::ActiveCall do |
| 35 ActiveCall = GRPC::ActiveCall |
| 36 Call = GRPC::Core::Call |
| 37 CallOps = GRPC::Core::CallOps |
| 38 WriteFlags = GRPC::Core::WriteFlags |
| 39 |
| 40 before(:each) do |
| 41 @pass_through = proc { |x| x } |
| 42 @server_tag = Object.new |
| 43 @tag = Object.new |
| 44 |
| 45 @client_queue = GRPC::Core::CompletionQueue.new |
| 46 @server_queue = GRPC::Core::CompletionQueue.new |
| 47 host = '0.0.0.0:0' |
| 48 @server = GRPC::Core::Server.new(@server_queue, nil) |
| 49 server_port = @server.add_http2_port(host, :this_port_is_insecure) |
| 50 @server.start |
| 51 @ch = GRPC::Core::Channel.new("0.0.0.0:#{server_port}", nil, |
| 52 :this_channel_is_insecure) |
| 53 end |
| 54 |
| 55 after(:each) do |
| 56 @server.close(@server_queue, deadline) |
| 57 end |
| 58 |
| 59 describe 'restricted view methods' do |
| 60 before(:each) do |
| 61 call = make_test_call |
| 62 md_tag = ActiveCall.client_invoke(call, @client_queue) |
| 63 @client_call = ActiveCall.new(call, @client_queue, @pass_through, |
| 64 @pass_through, deadline, |
| 65 metadata_tag: md_tag) |
| 66 end |
| 67 |
| 68 describe '#multi_req_view' do |
| 69 it 'exposes a fixed subset of the ActiveCall methods' do |
| 70 want = %w(cancelled, deadline, each_remote_read, metadata, shutdown) |
| 71 v = @client_call.multi_req_view |
| 72 want.each do |w| |
| 73 expect(v.methods.include?(w)) |
| 74 end |
| 75 end |
| 76 end |
| 77 |
| 78 describe '#single_req_view' do |
| 79 it 'exposes a fixed subset of the ActiveCall methods' do |
| 80 want = %w(cancelled, deadline, metadata, shutdown) |
| 81 v = @client_call.single_req_view |
| 82 want.each do |w| |
| 83 expect(v.methods.include?(w)) |
| 84 end |
| 85 end |
| 86 end |
| 87 end |
| 88 |
| 89 describe '#remote_send' do |
| 90 it 'allows a client to send a payload to the server' do |
| 91 call = make_test_call |
| 92 md_tag = ActiveCall.client_invoke(call, @client_queue) |
| 93 @client_call = ActiveCall.new(call, @client_queue, @pass_through, |
| 94 @pass_through, deadline, |
| 95 metadata_tag: md_tag) |
| 96 msg = 'message is a string' |
| 97 @client_call.remote_send(msg) |
| 98 |
| 99 # check that server rpc new was received |
| 100 recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline) |
| 101 expect(recvd_rpc).to_not eq nil |
| 102 recvd_call = recvd_rpc.call |
| 103 |
| 104 # Accept the call, and verify that the server reads the response ok. |
| 105 server_ops = { |
| 106 CallOps::SEND_INITIAL_METADATA => {} |
| 107 } |
| 108 recvd_call.run_batch(@server_queue, @server_tag, deadline, server_ops) |
| 109 server_call = ActiveCall.new(recvd_call, @server_queue, @pass_through, |
| 110 @pass_through, deadline) |
| 111 expect(server_call.remote_read).to eq(msg) |
| 112 end |
| 113 |
| 114 it 'marshals the payload using the marshal func' do |
| 115 call = make_test_call |
| 116 ActiveCall.client_invoke(call, @client_queue) |
| 117 marshal = proc { |x| 'marshalled:' + x } |
| 118 client_call = ActiveCall.new(call, @client_queue, marshal, |
| 119 @pass_through, deadline) |
| 120 msg = 'message is a string' |
| 121 client_call.remote_send(msg) |
| 122 |
| 123 # confirm that the message was marshalled |
| 124 recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline) |
| 125 recvd_call = recvd_rpc.call |
| 126 server_ops = { |
| 127 CallOps::SEND_INITIAL_METADATA => nil |
| 128 } |
| 129 recvd_call.run_batch(@server_queue, @server_tag, deadline, server_ops) |
| 130 server_call = ActiveCall.new(recvd_call, @server_queue, @pass_through, |
| 131 @pass_through, deadline) |
| 132 expect(server_call.remote_read).to eq('marshalled:' + msg) |
| 133 end |
| 134 |
| 135 TEST_WRITE_FLAGS = [WriteFlags::BUFFER_HINT, WriteFlags::NO_COMPRESS] |
| 136 TEST_WRITE_FLAGS.each do |f| |
| 137 it "successfully makes calls with write_flag set to #{f}" do |
| 138 call = make_test_call |
| 139 ActiveCall.client_invoke(call, @client_queue) |
| 140 marshal = proc { |x| 'marshalled:' + x } |
| 141 client_call = ActiveCall.new(call, @client_queue, marshal, |
| 142 @pass_through, deadline) |
| 143 msg = 'message is a string' |
| 144 client_call.write_flag = f |
| 145 client_call.remote_send(msg) |
| 146 |
| 147 # confirm that the message was marshalled |
| 148 recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline) |
| 149 recvd_call = recvd_rpc.call |
| 150 server_ops = { |
| 151 CallOps::SEND_INITIAL_METADATA => nil |
| 152 } |
| 153 recvd_call.run_batch(@server_queue, @server_tag, deadline, server_ops) |
| 154 server_call = ActiveCall.new(recvd_call, @server_queue, @pass_through, |
| 155 @pass_through, deadline) |
| 156 expect(server_call.remote_read).to eq('marshalled:' + msg) |
| 157 end |
| 158 end |
| 159 end |
| 160 |
| 161 describe '#client_invoke' do |
| 162 it 'sends keywords as metadata to the server when the are present' do |
| 163 call = make_test_call |
| 164 ActiveCall.client_invoke(call, @client_queue, k1: 'v1', k2: 'v2') |
| 165 recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline) |
| 166 recvd_call = recvd_rpc.call |
| 167 expect(recvd_call).to_not be_nil |
| 168 expect(recvd_rpc.metadata).to_not be_nil |
| 169 expect(recvd_rpc.metadata['k1']).to eq('v1') |
| 170 expect(recvd_rpc.metadata['k2']).to eq('v2') |
| 171 end |
| 172 end |
| 173 |
| 174 describe '#remote_read' do |
| 175 it 'reads the response sent by a server' do |
| 176 call = make_test_call |
| 177 md_tag = ActiveCall.client_invoke(call, @client_queue) |
| 178 client_call = ActiveCall.new(call, @client_queue, @pass_through, |
| 179 @pass_through, deadline, |
| 180 metadata_tag: md_tag) |
| 181 msg = 'message is a string' |
| 182 client_call.remote_send(msg) |
| 183 server_call = expect_server_to_receive(msg) |
| 184 server_call.remote_send('server_response') |
| 185 expect(client_call.remote_read).to eq('server_response') |
| 186 end |
| 187 |
| 188 it 'saves no metadata when the server adds no metadata' do |
| 189 call = make_test_call |
| 190 md_tag = ActiveCall.client_invoke(call, @client_queue) |
| 191 client_call = ActiveCall.new(call, @client_queue, @pass_through, |
| 192 @pass_through, deadline, |
| 193 metadata_tag: md_tag) |
| 194 msg = 'message is a string' |
| 195 client_call.remote_send(msg) |
| 196 server_call = expect_server_to_receive(msg) |
| 197 server_call.remote_send('ignore me') |
| 198 expect(client_call.metadata).to be_nil |
| 199 client_call.remote_read |
| 200 expect(client_call.metadata).to eq({}) |
| 201 end |
| 202 |
| 203 it 'saves metadata add by the server' do |
| 204 call = make_test_call |
| 205 md_tag = ActiveCall.client_invoke(call, @client_queue) |
| 206 client_call = ActiveCall.new(call, @client_queue, @pass_through, |
| 207 @pass_through, deadline, |
| 208 metadata_tag: md_tag) |
| 209 msg = 'message is a string' |
| 210 client_call.remote_send(msg) |
| 211 server_call = expect_server_to_receive(msg, k1: 'v1', k2: 'v2') |
| 212 server_call.remote_send('ignore me') |
| 213 expect(client_call.metadata).to be_nil |
| 214 client_call.remote_read |
| 215 expected = { 'k1' => 'v1', 'k2' => 'v2' } |
| 216 expect(client_call.metadata).to eq(expected) |
| 217 end |
| 218 |
| 219 it 'get a nil msg before a status when an OK status is sent' do |
| 220 call = make_test_call |
| 221 md_tag = ActiveCall.client_invoke(call, @client_queue) |
| 222 client_call = ActiveCall.new(call, @client_queue, @pass_through, |
| 223 @pass_through, deadline, |
| 224 metadata_tag: md_tag) |
| 225 msg = 'message is a string' |
| 226 client_call.remote_send(msg) |
| 227 client_call.writes_done(false) |
| 228 server_call = expect_server_to_receive(msg) |
| 229 server_call.remote_send('server_response') |
| 230 server_call.send_status(OK, 'OK') |
| 231 expect(client_call.remote_read).to eq('server_response') |
| 232 res = client_call.remote_read |
| 233 expect(res).to be_nil |
| 234 end |
| 235 |
| 236 it 'unmarshals the response using the unmarshal func' do |
| 237 call = make_test_call |
| 238 md_tag = ActiveCall.client_invoke(call, @client_queue) |
| 239 unmarshal = proc { |x| 'unmarshalled:' + x } |
| 240 client_call = ActiveCall.new(call, @client_queue, @pass_through, |
| 241 unmarshal, deadline, |
| 242 metadata_tag: md_tag) |
| 243 |
| 244 # confirm the client receives the unmarshalled message |
| 245 msg = 'message is a string' |
| 246 client_call.remote_send(msg) |
| 247 server_call = expect_server_to_receive(msg) |
| 248 server_call.remote_send('server_response') |
| 249 expect(client_call.remote_read).to eq('unmarshalled:server_response') |
| 250 end |
| 251 end |
| 252 |
| 253 describe '#each_remote_read' do |
| 254 it 'creates an Enumerator' do |
| 255 call = make_test_call |
| 256 client_call = ActiveCall.new(call, @client_queue, @pass_through, |
| 257 @pass_through, deadline) |
| 258 expect(client_call.each_remote_read).to be_a(Enumerator) |
| 259 end |
| 260 |
| 261 it 'the returns an enumerator that can read n responses' do |
| 262 call = make_test_call |
| 263 md_tag = ActiveCall.client_invoke(call, @client_queue) |
| 264 client_call = ActiveCall.new(call, @client_queue, @pass_through, |
| 265 @pass_through, deadline, |
| 266 metadata_tag: md_tag) |
| 267 msg = 'message is a string' |
| 268 reply = 'server_response' |
| 269 client_call.remote_send(msg) |
| 270 server_call = expect_server_to_receive(msg) |
| 271 e = client_call.each_remote_read |
| 272 n = 3 # arbitrary value > 1 |
| 273 n.times do |
| 274 server_call.remote_send(reply) |
| 275 expect(e.next).to eq(reply) |
| 276 end |
| 277 end |
| 278 |
| 279 it 'the returns an enumerator that stops after an OK Status' do |
| 280 call = make_test_call |
| 281 md_tag = ActiveCall.client_invoke(call, @client_queue) |
| 282 client_call = ActiveCall.new(call, @client_queue, @pass_through, |
| 283 @pass_through, deadline, |
| 284 metadata_tag: md_tag) |
| 285 msg = 'message is a string' |
| 286 reply = 'server_response' |
| 287 client_call.remote_send(msg) |
| 288 client_call.writes_done(false) |
| 289 server_call = expect_server_to_receive(msg) |
| 290 e = client_call.each_remote_read |
| 291 n = 3 # arbitrary value > 1 |
| 292 n.times do |
| 293 server_call.remote_send(reply) |
| 294 expect(e.next).to eq(reply) |
| 295 end |
| 296 server_call.send_status(OK, 'OK') |
| 297 expect { e.next }.to raise_error(StopIteration) |
| 298 end |
| 299 end |
| 300 |
| 301 describe '#writes_done' do |
| 302 it 'finishes ok if the server sends a status response' do |
| 303 call = make_test_call |
| 304 md_tag = ActiveCall.client_invoke(call, @client_queue) |
| 305 client_call = ActiveCall.new(call, @client_queue, @pass_through, |
| 306 @pass_through, deadline, |
| 307 metadata_tag: md_tag) |
| 308 msg = 'message is a string' |
| 309 client_call.remote_send(msg) |
| 310 expect { client_call.writes_done(false) }.to_not raise_error |
| 311 server_call = expect_server_to_receive(msg) |
| 312 server_call.remote_send('server_response') |
| 313 expect(client_call.remote_read).to eq('server_response') |
| 314 server_call.send_status(OK, 'status code is OK') |
| 315 expect { client_call.finished }.to_not raise_error |
| 316 end |
| 317 |
| 318 it 'finishes ok if the server sends an early status response' do |
| 319 call = make_test_call |
| 320 md_tag = ActiveCall.client_invoke(call, @client_queue) |
| 321 client_call = ActiveCall.new(call, @client_queue, @pass_through, |
| 322 @pass_through, deadline, |
| 323 metadata_tag: md_tag) |
| 324 msg = 'message is a string' |
| 325 client_call.remote_send(msg) |
| 326 server_call = expect_server_to_receive(msg) |
| 327 server_call.remote_send('server_response') |
| 328 server_call.send_status(OK, 'status code is OK') |
| 329 expect(client_call.remote_read).to eq('server_response') |
| 330 expect { client_call.writes_done(false) }.to_not raise_error |
| 331 expect { client_call.finished }.to_not raise_error |
| 332 end |
| 333 |
| 334 it 'finishes ok if writes_done is true' do |
| 335 call = make_test_call |
| 336 md_tag = ActiveCall.client_invoke(call, @client_queue) |
| 337 client_call = ActiveCall.new(call, @client_queue, @pass_through, |
| 338 @pass_through, deadline, |
| 339 metadata_tag: md_tag) |
| 340 msg = 'message is a string' |
| 341 client_call.remote_send(msg) |
| 342 server_call = expect_server_to_receive(msg) |
| 343 server_call.remote_send('server_response') |
| 344 server_call.send_status(OK, 'status code is OK') |
| 345 expect(client_call.remote_read).to eq('server_response') |
| 346 expect { client_call.writes_done(true) }.to_not raise_error |
| 347 end |
| 348 end |
| 349 |
| 350 def expect_server_to_receive(sent_text, **kw) |
| 351 c = expect_server_to_be_invoked(**kw) |
| 352 expect(c.remote_read).to eq(sent_text) |
| 353 c |
| 354 end |
| 355 |
| 356 def expect_server_to_be_invoked(**kw) |
| 357 recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline) |
| 358 expect(recvd_rpc).to_not eq nil |
| 359 recvd_call = recvd_rpc.call |
| 360 recvd_call.run_batch(@server_queue, @server_tag, deadline, |
| 361 CallOps::SEND_INITIAL_METADATA => kw) |
| 362 ActiveCall.new(recvd_call, @server_queue, @pass_through, |
| 363 @pass_through, deadline) |
| 364 end |
| 365 |
| 366 def make_test_call |
| 367 @ch.create_call(@client_queue, nil, nil, '/method', nil, deadline) |
| 368 end |
| 369 |
| 370 def deadline |
| 371 Time.now + 2 # in 2 seconds; arbitrary |
| 372 end |
| 373 end |
OLD | NEW |