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 39 [SetUp] Init()40 public void Init() 41 { 42 // Create a fake server just so we have an instance to refer to. 43 // The server won't actually be used at all. 44 server = new Server() 45 { 46 Ports = { { "localhost", 0, ServerCredentials.Insecure } } 47 }; 48 server.Start(); 49 50 fakeCall = new FakeNativeCall(); 51 asyncCallServer = new AsyncCallServer<string, string>( 52 Marshallers.StringMarshaller.Serializer, Marshallers.StringMarshaller.Deserializer, 53 server); 54 asyncCallServer.InitializeForTesting(fakeCall); 55 } 56 57 [TearDown] Cleanup()58 public void Cleanup() 59 { 60 server.ShutdownAsync().Wait(); 61 } 62 63 [Test] CancelNotificationAfterStartDisposes()64 public void CancelNotificationAfterStartDisposes() 65 { 66 var finishedTask = asyncCallServer.ServerSideCallAsync(); 67 fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); 68 AssertFinished(asyncCallServer, fakeCall, finishedTask); 69 } 70 71 [Test] CancelNotificationAfterStartDisposesAfterPendingReadFinishes()72 public void CancelNotificationAfterStartDisposesAfterPendingReadFinishes() 73 { 74 var finishedTask = asyncCallServer.ServerSideCallAsync(); 75 var requestStream = new ServerRequestStream<string, string>(asyncCallServer); 76 77 var moveNextTask = requestStream.MoveNext(); 78 79 fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); 80 fakeCall.ReceivedMessageCallback.OnReceivedMessage(true, null); 81 Assert.IsFalse(moveNextTask.Result); 82 83 AssertFinished(asyncCallServer, fakeCall, finishedTask); 84 } 85 86 [Test] ReadAfterCancelNotificationCanSucceed()87 public void ReadAfterCancelNotificationCanSucceed() 88 { 89 var finishedTask = asyncCallServer.ServerSideCallAsync(); 90 var requestStream = new ServerRequestStream<string, string>(asyncCallServer); 91 92 fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); 93 94 // Check that starting a read after cancel notification has been processed is legal. 95 var moveNextTask = requestStream.MoveNext(); 96 Assert.IsFalse(moveNextTask.Result); 97 98 AssertFinished(asyncCallServer, fakeCall, finishedTask); 99 } 100 101 [Test] ReadCompletionFailureClosesRequestStream()102 public void ReadCompletionFailureClosesRequestStream() 103 { 104 var finishedTask = asyncCallServer.ServerSideCallAsync(); 105 var requestStream = new ServerRequestStream<string, string>(asyncCallServer); 106 107 // if a read completion's success==false, the request stream will silently finish 108 // and we rely on C core cancelling the call. 109 var moveNextTask = requestStream.MoveNext(); 110 fakeCall.ReceivedMessageCallback.OnReceivedMessage(false, null); 111 Assert.IsFalse(moveNextTask.Result); 112 113 fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); 114 AssertFinished(asyncCallServer, fakeCall, finishedTask); 115 } 116 117 [Test] WriteAfterCancelNotificationFails()118 public void WriteAfterCancelNotificationFails() 119 { 120 var finishedTask = asyncCallServer.ServerSideCallAsync(); 121 var responseStream = new ServerResponseStream<string, string>(asyncCallServer); 122 123 fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); 124 125 // TODO(jtattermusch): should we throw a different exception type instead? 126 Assert.Throws(typeof(InvalidOperationException), () => responseStream.WriteAsync("request1")); 127 AssertFinished(asyncCallServer, fakeCall, finishedTask); 128 } 129 130 [Test] WriteCompletionFailureThrows()131 public void WriteCompletionFailureThrows() 132 { 133 var finishedTask = asyncCallServer.ServerSideCallAsync(); 134 var responseStream = new ServerResponseStream<string, string>(asyncCallServer); 135 136 var writeTask = responseStream.WriteAsync("request1"); 137 fakeCall.SendCompletionCallback.OnSendCompletion(false); 138 Assert.ThrowsAsync(typeof(IOException), async () => await writeTask); 139 140 fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); 141 AssertFinished(asyncCallServer, fakeCall, finishedTask); 142 } 143 144 [Test] WriteAndWriteStatusCanRunConcurrently()145 public void WriteAndWriteStatusCanRunConcurrently() 146 { 147 var finishedTask = asyncCallServer.ServerSideCallAsync(); 148 var responseStream = new ServerResponseStream<string, string>(asyncCallServer); 149 150 var writeTask = responseStream.WriteAsync("request1"); 151 var writeStatusTask = asyncCallServer.SendStatusFromServerAsync(Status.DefaultSuccess, new Metadata(), null); 152 153 fakeCall.SendCompletionCallback.OnSendCompletion(true); 154 fakeCall.SendStatusFromServerCallback.OnSendStatusFromServerCompletion(true); 155 156 Assert.DoesNotThrowAsync(async () => await writeTask); 157 Assert.DoesNotThrowAsync(async () => await writeStatusTask); 158 159 fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); 160 161 AssertFinished(asyncCallServer, fakeCall, finishedTask); 162 } 163 164 [Test] WriteAfterWriteStatusThrowsInvalidOperationException()165 public void WriteAfterWriteStatusThrowsInvalidOperationException() 166 { 167 var finishedTask = asyncCallServer.ServerSideCallAsync(); 168 var responseStream = new ServerResponseStream<string, string>(asyncCallServer); 169 170 asyncCallServer.SendStatusFromServerAsync(Status.DefaultSuccess, new Metadata(), null); 171 Assert.ThrowsAsync(typeof(InvalidOperationException), async () => await responseStream.WriteAsync("request1")); 172 173 fakeCall.SendStatusFromServerCallback.OnSendStatusFromServerCompletion(true); 174 fakeCall.ReceivedCloseOnServerCallback.OnReceivedCloseOnServer(true, cancelled: true); 175 176 AssertFinished(asyncCallServer, fakeCall, finishedTask); 177 } 178 AssertFinished(AsyncCallServer<string, string> asyncCallServer, FakeNativeCall fakeCall, Task finishedTask)179 static void AssertFinished(AsyncCallServer<string, string> asyncCallServer, FakeNativeCall fakeCall, Task finishedTask) 180 { 181 Assert.IsTrue(fakeCall.IsDisposed); 182 Assert.IsTrue(finishedTask.IsCompleted); 183 Assert.DoesNotThrow(() => finishedTask.Wait()); 184 } 185 } 186 } 187