diff options
Diffstat (limited to 'src/csharp/Grpc.Core.Tests')
-rw-r--r-- | src/csharp/Grpc.Core.Tests/CallOptionsTest.cs | 88 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs | 2 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/ClientServerTest.cs | 54 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs | 32 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj | 2 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs | 105 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/MetadataTest.cs | 113 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/PInvokeTest.cs | 4 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/PerformanceTest.cs | 8 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/SanityTest.cs | 125 | ||||
-rw-r--r-- | src/csharp/Grpc.Core.Tests/ShutdownTest.cs | 11 |
11 files changed, 458 insertions, 86 deletions
diff --git a/src/csharp/Grpc.Core.Tests/CallOptionsTest.cs b/src/csharp/Grpc.Core.Tests/CallOptionsTest.cs new file mode 100644 index 0000000000..a3a613be74 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/CallOptionsTest.cs @@ -0,0 +1,88 @@ +#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.Threading; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class CallOptionsTest + { + [Test] + public void WithMethods() + { + var options = new CallOptions(); + + var metadata = new Metadata(); + Assert.AreSame(metadata, options.WithHeaders(metadata).Headers); + + var deadline = DateTime.UtcNow; + Assert.AreEqual(deadline, options.WithDeadline(deadline).Deadline.Value); + + var token = new CancellationTokenSource().Token; + Assert.AreEqual(token, options.WithCancellationToken(token).CancellationToken); + + // Change original instance is unchanged. + Assert.IsNull(options.Headers); + Assert.IsNull(options.Deadline); + Assert.AreEqual(CancellationToken.None, options.CancellationToken); + Assert.IsNull(options.WriteOptions); + Assert.IsNull(options.PropagationToken); + Assert.IsNull(options.Credentials); + } + + [Test] + public void Normalize() + { + Assert.AreSame(Metadata.Empty, new CallOptions().Normalize().Headers); + Assert.AreEqual(DateTime.MaxValue, new CallOptions().Normalize().Deadline.Value); + + var deadline = DateTime.UtcNow; + var propagationToken1 = new ContextPropagationToken(CallSafeHandle.NullInstance, deadline, CancellationToken.None, + new ContextPropagationOptions(propagateDeadline: true, propagateCancellation: false)); + Assert.AreEqual(deadline, new CallOptions(propagationToken: propagationToken1).Normalize().Deadline.Value); + Assert.Throws(typeof(ArgumentException), () => new CallOptions(deadline: deadline, propagationToken: propagationToken1).Normalize()); + + var token = new CancellationTokenSource().Token; + var propagationToken2 = new ContextPropagationToken(CallSafeHandle.NullInstance, deadline, token, + new ContextPropagationOptions(propagateDeadline: false, propagateCancellation: true)); + Assert.AreEqual(token, new CallOptions(propagationToken: propagationToken2).Normalize().CancellationToken); + Assert.Throws(typeof(ArgumentException), () => new CallOptions(cancellationToken: token, propagationToken: propagationToken2).Normalize()); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs b/src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs index 52be77c846..d2b5a436fd 100644 --- a/src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs +++ b/src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs @@ -38,7 +38,7 @@ using Grpc.Core.Internal; using Grpc.Core.Utils; using NUnit.Framework; -namespace Grpc.Core.Internal.Tests +namespace Grpc.Core.Tests { public class ChannelOptionsTest { diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index b683751bc0..77f6a63156 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -32,6 +32,7 @@ #endregion using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; @@ -144,6 +145,45 @@ namespace Grpc.Core.Tests var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall()); await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" }); Assert.AreEqual("ABC", await call.ResponseAsync); + + Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode); + Assert.IsNotNull(call.GetTrailers()); + } + + [Test] + public async Task ServerStreamingCall() + { + helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>(async (request, responseStream, context) => + { + await responseStream.WriteAllAsync(request.Split(new []{' '})); + context.ResponseTrailers.Add("xyz", ""); + }); + + var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "A B C"); + CollectionAssert.AreEqual(new string[] { "A", "B", "C" }, await call.ResponseStream.ToListAsync()); + + Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode); + Assert.IsNotNull("xyz", call.GetTrailers()[0].Key); + } + + [Test] + public async Task DuplexStreamingCall() + { + helper.DuplexStreamingHandler = new DuplexStreamingServerMethod<string, string>(async (requestStream, responseStream, context) => + { + while (await requestStream.MoveNext()) + { + await responseStream.WriteAsync(requestStream.Current); + } + context.ResponseTrailers.Add("xyz", "xyz-value"); + }); + + var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall()); + await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" }); + CollectionAssert.AreEqual(new string[] { "A", "B", "C" }, await call.ResponseStream.ToListAsync()); + + Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode); + Assert.IsNotNull("xyz-value", call.GetTrailers()[0].Value); } [Test] @@ -219,7 +259,7 @@ namespace Grpc.Core.Tests } [Test] - public void PeerInfoPresent() + public void ServerCallContext_PeerInfoPresent() { helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => { @@ -231,6 +271,18 @@ namespace Grpc.Core.Tests } [Test] + public void ServerCallContext_HostAndMethodPresent() + { + helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => + { + Assert.IsTrue(context.Host.Contains(Host)); + Assert.AreEqual("/tests.Test/Unary", context.Method); + return "PASS"; + }); + Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); + } + + [Test] public async Task Channel_WaitForStateChangedAsync() { helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => diff --git a/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs b/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs index 2db3f286f7..90c510ec61 100644 --- a/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs +++ b/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs @@ -69,11 +69,19 @@ namespace Grpc.Core.Tests [Test] public async Task PropagateCancellation() { + var readyToCancelTcs = new TaskCompletionSource<object>(); + var successTcs = new TaskCompletionSource<string>(); + helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => { - // check that we didn't obtain the default cancellation token. - Assert.IsTrue(context.CancellationToken.CanBeCanceled); - return "PASS"; + readyToCancelTcs.SetResult(null); // child call running, ready to parent call + + while (!context.CancellationToken.IsCancellationRequested) + { + await Task.Delay(10); + } + successTcs.SetResult("CHILD_CALL_CANCELLED"); + return ""; }); helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) => @@ -82,13 +90,23 @@ namespace Grpc.Core.Tests Assert.IsNotNull(propagationToken.ParentCall); var callOptions = new CallOptions(propagationToken: propagationToken); - return await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz"); + try + { + await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz"); + } + catch(RpcException) + { + // Child call will get cancelled, eat the exception. + } + return ""; }); var cts = new CancellationTokenSource(); - var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(cancellationToken: cts.Token))); - await call.RequestStream.CompleteAsync(); - Assert.AreEqual("PASS", await call); + var parentCall = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(cancellationToken: cts.Token))); + await readyToCancelTcs.Task; + cts.Cancel(); + Assert.Throws(typeof(RpcException), async () => await parentCall); + Assert.AreEqual("CHILD_CALL_CANCELLED", await successTcs.Task); } [Test] diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj index 70b83f7fb1..475c792347 100644 --- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj +++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj @@ -64,6 +64,7 @@ <Link>Version.cs</Link> </Compile> <Compile Include="CallCredentialsTest.cs" /> + <Compile Include="CallOptionsTest.cs" /> <Compile Include="UserAgentStringTest.cs" /> <Compile Include="FakeCredentials.cs" /> <Compile Include="MarshallingErrorsTest.cs" /> @@ -90,6 +91,7 @@ <Compile Include="ContextPropagationTest.cs" /> <Compile Include="MetadataTest.cs" /> <Compile Include="PerformanceTest.cs" /> + <Compile Include="SanityTest.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup> diff --git a/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs b/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs index 9be5450d81..74f7f2497a 100644 --- a/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs +++ b/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs @@ -82,17 +82,17 @@ namespace Grpc.Core.Internal.Tests public void ToDateTime() { Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), - new Timespec(IntPtr.Zero, 0).ToDateTime()); + new Timespec(0, 0).ToDateTime()); Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 10, DateTimeKind.Utc).AddTicks(50), - new Timespec(new IntPtr(10), 5000).ToDateTime()); + new Timespec(10, 5000).ToDateTime()); Assert.AreEqual(new DateTime(2015, 7, 21, 4, 21, 48, DateTimeKind.Utc), - new Timespec(new IntPtr(1437452508), 0).ToDateTime()); + new Timespec(1437452508, 0).ToDateTime()); // before epoch Assert.AreEqual(new DateTime(1969, 12, 31, 23, 59, 55, DateTimeKind.Utc).AddTicks(10), - new Timespec(new IntPtr(-5), 1000).ToDateTime()); + new Timespec(-5, 1000).ToDateTime()); // infinity Assert.AreEqual(DateTime.MaxValue, Timespec.InfFuture.ToDateTime()); @@ -100,80 +100,62 @@ namespace Grpc.Core.Internal.Tests // nanos are rounded to ticks are rounded up Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddTicks(1), - new Timespec(IntPtr.Zero, 99).ToDateTime()); + new Timespec(0, 99).ToDateTime()); // Illegal inputs Assert.Throws(typeof(InvalidOperationException), - () => new Timespec(new IntPtr(0), -2).ToDateTime()); + () => new Timespec(0, -2).ToDateTime()); Assert.Throws(typeof(InvalidOperationException), - () => new Timespec(new IntPtr(0), 1000 * 1000 * 1000).ToDateTime()); + () => new Timespec(0, 1000 * 1000 * 1000).ToDateTime()); Assert.Throws(typeof(InvalidOperationException), - () => new Timespec(new IntPtr(0), 0, GPRClockType.Monotonic).ToDateTime()); + () => new Timespec(0, 0, GPRClockType.Monotonic).ToDateTime()); } [Test] public void ToDateTime_ReturnsUtc() { - Assert.AreEqual(DateTimeKind.Utc, new Timespec(new IntPtr(1437452508), 0).ToDateTime().Kind); - Assert.AreNotEqual(DateTimeKind.Unspecified, new Timespec(new IntPtr(1437452508), 0).ToDateTime().Kind); + Assert.AreEqual(DateTimeKind.Utc, new Timespec(1437452508, 0).ToDateTime().Kind); + Assert.AreNotEqual(DateTimeKind.Unspecified, new Timespec(1437452508, 0).ToDateTime().Kind); } [Test] public void ToDateTime_Overflow() - { - // we can only get overflow in ticks arithmetic on 64-bit - if (IntPtr.Size == 8) - { - var timespec = new Timespec(new IntPtr(long.MaxValue - 100), 0); - Assert.AreNotEqual(Timespec.InfFuture, timespec); - Assert.AreEqual(DateTime.MaxValue, timespec.ToDateTime()); - - Assert.AreEqual(DateTime.MinValue, new Timespec(new IntPtr(long.MinValue + 100), 0).ToDateTime()); - } - else - { - Console.WriteLine("Test cannot be run on this platform, skipping the test."); - } + { + var timespec = new Timespec(long.MaxValue - 100, 0); + Assert.AreNotEqual(Timespec.InfFuture, timespec); + Assert.AreEqual(DateTime.MaxValue, timespec.ToDateTime()); + + Assert.AreEqual(DateTime.MinValue, new Timespec(long.MinValue + 100, 0).ToDateTime()); } [Test] public void ToDateTime_OutOfDateTimeRange() { - // we can only get out of range on 64-bit, on 32 bit the max - // timestamp is ~ Jan 19 2038, which is far within range of DateTime - // same case for min value. - if (IntPtr.Size == 8) - { - // DateTime range goes up to year 9999, 20000 years from now should - // be out of range. - long seconds = 20000L * 365L * 24L * 3600L; - - var timespec = new Timespec(new IntPtr(seconds), 0); - Assert.AreNotEqual(Timespec.InfFuture, timespec); - Assert.AreEqual(DateTime.MaxValue, timespec.ToDateTime()); - - Assert.AreEqual(DateTime.MinValue, new Timespec(new IntPtr(-seconds), 0).ToDateTime()); - } - else - { - Console.WriteLine("Test cannot be run on this platform, skipping the test"); - } + // DateTime range goes up to year 9999, 20000 years from now should + // be out of range. + long seconds = 20000L * 365L * 24L * 3600L; + + var timespec = new Timespec(seconds, 0); + Assert.AreNotEqual(Timespec.InfFuture, timespec); + Assert.AreEqual(DateTime.MaxValue, timespec.ToDateTime()); + + Assert.AreEqual(DateTime.MinValue, new Timespec(-seconds, 0).ToDateTime()); } [Test] public void FromDateTime() { - Assert.AreEqual(new Timespec(IntPtr.Zero, 0), + Assert.AreEqual(new Timespec(0, 0), Timespec.FromDateTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc))); - Assert.AreEqual(new Timespec(new IntPtr(10), 5000), + Assert.AreEqual(new Timespec(10, 5000), Timespec.FromDateTime(new DateTime(1970, 1, 1, 0, 0, 10, DateTimeKind.Utc).AddTicks(50))); - Assert.AreEqual(new Timespec(new IntPtr(1437452508), 0), + Assert.AreEqual(new Timespec(1437452508, 0), Timespec.FromDateTime(new DateTime(2015, 7, 21, 4, 21, 48, DateTimeKind.Utc))); // before epoch - Assert.AreEqual(new Timespec(new IntPtr(-5), 1000), + Assert.AreEqual(new Timespec(-5, 1000), Timespec.FromDateTime(new DateTime(1969, 12, 31, 23, 59, 55, DateTimeKind.Utc).AddTicks(10))); // infinity @@ -184,34 +166,19 @@ namespace Grpc.Core.Internal.Tests Assert.Throws(typeof(ArgumentException), () => Timespec.FromDateTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Unspecified))); } - - [Test] - public void FromDateTime_OutOfTimespecRange() - { - // we can only get overflow in Timespec on 32-bit - if (IntPtr.Size == 4) - { - Assert.AreEqual(Timespec.InfFuture, Timespec.FromDateTime(new DateTime(2040, 1, 1, 0, 0, 0, DateTimeKind.Utc))); - Assert.AreEqual(Timespec.InfPast, Timespec.FromDateTime(new DateTime(1800, 1, 1, 0, 0, 0, DateTimeKind.Utc))); - } - else - { - Console.WriteLine("Test cannot be run on this platform, skipping the test."); - } - } - // Test attribute commented out to prevent running as part of the default test suite. - // [Test] - // [Category("Performance")] + [Test] + [Category("Performance")] + [Ignore("Prevent running on Jenkins")] public void NowBenchmark() { // approx Timespec.Now latency <33ns BenchmarkUtil.RunBenchmark(10000000, 1000000000, () => { var now = Timespec.Now; }); } - - // Test attribute commented out to prevent running as part of the default test suite. - // [Test] - // [Category("Performance")] + + [Test] + [Category("Performance")] + [Ignore("Prevent running on Jenkins")] public void PreciseNowBenchmark() { // approx Timespec.PreciseNow latency <18ns (when compiled with GRPC_TIMERS_RDTSC) diff --git a/src/csharp/Grpc.Core.Tests/MetadataTest.cs b/src/csharp/Grpc.Core.Tests/MetadataTest.cs index ddeb7d0926..49e9de1174 100644 --- a/src/csharp/Grpc.Core.Tests/MetadataTest.cs +++ b/src/csharp/Grpc.Core.Tests/MetadataTest.cs @@ -127,5 +127,118 @@ namespace Grpc.Core.Tests Assert.Throws(typeof(InvalidOperationException), () => { var v = entry.Value; }); CollectionAssert.AreEqual(bytes, entry.ValueBytes); } + + [Test] + public void IndexOf() + { + var metadata = CreateMetadata(); + Assert.AreEqual(0, metadata.IndexOf(metadata[0])); + Assert.AreEqual(1, metadata.IndexOf(metadata[1])); + } + + [Test] + public void Insert() + { + var metadata = CreateMetadata(); + metadata.Insert(0, new Metadata.Entry("new-key", "new-value")); + Assert.AreEqual(3, metadata.Count); + Assert.AreEqual("new-key", metadata[0].Key); + Assert.AreEqual("abc", metadata[1].Key); + } + + [Test] + public void RemoveAt() + { + var metadata = CreateMetadata(); + metadata.RemoveAt(0); + Assert.AreEqual(1, metadata.Count); + Assert.AreEqual("xyz", metadata[0].Key); + } + + [Test] + public void Remove() + { + var metadata = CreateMetadata(); + metadata.Remove(metadata[0]); + Assert.AreEqual(1, metadata.Count); + Assert.AreEqual("xyz", metadata[0].Key); + } + + [Test] + public void Indexer_Set() + { + var metadata = CreateMetadata(); + var entry = new Metadata.Entry("new-key", "new-value"); + + metadata[1] = entry; + Assert.AreEqual(entry, metadata[1]); + } + + [Test] + public void Clear() + { + var metadata = CreateMetadata(); + metadata.Clear(); + Assert.AreEqual(0, metadata.Count); + } + + [Test] + public void Contains() + { + var metadata = CreateMetadata(); + Assert.IsTrue(metadata.Contains(metadata[0])); + Assert.IsFalse(metadata.Contains(new Metadata.Entry("new-key", "new-value"))); + } + + [Test] + public void CopyTo() + { + var metadata = CreateMetadata(); + var array = new Metadata.Entry[metadata.Count + 1]; + + metadata.CopyTo(array, 1); + Assert.AreEqual(default(Metadata.Entry), array[0]); + Assert.AreEqual(metadata[0], array[1]); + } + + [Test] + public void IEnumerableGetEnumerator() + { + var metadata = CreateMetadata(); + var enumerator = (metadata as System.Collections.IEnumerable).GetEnumerator(); + + int i = 0; + while (enumerator.MoveNext()) + { + Assert.AreEqual(metadata[i], enumerator.Current); + i++; + } + } + + [Test] + public void FreezeMakesReadOnly() + { + var entry = new Metadata.Entry("new-key", "new-value"); + var metadata = CreateMetadata().Freeze(); + + Assert.IsTrue(metadata.IsReadOnly); + Assert.Throws<InvalidOperationException>(() => metadata.Insert(0, entry)); + Assert.Throws<InvalidOperationException>(() => metadata.RemoveAt(0)); + Assert.Throws<InvalidOperationException>(() => metadata[0] = entry); + Assert.Throws<InvalidOperationException>(() => metadata.Add(entry)); + Assert.Throws<InvalidOperationException>(() => metadata.Add("new-key", "new-value")); + Assert.Throws<InvalidOperationException>(() => metadata.Add("new-key-bin", new byte[] { 0xaa })); + Assert.Throws<InvalidOperationException>(() => metadata.Clear()); + Assert.Throws<InvalidOperationException>(() => metadata.Remove(metadata[0])); + } + + private Metadata CreateMetadata() + { + return new Metadata + { + { "abc", "abc-value" }, + { "xyz", "xyz-value" }, + }; + } } } diff --git a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs index 073c502daf..af55cb0566 100644 --- a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs +++ b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs @@ -59,6 +59,8 @@ namespace Grpc.Core.Tests [Test] public void CompletionQueueCreateDestroyBenchmark() { + GrpcEnvironment.AddRef(); // completion queue requires gRPC environment being initialized. + BenchmarkUtil.RunBenchmark( 10, 10, () => @@ -66,6 +68,8 @@ namespace Grpc.Core.Tests CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.Create(); cq.Dispose(); }); + + GrpcEnvironment.Release(); } /// <summary> diff --git a/src/csharp/Grpc.Core.Tests/PerformanceTest.cs b/src/csharp/Grpc.Core.Tests/PerformanceTest.cs index 5516cd3377..6815839992 100644 --- a/src/csharp/Grpc.Core.Tests/PerformanceTest.cs +++ b/src/csharp/Grpc.Core.Tests/PerformanceTest.cs @@ -67,10 +67,10 @@ namespace Grpc.Core.Tests channel.ShutdownAsync().Wait(); server.ShutdownAsync().Wait(); } - - // Test attribute commented out to prevent running as part of the default test suite. - //[Test] - //[Category("Performance")] + + [Test] + [Category("Performance")] + [Ignore("Prevent running on Jenkins")] public void UnaryCallPerformance() { var profiler = new BasicProfiler(); diff --git a/src/csharp/Grpc.Core.Tests/SanityTest.cs b/src/csharp/Grpc.Core.Tests/SanityTest.cs new file mode 100644 index 0000000000..343ab1e85a --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/SanityTest.cs @@ -0,0 +1,125 @@ +#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.IO; +using System.Reflection; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class SanityTest + { + /// <summary> + /// Because we depend on a native library, sometimes when things go wrong, the + /// entire NUnit test process crashes. To be able to track down problems better, + /// the NUnit tests are run by run_tests.py script in a separate process per test class. + /// The list of tests to run is stored in src/csharp/tests.json. + /// This test checks that the tests.json file is up to date by discovering all the + /// existing NUnit tests in all test assemblies and comparing to contents of tests.json. + /// </summary> + [Test] + public void TestsJsonUpToDate() + { + var testClasses = DiscoverAllTestClasses(); + string testsJson = GetTestsJson(); + + // we don't have a JSON parser at hand, but check that the test class + // name is contained in the file instead. + foreach (var className in testClasses) { + Assert.IsTrue(testsJson.Contains(className), + string.Format("Test class \"{0}\" is missing in C# tests.json file", className)); + } + } + + /// <summary> + /// Gets list of all test classes obtained by inspecting all the test assemblies. + /// </summary> + private List<string> DiscoverAllTestClasses() + { + var assemblies = GetTestAssemblies(); + + var testClasses = new List<string>(); + foreach (var assembly in assemblies) + { + foreach (var t in assembly.GetTypes()) + { + foreach (var m in t.GetMethods()) + { + var attributes = m.GetCustomAttributes(typeof(NUnit.Framework.TestAttribute), true); + if (attributes.Length > 0) + { + testClasses.Add(t.FullName); + break; + } + + } + } + } + testClasses.Sort(); + return testClasses; + } + + private string GetTestsJson() + { + var assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var testsJsonFile = Path.Combine(assemblyDir, "..", "..", "..", "tests.json"); + + return File.ReadAllText(testsJsonFile); + } + + private List<Assembly> GetTestAssemblies() + { + var result = new List<Assembly>(); + var executingAssembly = Assembly.GetExecutingAssembly(); + + result.Add(executingAssembly); + + var otherAssemblies = new[] { + "Grpc.Examples.Tests", + "Grpc.HealthCheck.Tests", + "Grpc.IntegrationTesting" + }; + foreach (var assemblyName in otherAssemblies) + { + var location = executingAssembly.Location.Replace("Grpc.Core.Tests", assemblyName); + result.Add(Assembly.LoadFrom(location)); + } + return result; + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/ShutdownTest.cs b/src/csharp/Grpc.Core.Tests/ShutdownTest.cs index a2be7ddd5e..10d666d109 100644 --- a/src/csharp/Grpc.Core.Tests/ShutdownTest.cs +++ b/src/csharp/Grpc.Core.Tests/ShutdownTest.cs @@ -61,17 +61,20 @@ namespace Grpc.Core.Tests } [Test] - public async Task AbandonedCall() + public async Task AbandonedCall_ServerKillAsync() { + var readyToShutdown = new TaskCompletionSource<object>(); helper.DuplexStreamingHandler = new DuplexStreamingServerMethod<string, string>(async (requestStream, responseStream, context) => { + readyToShutdown.SetResult(null); await requestStream.ToListAsync(); }); - var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall(new CallOptions(deadline: DateTime.UtcNow.AddMilliseconds(1)))); + var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall()); + await readyToShutdown.Task; // make sure handler is running - channel.ShutdownAsync().Wait(); - server.ShutdownAsync().Wait(); + await channel.ShutdownAsync(); // channel.ShutdownAsync() works even if there's a pending call. + await server.KillAsync(); // server.ShutdownAsync() would hang waiting for the call to finish. } } } |