1 #region Copyright notice and license 2 3 // Copyright 2015 gRPC authors. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 #endregion 18 19 using System; 20 using System.Collections.Generic; 21 using System.IO; 22 using System.Runtime.InteropServices; 23 using System.Threading.Tasks; 24 25 using Grpc.Core.Internal; 26 using NUnit.Framework; 27 28 namespace Grpc.Core.Internal.Tests 29 { 30 /// <summary> 31 /// Uses fake native call to test interaction of <c>AsyncCallServer</c> wrapping code with C core in different situations. 32 /// </summary> 33 public class AsyncCallServerTest 34 { 35 Server server; 36 FakeNativeCall fakeCall; 37 AsyncCallServer<string, string> asyncCallServer; 38 FakeBufferReaderManager fakeBufferReaderManager; 39 40 [SetUp] Init()41 public void Init() 42 { 43 // Create a fake server just so we have an instance to refer to. 44 // The server won't actually be used at all. 45 server = new Server() 46 { 47 Ports = { { "localhost", 0, ServerCredentials.Insecure } } 48 }; 49 server.Start(); 50 51 fakeCall = new FakeNativeCall(); 52 asyncCallServer = new AsyncCallServer<string, string>( 53 Marshallers.StringMarshaller.ContextualSerializer, Marshallers.StringMarshaller.ContextualDeserializer, 54 server); 55 asyncCallServer.InitializeForTesting(fakeCall); 56 fakeBufferReaderManager = new FakeBufferReaderManager(); 57 } 58 59 [TearDown] Cleanup()60 public void Cleanup() 61 { 62 fakeBufferReaderManager.Dispose(); 63 server.ShutdownAsync().Wait(); 64 } 65 66 [Test] CancelNotificationAfterStartDisposes()67 public void CancelNotificationAfterStartDisposes() 68 { 69 var finishedTask = asyncCallServer.ServerSideCallAsync(); 70 fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); 71 AssertFinished(asyncCallServer, fakeCall, finishedTask); 72 } 73 74 [Test] CancelNotificationAfterStartDisposesAfterPendingReadFinishes()75 public void CancelNotificationAfterStartDisposesAfterPendingReadFinishes() 76 { 77 var finishedTask = asyncCallServer.ServerSideCallAsync(); 78 var requestStream = new ServerRequestStream<string, string>(asyncCallServer); 79 80 var moveNextTask = requestStream.MoveNext(); 81 82 fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); 83 fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, CreateNullResponse()); 84 Assert.IsFalse(moveNextTask.Result); 85 86 AssertFinished(asyncCallServer, fakeCall, finishedTask); 87 } 88 89 [Test] ReadAfterCancelNotificationCanSucceed()90 public void ReadAfterCancelNotificationCanSucceed() 91 { 92 var finishedTask = asyncCallServer.ServerSideCallAsync(); 93 var requestStream = new ServerRequestStream<string, string>(asyncCallServer); 94 95 fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); 96 97 // Check that starting a read after cancel notification has been processed is legal. 98 var moveNextTask = requestStream.MoveNext(); 99 Assert.IsFalse(moveNextTask.Result); 100 101 AssertFinished(asyncCallServer, fakeCall, finishedTask); 102 } 103 104 [Test] ReadCompletionFailureClosesRequestStream()105 public void ReadCompletionFailureClosesRequestStream() 106 { 107 var finishedTask = asyncCallServer.ServerSideCallAsync(); 108 var requestStream = new ServerRequestStream<string, string>(asyncCallServer); 109 110 // if a read completion's success==false, the request stream will silently finish 111 // and we rely on C core cancelling the call. 112 var moveNextTask = requestStream.MoveNext(); 113 fakeCall.ReceivedMessageCallback.OnReceivedMessage(false, CreateNullResponse()); 114 Assert.IsFalse(moveNextTask.Result); 115 116 fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); 117 AssertFinished(asyncCallServer, fakeCall, finishedTask); 118 } 119 120 [Test] WriteAfterCancelNotificationFails()121 public void WriteAfterCancelNotificationFails() 122 { 123 var finishedTask = asyncCallServer.ServerSideCallAsync(); 124 var responseStream = new ServerResponseStream<string, string>(asyncCallServer); 125 126 fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); 127 128 // TODO(jtattermusch): should we throw a different exception type instead? 129 Assert.Throws(typeof(InvalidOperationException), () => responseStream.WriteAsync("request1")); 130 AssertFinished(asyncCallServer, fakeCall, finishedTask); 131 } 132 133 [Test] WriteCompletionFailureThrows()134 public void WriteCompletionFailureThrows() 135 { 136 var finishedTask = asyncCallServer.ServerSideCallAsync(); 137 var responseStream = new ServerResponseStream<string, string>(asyncCallServer); 138 139 var writeTask = responseStream.WriteAsync("request1"); 140 fakeCall.SendCompletionCallback.OnSendCompletion(false); 141 Assert.ThrowsAsync(typeof(IOException), async () => await writeTask); 142 143 fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); 144 AssertFinished(asyncCallServer, fakeCall, finishedTask); 145 } 146 147 [Test] WriteAndWriteStatusCanRunConcurrently()148 public void WriteAndWriteStatusCanRunConcurrently() 149 { 150 var finishedTask = asyncCallServer.ServerSideCallAsync(); 151 var responseStream = new ServerResponseStream<string, string>(asyncCallServer); 152 153 var writeTask = responseStream.WriteAsync("request1"); 154 var writeStatusTask = asyncCallServer.SendStatusFromServerAsync(Status.DefaultSuccess, new Metadata(), null); 155 156 fakeCall.SendCompletionCallback.OnSendCompletion(true); 157 fakeCall.SendStatusFromServerCallback.OnSendStatusFromServerCompletion(true); 158 159 Assert.DoesNotThrowAsync(async () => await writeTask); 160 Assert.DoesNotThrowAsync(async () => await writeStatusTask); 161 162 fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); 163 164 AssertFinished(asyncCallServer, fakeCall, finishedTask); 165 } 166 167 [Test] WriteAfterWriteStatusThrowsInvalidOperationException()168 public void WriteAfterWriteStatusThrowsInvalidOperationException() 169 { 170 var finishedTask = asyncCallServer.ServerSideCallAsync(); 171 var responseStream = new ServerResponseStream<string, string>(asyncCallServer); 172 173 asyncCallServer.SendStatusFromServerAsync(Status.DefaultSuccess, new Metadata(), null); 174 Assert.ThrowsAsync(typeof(InvalidOperationException), async () => await responseStream.WriteAsync("request1")); 175 176 fakeCall.SendStatusFromServerCallback.OnSendStatusFromServerCompletion(true); 177 fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); 178 179 AssertFinished(asyncCallServer, fakeCall, finishedTask); 180 } 181 AssertFinished(AsyncCallServer<string, string> asyncCallServer, FakeNativeCall fakeCall, Task finishedTask)182 static void AssertFinished(AsyncCallServer<string, string> asyncCallServer, FakeNativeCall fakeCall, Task finishedTask) 183 { 184 Assert.IsTrue(fakeCall.IsDisposed); 185 Assert.IsTrue(finishedTask.IsCompleted); 186 Assert.DoesNotThrow(() => finishedTask.Wait()); 187 } 188 CreateNullResponse()189 IBufferReader CreateNullResponse() 190 { 191 return fakeBufferReaderManager.CreateNullPayloadBufferReader(); 192 } 193 } 194 } 195