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.Diagnostics; 22 using System.Linq; 23 using System.Threading; 24 using System.Threading.Tasks; 25 using Grpc.Core; 26 using Grpc.Core.Internal; 27 using Grpc.Core.Utils; 28 using NUnit.Framework; 29 30 namespace Grpc.Core.Tests 31 { 32 /// <summary> 33 /// Allows setting up a mock service in the client-server tests easily. 34 /// </summary> 35 public class MockServiceHelper 36 { 37 public const string ServiceName = "tests.Test"; 38 39 readonly string host; 40 readonly IEnumerable<ChannelOption> channelOptions; 41 42 readonly Method<string, string> unaryMethod; 43 readonly Method<string, string> clientStreamingMethod; 44 readonly Method<string, string> serverStreamingMethod; 45 readonly Method<string, string> duplexStreamingMethod; 46 47 UnaryServerMethod<string, string> unaryHandler; 48 ClientStreamingServerMethod<string, string> clientStreamingHandler; 49 ServerStreamingServerMethod<string, string> serverStreamingHandler; 50 DuplexStreamingServerMethod<string, string> duplexStreamingHandler; 51 52 Server server; 53 Channel channel; 54 MockServiceHelper(string host = null, Marshaller<string> marshaller = null, IEnumerable<ChannelOption> channelOptions = null)55 public MockServiceHelper(string host = null, Marshaller<string> marshaller = null, IEnumerable<ChannelOption> channelOptions = null) 56 { 57 this.host = host ?? "localhost"; 58 this.channelOptions = channelOptions; 59 marshaller = marshaller ?? Marshallers.StringMarshaller; 60 61 unaryMethod = new Method<string, string>( 62 MethodType.Unary, 63 ServiceName, 64 "Unary", 65 marshaller, 66 marshaller); 67 68 clientStreamingMethod = new Method<string, string>( 69 MethodType.ClientStreaming, 70 ServiceName, 71 "ClientStreaming", 72 marshaller, 73 marshaller); 74 75 serverStreamingMethod = new Method<string, string>( 76 MethodType.ServerStreaming, 77 ServiceName, 78 "ServerStreaming", 79 marshaller, 80 marshaller); 81 82 duplexStreamingMethod = new Method<string, string>( 83 MethodType.DuplexStreaming, 84 ServiceName, 85 "DuplexStreaming", 86 marshaller, 87 marshaller); 88 89 ServiceDefinition = ServerServiceDefinition.CreateBuilder() 90 .AddMethod(unaryMethod, (request, context) => unaryHandler(request, context)) 91 .AddMethod(clientStreamingMethod, (requestStream, context) => clientStreamingHandler(requestStream, context)) 92 .AddMethod(serverStreamingMethod, (request, responseStream, context) => serverStreamingHandler(request, responseStream, context)) 93 .AddMethod(duplexStreamingMethod, (requestStream, responseStream, context) => duplexStreamingHandler(requestStream, responseStream, context)) 94 .Build(); 95 96 var defaultStatus = new Status(StatusCode.Unknown, "Default mock implementation. Please provide your own."); 97 98 unaryHandler = new UnaryServerMethod<string, string>((request, context) => 99 { 100 context.Status = defaultStatus; 101 return Task.FromResult(""); 102 }); 103 104 clientStreamingHandler = new ClientStreamingServerMethod<string, string>((requestStream, context) => 105 { 106 context.Status = defaultStatus; 107 return Task.FromResult(""); 108 }); 109 110 serverStreamingHandler = new ServerStreamingServerMethod<string, string>((request, responseStream, context) => 111 { 112 context.Status = defaultStatus; 113 return TaskUtils.CompletedTask; 114 }); 115 116 duplexStreamingHandler = new DuplexStreamingServerMethod<string, string>((requestStream, responseStream, context) => 117 { 118 context.Status = defaultStatus; 119 return TaskUtils.CompletedTask; 120 }); 121 } 122 123 /// <summary> 124 /// Returns the default server for this service and creates one if not yet created. 125 /// </summary> GetServer()126 public Server GetServer() 127 { 128 if (server == null) 129 { 130 // Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755 131 server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) }) 132 { 133 Services = { ServiceDefinition }, 134 Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } 135 }; 136 } 137 return server; 138 } 139 140 /// <summary> 141 /// Returns the default channel for this service and creates one if not yet created. 142 /// </summary> GetChannel()143 public Channel GetChannel() 144 { 145 if (channel == null) 146 { 147 channel = new Channel(Host, GetServer().Ports.Single().BoundPort, ChannelCredentials.Insecure, channelOptions); 148 } 149 return channel; 150 } 151 CreateUnaryCall(CallOptions options = default(CallOptions))152 public CallInvocationDetails<string, string> CreateUnaryCall(CallOptions options = default(CallOptions)) 153 { 154 return new CallInvocationDetails<string, string>(channel, unaryMethod, options); 155 } 156 CreateClientStreamingCall(CallOptions options = default(CallOptions))157 public CallInvocationDetails<string, string> CreateClientStreamingCall(CallOptions options = default(CallOptions)) 158 { 159 return new CallInvocationDetails<string, string>(channel, clientStreamingMethod, options); 160 } 161 CreateServerStreamingCall(CallOptions options = default(CallOptions))162 public CallInvocationDetails<string, string> CreateServerStreamingCall(CallOptions options = default(CallOptions)) 163 { 164 return new CallInvocationDetails<string, string>(channel, serverStreamingMethod, options); 165 } 166 CreateDuplexStreamingCall(CallOptions options = default(CallOptions))167 public CallInvocationDetails<string, string> CreateDuplexStreamingCall(CallOptions options = default(CallOptions)) 168 { 169 return new CallInvocationDetails<string, string>(channel, duplexStreamingMethod, options); 170 } 171 172 public string Host 173 { 174 get 175 { 176 return this.host; 177 } 178 } 179 180 public ServerServiceDefinition ServiceDefinition { get; set; } 181 182 public UnaryServerMethod<string, string> UnaryHandler 183 { 184 get 185 { 186 return this.unaryHandler; 187 } 188 189 set 190 { 191 unaryHandler = value; 192 } 193 } 194 195 public ClientStreamingServerMethod<string, string> ClientStreamingHandler 196 { 197 get 198 { 199 return this.clientStreamingHandler; 200 } 201 202 set 203 { 204 clientStreamingHandler = value; 205 } 206 } 207 208 public ServerStreamingServerMethod<string, string> ServerStreamingHandler 209 { 210 get 211 { 212 return this.serverStreamingHandler; 213 } 214 215 set 216 { 217 serverStreamingHandler = value; 218 } 219 } 220 221 public DuplexStreamingServerMethod<string, string> DuplexStreamingHandler 222 { 223 get 224 { 225 return this.duplexStreamingHandler; 226 } 227 228 set 229 { 230 duplexStreamingHandler = value; 231 } 232 } 233 } 234 } 235