From 503bbac2175c4dced3f82be428b74eafe4141251 Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Thu, 26 Feb 2015 18:19:47 -0800 Subject: added C# interop server --- src/csharp/Grpc.Core/Grpc.Core.csproj | 2 +- src/csharp/Grpc.Core/Server.cs | 11 + .../Grpc.IntegrationTesting.Client/.gitignore | 3 + .../Grpc.IntegrationTesting.Client.csproj | 49 +++ .../Grpc.IntegrationTesting.Client/Program.cs | 46 +++ .../Properties/AssemblyInfo.cs | 22 ++ .../Grpc.IntegrationTesting.Server/.gitignore | 3 + .../Grpc.IntegrationTesting.Server.csproj | 49 +++ .../Grpc.IntegrationTesting.Server/Program.cs | 45 +++ .../Properties/AssemblyInfo.cs | 22 ++ src/csharp/Grpc.IntegrationTesting/Client.cs | 351 --------------------- .../Grpc.IntegrationTesting.csproj | 6 +- .../Grpc.IntegrationTesting/InteropClient.cs | 351 +++++++++++++++++++++ .../InteropClientServerTest.cs | 12 +- .../Grpc.IntegrationTesting/InteropServer.cs | 140 ++++++++ src/csharp/Grpc.sln | 12 + 16 files changed, 763 insertions(+), 361 deletions(-) create mode 100644 src/csharp/Grpc.IntegrationTesting.Client/.gitignore create mode 100644 src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj create mode 100644 src/csharp/Grpc.IntegrationTesting.Client/Program.cs create mode 100644 src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs create mode 100644 src/csharp/Grpc.IntegrationTesting.Server/.gitignore create mode 100644 src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj create mode 100644 src/csharp/Grpc.IntegrationTesting.Server/Program.cs create mode 100644 src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs delete mode 100644 src/csharp/Grpc.IntegrationTesting/Client.cs create mode 100644 src/csharp/Grpc.IntegrationTesting/InteropClient.cs create mode 100644 src/csharp/Grpc.IntegrationTesting/InteropServer.cs (limited to 'src') diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index 183c442358..de742f99ad 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -76,7 +76,7 @@ - + \ No newline at end of file diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs index 002592a3d8..152cc2176c 100644 --- a/src/csharp/Grpc.Core/Server.cs +++ b/src/csharp/Grpc.Core/Server.cs @@ -124,6 +124,17 @@ namespace Grpc.Core handle.Dispose(); } + /// + /// To allow awaiting termination of the server. + /// + public Task ShutdownTask + { + get + { + return shutdownTcs.Task; + } + } + public void Kill() { handle.Dispose(); } diff --git a/src/csharp/Grpc.IntegrationTesting.Client/.gitignore b/src/csharp/Grpc.IntegrationTesting.Client/.gitignore new file mode 100644 index 0000000000..a382af2294 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Client/.gitignore @@ -0,0 +1,3 @@ +bin +obj + diff --git a/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj new file mode 100644 index 0000000000..b1a4a81916 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Client/Grpc.IntegrationTesting.Client.csproj @@ -0,0 +1,49 @@ + + + + Debug + x86 + 10.0.0 + 2.0 + {3D166931-BA2D-416E-95A3-D36E8F6E90B9} + Exe + Grpc.IntegrationTesting.Client + Grpc.IntegrationTesting.Client + Grpc.IntegrationTesting.Client.Program + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + x86 + + + full + true + bin\Release + prompt + 4 + true + x86 + + + + + + + + + + + + {C61154BA-DD4A-4838-8420-0162A28925E0} + Grpc.IntegrationTesting + + + \ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting.Client/Program.cs b/src/csharp/Grpc.IntegrationTesting.Client/Program.cs new file mode 100644 index 0000000000..2e1c9aaac2 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Client/Program.cs @@ -0,0 +1,46 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using Grpc.IntegrationTesting; + +namespace Grpc.IntegrationTesting.Client +{ + class Program + { + public static void Main(string[] args) + { + InteropClient.Run(args); + } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..d1f9e8560d --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Client/Properties/AssemblyInfo.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. +[assembly: AssemblyTitle("Grpc.IntegrationTesting.Client")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. +[assembly: AssemblyVersion("0.1.*")] +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/src/csharp/Grpc.IntegrationTesting.Server/.gitignore b/src/csharp/Grpc.IntegrationTesting.Server/.gitignore new file mode 100644 index 0000000000..a382af2294 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Server/.gitignore @@ -0,0 +1,3 @@ +bin +obj + diff --git a/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj new file mode 100644 index 0000000000..73c9f2d207 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Server/Grpc.IntegrationTesting.Server.csproj @@ -0,0 +1,49 @@ + + + + Debug + x86 + 10.0.0 + 2.0 + {A654F3B8-E859-4E6A-B30D-227527DBEF0D} + Exe + Grpc.IntegrationTesting.Server + Grpc.IntegrationTesting.Server + Grpc.IntegrationTesting.Server.Program + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + x86 + + + full + true + bin\Release + prompt + 4 + true + x86 + + + + + + + + + + + + {C61154BA-DD4A-4838-8420-0162A28925E0} + Grpc.IntegrationTesting + + + \ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting.Server/Program.cs b/src/csharp/Grpc.IntegrationTesting.Server/Program.cs new file mode 100644 index 0000000000..01bcc6e308 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Server/Program.cs @@ -0,0 +1,45 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; + +namespace Grpc.IntegrationTesting.Server +{ + class Program + { + public static void Main(string[] args) + { + InteropServer.Run(args); + } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs b/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..4ef93f328d --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting.Server/Properties/AssemblyInfo.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. +[assembly: AssemblyTitle("Grpc.IntegrationTesting.Server")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Google Inc. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. +[assembly: AssemblyVersion("0.1.*")] +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/src/csharp/Grpc.IntegrationTesting/Client.cs b/src/csharp/Grpc.IntegrationTesting/Client.cs deleted file mode 100644 index fa1c7cd051..0000000000 --- a/src/csharp/Grpc.IntegrationTesting/Client.cs +++ /dev/null @@ -1,351 +0,0 @@ -#region Copyright notice and license - -// Copyright 2015, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#endregion - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using Google.ProtocolBuffers; -using Grpc.Core; -using Grpc.Core.Utils; -using NUnit.Framework; -using grpc.testing; - -namespace Grpc.IntegrationTesting -{ - class Client - { - private class ClientOptions - { - public bool help; - public string serverHost; - public string serverHostOverride; - public int? serverPort; - public string testCase; - public bool useTls; - public bool useTestCa; - } - - ClientOptions options; - - private Client(ClientOptions options) - { - this.options = options; - } - - public static void Main(string[] args) - { - Console.WriteLine("gRPC C# interop testing client"); - ClientOptions options = ParseArguments(args); - - if (options.serverHost == null || !options.serverPort.HasValue || options.testCase == null) - { - Console.WriteLine("Missing required argument."); - Console.WriteLine(); - options.help = true; - } - - if (options.help) - { - Console.WriteLine("Usage:"); - Console.WriteLine(" --server_host=HOSTNAME"); - Console.WriteLine(" --server_host_override=HOSTNAME"); - Console.WriteLine(" --server_port=PORT"); - Console.WriteLine(" --test_case=TESTCASE"); - Console.WriteLine(" --use_tls=BOOLEAN"); - Console.WriteLine(" --use_test_ca=BOOLEAN"); - Console.WriteLine(); - Environment.Exit(1); - } - - var interopClient = new Client(options); - interopClient.Run(); - } - - private void Run() - { - GrpcEnvironment.Initialize(); - - string addr = string.Format("{0}:{1}", options.serverHost, options.serverPort); - using (Channel channel = new Channel(addr)) - { - TestServiceGrpc.ITestServiceClient client = new TestServiceGrpc.TestServiceClientStub(channel); - - RunTestCase(options.testCase, client); - } - - GrpcEnvironment.Shutdown(); - } - - private void RunTestCase(string testCase, TestServiceGrpc.ITestServiceClient client) - { - switch (testCase) - { - case "empty_unary": - RunEmptyUnary(client); - break; - case "large_unary": - RunLargeUnary(client); - break; - case "client_streaming": - RunClientStreaming(client); - break; - case "server_streaming": - RunServerStreaming(client); - break; - case "ping_pong": - RunPingPong(client); - break; - case "empty_stream": - RunEmptyStream(client); - break; - case "benchmark_empty_unary": - RunBenchmarkEmptyUnary(client); - break; - default: - throw new ArgumentException("Unknown test case " + testCase); - } - } - - public static void RunEmptyUnary(TestServiceGrpc.ITestServiceClient client) - { - Console.WriteLine("running empty_unary"); - var response = client.EmptyCall(Empty.DefaultInstance); - Assert.IsNotNull(response); - Console.WriteLine("Passed!"); - } - - public static void RunLargeUnary(TestServiceGrpc.ITestServiceClient client) - { - Console.WriteLine("running large_unary"); - var request = SimpleRequest.CreateBuilder() - .SetResponseType(PayloadType.COMPRESSABLE) - .SetResponseSize(314159) - .SetPayload(CreateZerosPayload(271828)) - .Build(); - - var response = client.UnaryCall(request); - - Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); - Assert.AreEqual(314159, response.Payload.Body.Length); - Console.WriteLine("Passed!"); - } - - public static void RunClientStreaming(TestServiceGrpc.ITestServiceClient client) - { - Console.WriteLine("running client_streaming"); - - var bodySizes = new List{27182, 8, 1828, 45904}; - - var context = client.StreamingInputCall(); - foreach (var size in bodySizes) - { - context.Inputs.OnNext( - StreamingInputCallRequest.CreateBuilder().SetPayload(CreateZerosPayload(size)).Build()); - } - context.Inputs.OnCompleted(); - - var response = context.Task.Result; - Assert.AreEqual(74922, response.AggregatedPayloadSize); - Console.WriteLine("Passed!"); - } - - public static void RunServerStreaming(TestServiceGrpc.ITestServiceClient client) - { - Console.WriteLine("running server_streaming"); - - var bodySizes = new List{31415, 9, 2653, 58979}; - - var request = StreamingOutputCallRequest.CreateBuilder() - .SetResponseType(PayloadType.COMPRESSABLE) - .AddRangeResponseParameters(bodySizes.ConvertAll( - (size) => ResponseParameters.CreateBuilder().SetSize(size).Build())) - .Build(); - - var recorder = new RecordingObserver(); - client.StreamingOutputCall(request, recorder); - - var responseList = recorder.ToList().Result; - - foreach (var res in responseList) - { - Assert.AreEqual(PayloadType.COMPRESSABLE, res.Payload.Type); - } - CollectionAssert.AreEqual(bodySizes, responseList.ConvertAll((item) => item.Payload.Body.Length)); - Console.WriteLine("Passed!"); - } - - public static void RunPingPong(TestServiceGrpc.ITestServiceClient client) - { - Console.WriteLine("running ping_pong"); - - var recorder = new RecordingQueue(); - var inputs = client.FullDuplexCall(recorder); - - StreamingOutputCallResponse response; - - inputs.OnNext(StreamingOutputCallRequest.CreateBuilder() - .SetResponseType(PayloadType.COMPRESSABLE) - .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(31415)) - .SetPayload(CreateZerosPayload(27182)).Build()); - - response = recorder.Queue.Take(); - Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); - Assert.AreEqual(31415, response.Payload.Body.Length); - - inputs.OnNext(StreamingOutputCallRequest.CreateBuilder() - .SetResponseType(PayloadType.COMPRESSABLE) - .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(9)) - .SetPayload(CreateZerosPayload(8)).Build()); - - response = recorder.Queue.Take(); - Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); - Assert.AreEqual(9, response.Payload.Body.Length); - - inputs.OnNext(StreamingOutputCallRequest.CreateBuilder() - .SetResponseType(PayloadType.COMPRESSABLE) - .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(2653)) - .SetPayload(CreateZerosPayload(1828)).Build()); - - response = recorder.Queue.Take(); - Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); - Assert.AreEqual(2653, response.Payload.Body.Length); - - - inputs.OnNext(StreamingOutputCallRequest.CreateBuilder() - .SetResponseType(PayloadType.COMPRESSABLE) - .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(58979)) - .SetPayload(CreateZerosPayload(45904)).Build()); - - response = recorder.Queue.Take(); - Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); - Assert.AreEqual(58979, response.Payload.Body.Length); - - inputs.OnCompleted(); - - recorder.Finished.Wait(); - Assert.AreEqual(0, recorder.Queue.Count); - - Console.WriteLine("Passed!"); - } - - public static void RunEmptyStream(TestServiceGrpc.ITestServiceClient client) - { - Console.WriteLine("running empty_stream"); - - var recorder = new RecordingObserver(); - var inputs = client.FullDuplexCall(recorder); - inputs.OnCompleted(); - - var responseList = recorder.ToList().Result; - Assert.AreEqual(0, responseList.Count); - - Console.WriteLine("Passed!"); - } - - // This is not an official interop test, but it's useful. - public static void RunBenchmarkEmptyUnary(TestServiceGrpc.ITestServiceClient client) - { - BenchmarkUtil.RunBenchmark(10000, 10000, - () => { client.EmptyCall(Empty.DefaultInstance);}); - } - - private static Payload CreateZerosPayload(int size) { - return Payload.CreateBuilder().SetBody(ByteString.CopyFrom(new byte[size])).Build(); - } - - private static ClientOptions ParseArguments(string[] args) - { - var options = new ClientOptions(); - foreach(string arg in args) - { - ParseArgument(arg, options); - if (options.help) - { - break; - } - } - return options; - } - - private static void ParseArgument(string arg, ClientOptions options) - { - Match match; - match = Regex.Match(arg, "--server_host=(.*)"); - if (match.Success) - { - options.serverHost = match.Groups[1].Value.Trim(); - return; - } - - match = Regex.Match(arg, "--server_host_override=(.*)"); - if (match.Success) - { - options.serverHostOverride = match.Groups[1].Value.Trim(); - return; - } - - match = Regex.Match(arg, "--server_port=(.*)"); - if (match.Success) - { - options.serverPort = int.Parse(match.Groups[1].Value.Trim()); - return; - } - - match = Regex.Match(arg, "--test_case=(.*)"); - if (match.Success) - { - options.testCase = match.Groups[1].Value.Trim(); - return; - } - - match = Regex.Match(arg, "--use_tls=(.*)"); - if (match.Success) - { - options.useTls = bool.Parse(match.Groups[1].Value.Trim()); - return; - } - - match = Regex.Match(arg, "--use_test_ca=(.*)"); - if (match.Success) - { - options.useTestCa = bool.Parse(match.Groups[1].Value.Trim()); - return; - } - - Console.WriteLine(string.Format("Unrecognized argument \"{0}\"", arg)); - options.help = true; - } - } -} diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj index e66f708a94..6d6aaf5747 100644 --- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj +++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj @@ -6,10 +6,9 @@ 10.0.0 2.0 {C61154BA-DD4A-4838-8420-0162A28925E0} - Exe + Library Grpc.IntegrationTesting Grpc.IntegrationTesting - Grpc.IntegrationTesting.Client v4.5 @@ -43,12 +42,13 @@ - + + diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs new file mode 100644 index 0000000000..a7a3c63e03 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs @@ -0,0 +1,351 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Google.ProtocolBuffers; +using Grpc.Core; +using Grpc.Core.Utils; +using NUnit.Framework; +using grpc.testing; + +namespace Grpc.IntegrationTesting +{ + public class InteropClient + { + private class ClientOptions + { + public bool help; + public string serverHost; + public string serverHostOverride; + public int? serverPort; + public string testCase; + public bool useTls; + public bool useTestCa; + } + + ClientOptions options; + + private InteropClient(ClientOptions options) + { + this.options = options; + } + + public static void Run(string[] args) + { + Console.WriteLine("gRPC C# interop testing client"); + ClientOptions options = ParseArguments(args); + + if (options.serverHost == null || !options.serverPort.HasValue || options.testCase == null) + { + Console.WriteLine("Missing required argument."); + Console.WriteLine(); + options.help = true; + } + + if (options.help) + { + Console.WriteLine("Usage:"); + Console.WriteLine(" --server_host=HOSTNAME"); + Console.WriteLine(" --server_host_override=HOSTNAME"); + Console.WriteLine(" --server_port=PORT"); + Console.WriteLine(" --test_case=TESTCASE"); + Console.WriteLine(" --use_tls=BOOLEAN"); + Console.WriteLine(" --use_test_ca=BOOLEAN"); + Console.WriteLine(); + Environment.Exit(1); + } + + var interopClient = new InteropClient(options); + interopClient.Run(); + } + + private void Run() + { + GrpcEnvironment.Initialize(); + + string addr = string.Format("{0}:{1}", options.serverHost, options.serverPort); + using (Channel channel = new Channel(addr)) + { + TestServiceGrpc.ITestServiceClient client = new TestServiceGrpc.TestServiceClientStub(channel); + + RunTestCase(options.testCase, client); + } + + GrpcEnvironment.Shutdown(); + } + + private void RunTestCase(string testCase, TestServiceGrpc.ITestServiceClient client) + { + switch (testCase) + { + case "empty_unary": + RunEmptyUnary(client); + break; + case "large_unary": + RunLargeUnary(client); + break; + case "client_streaming": + RunClientStreaming(client); + break; + case "server_streaming": + RunServerStreaming(client); + break; + case "ping_pong": + RunPingPong(client); + break; + case "empty_stream": + RunEmptyStream(client); + break; + case "benchmark_empty_unary": + RunBenchmarkEmptyUnary(client); + break; + default: + throw new ArgumentException("Unknown test case " + testCase); + } + } + + public static void RunEmptyUnary(TestServiceGrpc.ITestServiceClient client) + { + Console.WriteLine("running empty_unary"); + var response = client.EmptyCall(Empty.DefaultInstance); + Assert.IsNotNull(response); + Console.WriteLine("Passed!"); + } + + public static void RunLargeUnary(TestServiceGrpc.ITestServiceClient client) + { + Console.WriteLine("running large_unary"); + var request = SimpleRequest.CreateBuilder() + .SetResponseType(PayloadType.COMPRESSABLE) + .SetResponseSize(314159) + .SetPayload(CreateZerosPayload(271828)) + .Build(); + + var response = client.UnaryCall(request); + + Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); + Assert.AreEqual(314159, response.Payload.Body.Length); + Console.WriteLine("Passed!"); + } + + public static void RunClientStreaming(TestServiceGrpc.ITestServiceClient client) + { + Console.WriteLine("running client_streaming"); + + var bodySizes = new List{27182, 8, 1828, 45904}; + + var context = client.StreamingInputCall(); + foreach (var size in bodySizes) + { + context.Inputs.OnNext( + StreamingInputCallRequest.CreateBuilder().SetPayload(CreateZerosPayload(size)).Build()); + } + context.Inputs.OnCompleted(); + + var response = context.Task.Result; + Assert.AreEqual(74922, response.AggregatedPayloadSize); + Console.WriteLine("Passed!"); + } + + public static void RunServerStreaming(TestServiceGrpc.ITestServiceClient client) + { + Console.WriteLine("running server_streaming"); + + var bodySizes = new List{31415, 9, 2653, 58979}; + + var request = StreamingOutputCallRequest.CreateBuilder() + .SetResponseType(PayloadType.COMPRESSABLE) + .AddRangeResponseParameters(bodySizes.ConvertAll( + (size) => ResponseParameters.CreateBuilder().SetSize(size).Build())) + .Build(); + + var recorder = new RecordingObserver(); + client.StreamingOutputCall(request, recorder); + + var responseList = recorder.ToList().Result; + + foreach (var res in responseList) + { + Assert.AreEqual(PayloadType.COMPRESSABLE, res.Payload.Type); + } + CollectionAssert.AreEqual(bodySizes, responseList.ConvertAll((item) => item.Payload.Body.Length)); + Console.WriteLine("Passed!"); + } + + public static void RunPingPong(TestServiceGrpc.ITestServiceClient client) + { + Console.WriteLine("running ping_pong"); + + var recorder = new RecordingQueue(); + var inputs = client.FullDuplexCall(recorder); + + StreamingOutputCallResponse response; + + inputs.OnNext(StreamingOutputCallRequest.CreateBuilder() + .SetResponseType(PayloadType.COMPRESSABLE) + .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(31415)) + .SetPayload(CreateZerosPayload(27182)).Build()); + + response = recorder.Queue.Take(); + Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); + Assert.AreEqual(31415, response.Payload.Body.Length); + + inputs.OnNext(StreamingOutputCallRequest.CreateBuilder() + .SetResponseType(PayloadType.COMPRESSABLE) + .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(9)) + .SetPayload(CreateZerosPayload(8)).Build()); + + response = recorder.Queue.Take(); + Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); + Assert.AreEqual(9, response.Payload.Body.Length); + + inputs.OnNext(StreamingOutputCallRequest.CreateBuilder() + .SetResponseType(PayloadType.COMPRESSABLE) + .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(2653)) + .SetPayload(CreateZerosPayload(1828)).Build()); + + response = recorder.Queue.Take(); + Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); + Assert.AreEqual(2653, response.Payload.Body.Length); + + + inputs.OnNext(StreamingOutputCallRequest.CreateBuilder() + .SetResponseType(PayloadType.COMPRESSABLE) + .AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(58979)) + .SetPayload(CreateZerosPayload(45904)).Build()); + + response = recorder.Queue.Take(); + Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); + Assert.AreEqual(58979, response.Payload.Body.Length); + + inputs.OnCompleted(); + + recorder.Finished.Wait(); + Assert.AreEqual(0, recorder.Queue.Count); + + Console.WriteLine("Passed!"); + } + + public static void RunEmptyStream(TestServiceGrpc.ITestServiceClient client) + { + Console.WriteLine("running empty_stream"); + + var recorder = new RecordingObserver(); + var inputs = client.FullDuplexCall(recorder); + inputs.OnCompleted(); + + var responseList = recorder.ToList().Result; + Assert.AreEqual(0, responseList.Count); + + Console.WriteLine("Passed!"); + } + + // This is not an official interop test, but it's useful. + public static void RunBenchmarkEmptyUnary(TestServiceGrpc.ITestServiceClient client) + { + BenchmarkUtil.RunBenchmark(10000, 10000, + () => { client.EmptyCall(Empty.DefaultInstance);}); + } + + private static Payload CreateZerosPayload(int size) { + return Payload.CreateBuilder().SetBody(ByteString.CopyFrom(new byte[size])).Build(); + } + + private static ClientOptions ParseArguments(string[] args) + { + var options = new ClientOptions(); + foreach(string arg in args) + { + ParseArgument(arg, options); + if (options.help) + { + break; + } + } + return options; + } + + private static void ParseArgument(string arg, ClientOptions options) + { + Match match; + match = Regex.Match(arg, "--server_host=(.*)"); + if (match.Success) + { + options.serverHost = match.Groups[1].Value.Trim(); + return; + } + + match = Regex.Match(arg, "--server_host_override=(.*)"); + if (match.Success) + { + options.serverHostOverride = match.Groups[1].Value.Trim(); + return; + } + + match = Regex.Match(arg, "--server_port=(.*)"); + if (match.Success) + { + options.serverPort = int.Parse(match.Groups[1].Value.Trim()); + return; + } + + match = Regex.Match(arg, "--test_case=(.*)"); + if (match.Success) + { + options.testCase = match.Groups[1].Value.Trim(); + return; + } + + match = Regex.Match(arg, "--use_tls=(.*)"); + if (match.Success) + { + options.useTls = bool.Parse(match.Groups[1].Value.Trim()); + return; + } + + match = Regex.Match(arg, "--use_test_ca=(.*)"); + if (match.Success) + { + options.useTestCa = bool.Parse(match.Groups[1].Value.Trim()); + return; + } + + Console.WriteLine(string.Format("Unrecognized argument \"{0}\"", arg)); + options.help = true; + } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs index 87d25b0a98..4bb0b9ee51 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs @@ -77,37 +77,37 @@ namespace Grpc.IntegrationTesting [Test] public void EmptyUnary() { - Client.RunEmptyUnary(client); + InteropClient.RunEmptyUnary(client); } [Test] public void LargeUnary() { - Client.RunEmptyUnary(client); + InteropClient.RunEmptyUnary(client); } [Test] public void ClientStreaming() { - Client.RunClientStreaming(client); + InteropClient.RunClientStreaming(client); } [Test] public void ServerStreaming() { - Client.RunServerStreaming(client); + InteropClient.RunServerStreaming(client); } [Test] public void PingPong() { - Client.RunPingPong(client); + InteropClient.RunPingPong(client); } [Test] public void EmptyStream() { - Client.RunEmptyStream(client); + InteropClient.RunEmptyStream(client); } // TODO: add cancel_after_begin diff --git a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs new file mode 100644 index 0000000000..a25d3b3530 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs @@ -0,0 +1,140 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Google.ProtocolBuffers; +using Grpc.Core; +using Grpc.Core.Utils; +using NUnit.Framework; +using grpc.testing; + +namespace Grpc.IntegrationTesting +{ + public class InteropServer + { + private class ServerOptions + { + public bool help; + public int? port; + public bool useTls; + } + + ServerOptions options; + + private InteropServer(ServerOptions options) + { + this.options = options; + } + + public static void Run(string[] args) + { + Console.WriteLine("gRPC C# interop testing server"); + ServerOptions options = ParseArguments(args); + + if (!options.port.HasValue) + { + Console.WriteLine("Missing required argument."); + Console.WriteLine(); + options.help = true; + } + + if (options.help) + { + Console.WriteLine("Usage:"); + Console.WriteLine(" --port=PORT"); + Console.WriteLine(" --use_tls=BOOLEAN"); + Console.WriteLine(); + Environment.Exit(1); + } + + var interopServer = new InteropServer(options); + interopServer.Run(); + } + + private void Run() + { + GrpcEnvironment.Initialize(); + + var server = new Server(); + server.AddServiceDefinition(TestServiceGrpc.BindService(new TestServiceImpl())); + + string addr = "0.0.0.0:" + options.port; + server.AddPort(addr); + Console.WriteLine("Running server on " + addr); + server.Start(); + + server.ShutdownTask.Wait(); + + GrpcEnvironment.Shutdown(); + } + + private static ServerOptions ParseArguments(string[] args) + { + var options = new ServerOptions(); + foreach(string arg in args) + { + ParseArgument(arg, options); + if (options.help) + { + break; + } + } + return options; + } + + private static void ParseArgument(string arg, ServerOptions options) + { + Match match; + match = Regex.Match(arg, "--port=(.*)"); + if (match.Success) + { + options.port = int.Parse(match.Groups[1].Value.Trim()); + return; + } + + match = Regex.Match(arg, "--use_tls=(.*)"); + if (match.Success) + { + options.useTls = bool.Parse(match.Groups[1].Value.Trim()); + return; + } + + Console.WriteLine(string.Format("Unrecognized argument \"{0}\"", arg)); + options.help = true; + } + } +} diff --git a/src/csharp/Grpc.sln b/src/csharp/Grpc.sln index a544eb1c33..2e6d288699 100644 --- a/src/csharp/Grpc.sln +++ b/src/csharp/Grpc.sln @@ -13,6 +13,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.Examples.MathClient", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.IntegrationTesting", "Grpc.IntegrationTesting\Grpc.IntegrationTesting.csproj", "{C61154BA-DD4A-4838-8420-0162A28925E0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.IntegrationTesting.Client", "Grpc.IntegrationTesting.Client\Grpc.IntegrationTesting.Client.csproj", "{3D166931-BA2D-416E-95A3-D36E8F6E90B9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Grpc.IntegrationTesting.Server", "Grpc.IntegrationTesting.Server\Grpc.IntegrationTesting.Server.csproj", "{A654F3B8-E859-4E6A-B30D-227527DBEF0D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 @@ -23,6 +27,10 @@ Global {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Debug|x86.Build.0 = Debug|Any CPU {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|x86.ActiveCfg = Release|Any CPU {143B1C29-C442-4BE0-BF3F-A8F92288AC9F}.Release|x86.Build.0 = Release|Any CPU + {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|x86.ActiveCfg = Debug|x86 + {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Debug|x86.Build.0 = Debug|x86 + {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|x86.ActiveCfg = Release|x86 + {3D166931-BA2D-416E-95A3-D36E8F6E90B9}.Release|x86.Build.0 = Release|x86 {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.ActiveCfg = Debug|x86 {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.Build.0 = Debug|x86 {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.ActiveCfg = Release|x86 @@ -35,6 +43,10 @@ Global {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.Build.0 = Debug|Any CPU {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.ActiveCfg = Release|Any CPU {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.Build.0 = Release|Any CPU + {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|x86.ActiveCfg = Debug|x86 + {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Debug|x86.Build.0 = Debug|x86 + {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|x86.ActiveCfg = Release|x86 + {A654F3B8-E859-4E6A-B30D-227527DBEF0D}.Release|x86.Build.0 = Release|x86 {C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|x86.ActiveCfg = Debug|x86 {C61154BA-DD4A-4838-8420-0162A28925E0}.Debug|x86.Build.0 = Debug|x86 {C61154BA-DD4A-4838-8420-0162A28925E0}.Release|x86.ActiveCfg = Release|x86 -- cgit v1.2.3