aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/csharp
diff options
context:
space:
mode:
Diffstat (limited to 'src/csharp')
-rw-r--r--src/csharp/Grpc.Core.Tests/CallOptionsTest.cs88
-rw-r--r--src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs2
-rw-r--r--src/csharp/Grpc.Core.Tests/ClientServerTest.cs54
-rw-r--r--src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs32
-rw-r--r--src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj2
-rw-r--r--src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs105
-rw-r--r--src/csharp/Grpc.Core.Tests/MetadataTest.cs113
-rw-r--r--src/csharp/Grpc.Core.Tests/PInvokeTest.cs4
-rw-r--r--src/csharp/Grpc.Core.Tests/PerformanceTest.cs8
-rw-r--r--src/csharp/Grpc.Core.Tests/SanityTest.cs125
-rw-r--r--src/csharp/Grpc.Core.Tests/ShutdownTest.cs11
-rw-r--r--src/csharp/Grpc.Core/CallOptions.cs1
-rw-r--r--src/csharp/Grpc.Core/Internal/AsyncCallBase.cs14
-rw-r--r--src/csharp/Grpc.Core/Internal/ServerCallHandler.cs2
-rw-r--r--src/csharp/Grpc.Core/Internal/Timespec.cs23
-rw-r--r--src/csharp/Grpc.Core/Server.cs2
-rw-r--r--src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj4
-rw-r--r--src/csharp/Grpc.IntegrationTesting/HeaderInterceptorTest.cs113
-rw-r--r--src/csharp/Grpc.IntegrationTesting/InteropClient.cs119
-rw-r--r--src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs22
-rw-r--r--src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs54
-rw-r--r--src/csharp/Grpc.IntegrationTesting/RunnerClientServerTest.cs7
-rw-r--r--src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs39
-rw-r--r--src/csharp/Grpc.IntegrationTesting/packages.config1
-rw-r--r--src/csharp/tests.json45
25 files changed, 842 insertions, 148 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.
}
}
}
diff --git a/src/csharp/Grpc.Core/CallOptions.cs b/src/csharp/Grpc.Core/CallOptions.cs
index c0f94c63c2..1fda80cb90 100644
--- a/src/csharp/Grpc.Core/CallOptions.cs
+++ b/src/csharp/Grpc.Core/CallOptions.cs
@@ -184,6 +184,7 @@ namespace Grpc.Core
{
Preconditions.CheckArgument(!newOptions.cancellationToken.CanBeCanceled,
"Cannot propagate cancellation token from parent call. The cancellation token has already been set to a non-default value.");
+ newOptions.cancellationToken = propagationToken.ParentCancellationToken;
}
}
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
index 953f61aa1e..92f8d77e85 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs
@@ -238,20 +238,6 @@ namespace Grpc.Core.Internal
}
}
- protected Exception TrySerialize(TWrite msg, out byte[] payload)
- {
- try
- {
- payload = serializer(msg);
- return null;
- }
- catch (Exception e)
- {
- payload = null;
- return e;
- }
- }
-
protected Exception TryDeserialize(byte[] payload, out TRead msg)
{
using (Profilers.ForCurrentThread().NewScope("AsyncCallBase.TryDeserialize"))
diff --git a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
index 0ade701a53..de66759b94 100644
--- a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
+++ b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs
@@ -284,7 +284,7 @@ namespace Grpc.Core.Internal
var finishedTask = asyncCall.ServerSideCallAsync();
var responseStream = new ServerResponseStream<byte[], byte[]>(asyncCall);
- await responseStream.WriteStatusAsync(new Status(StatusCode.Unimplemented, "No such method."), Metadata.Empty).ConfigureAwait(false);
+ await responseStream.WriteStatusAsync(new Status(StatusCode.Unimplemented, ""), Metadata.Empty).ConfigureAwait(false);
await finishedTask.ConfigureAwait(false);
}
}
diff --git a/src/csharp/Grpc.Core/Internal/Timespec.cs b/src/csharp/Grpc.Core/Internal/Timespec.cs
index 38fc067d9f..3031175c8a 100644
--- a/src/csharp/Grpc.Core/Internal/Timespec.cs
+++ b/src/csharp/Grpc.Core/Internal/Timespec.cs
@@ -63,20 +63,18 @@ namespace Grpc.Core.Internal
[DllImport("grpc_csharp_ext.dll")]
static extern int gprsharp_sizeof_timespec();
- public Timespec(IntPtr tv_sec, int tv_nsec) : this(tv_sec, tv_nsec, GPRClockType.Realtime)
+ public Timespec(long tv_sec, int tv_nsec) : this(tv_sec, tv_nsec, GPRClockType.Realtime)
{
}
- public Timespec(IntPtr tv_sec, int tv_nsec, GPRClockType clock_type)
+ public Timespec(long tv_sec, int tv_nsec, GPRClockType clock_type)
{
this.tv_sec = tv_sec;
this.tv_nsec = tv_nsec;
this.clock_type = clock_type;
}
- // NOTE: on linux 64bit sizeof(gpr_timespec) = 16, on windows 32bit sizeof(gpr_timespec) = 8
- // so IntPtr seems to have the right size to work on both.
- private System.IntPtr tv_sec;
+ private long tv_sec;
private int tv_nsec;
private GPRClockType clock_type;
@@ -116,7 +114,7 @@ namespace Grpc.Core.Internal
/// <summary>
/// Seconds since unix epoch.
/// </summary>
- public IntPtr TimevalSeconds
+ public long TimevalSeconds
{
get
{
@@ -176,18 +174,18 @@ namespace Grpc.Core.Internal
{
// convert nanos to ticks, round up to the nearest tick
long ticksFromNanos = tv_nsec / NanosPerTick + ((tv_nsec % NanosPerTick != 0) ? 1 : 0);
- long ticksTotal = checked(tv_sec.ToInt64() * TicksPerSecond + ticksFromNanos);
+ long ticksTotal = checked(tv_sec * TicksPerSecond + ticksFromNanos);
return UnixEpoch.AddTicks(ticksTotal);
}
catch (OverflowException)
{
// ticks out of long range
- return tv_sec.ToInt64() > 0 ? DateTime.MaxValue : DateTime.MinValue;
+ return tv_sec > 0 ? DateTime.MaxValue : DateTime.MinValue;
}
catch (ArgumentOutOfRangeException)
{
// resulting date time would be larger than MaxValue
- return tv_sec.ToInt64() > 0 ? DateTime.MaxValue : DateTime.MinValue;
+ return tv_sec > 0 ? DateTime.MaxValue : DateTime.MinValue;
}
}
@@ -226,12 +224,7 @@ namespace Grpc.Core.Internal
seconds--;
nanos += (int)NanosPerSecond;
}
- // new IntPtr possibly throws OverflowException
- return new Timespec(new IntPtr(seconds), nanos);
- }
- catch (OverflowException)
- {
- return dateTime > UnixEpoch ? Timespec.InfFuture : Timespec.InfPast;
+ return new Timespec(seconds, nanos);
}
catch (ArgumentOutOfRangeException)
{
diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs
index 0fadabe554..d120f95fdf 100644
--- a/src/csharp/Grpc.Core/Server.cs
+++ b/src/csharp/Grpc.Core/Server.cs
@@ -171,6 +171,8 @@ namespace Grpc.Core
handle.CancelAllCalls();
await shutdownTcs.Task.ConfigureAwait(false);
DisposeHandle();
+
+ await Task.Run(() => GrpcEnvironment.Release()).ConfigureAwait(false);
}
internal void AddCallReference(object call)
diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
index 012de45524..b0d920a34c 100644
--- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
+++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj
@@ -41,6 +41,9 @@
<Reference Include="CommandLine">
<HintPath>..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll</HintPath>
</Reference>
+ <Reference Include="Moq">
+ <HintPath>..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll</HintPath>
+ </Reference>
<Reference Include="nunit.framework">
<HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
</Reference>
@@ -83,6 +86,7 @@
<Compile Include="..\Grpc.Core\Version.cs">
<Link>Version.cs</Link>
</Compile>
+ <Compile Include="HeaderInterceptorTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Empty.cs" />
<Compile Include="Messages.cs" />
diff --git a/src/csharp/Grpc.IntegrationTesting/HeaderInterceptorTest.cs b/src/csharp/Grpc.IntegrationTesting/HeaderInterceptorTest.cs
new file mode 100644
index 0000000000..1d758b7540
--- /dev/null
+++ b/src/csharp/Grpc.IntegrationTesting/HeaderInterceptorTest.cs
@@ -0,0 +1,113 @@
+#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.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Grpc.Core;
+using Grpc.Core.Utils;
+using Grpc.Testing;
+using NUnit.Framework;
+
+namespace Grpc.IntegrationTesting
+{
+ public class HeaderInterceptorTest
+ {
+ const string Host = "localhost";
+ Server server;
+ Channel channel;
+ TestService.TestServiceClient client;
+
+ [TestFixtureSetUp]
+ public void Init()
+ {
+ server = new Server
+ {
+ Services = { TestService.BindService(new TestServiceImpl()) },
+ Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } }
+ };
+ server.Start();
+
+ channel = new Channel(Host, server.Ports.Single().BoundPort, ChannelCredentials.Insecure);
+ client = TestService.NewClient(channel);
+ }
+
+ [TestFixtureTearDown]
+ public void Cleanup()
+ {
+ channel.ShutdownAsync().Wait();
+ server.ShutdownAsync().Wait();
+ }
+
+ [Test]
+ public async Task HeaderInterceptor_CreateMetadata()
+ {
+ var key = "x-grpc-test-echo-initial";
+ client.HeaderInterceptor = new HeaderInterceptor((method, metadata) =>
+ {
+ metadata.Add(key, "ABC");
+ });
+
+ var call = client.UnaryCallAsync(new SimpleRequest());
+ await call;
+
+ var responseHeaders = await call.ResponseHeadersAsync;
+ Assert.AreEqual("ABC", responseHeaders.First((entry) => entry.Key == key).Value);
+ }
+
+ [Test]
+ public async Task HeaderInterceptor_AppendMetadata()
+ {
+ var initialKey = "x-grpc-test-echo-initial";
+ var trailingKey = "x-grpc-test-echo-trailing-bin";
+
+ client.HeaderInterceptor = new HeaderInterceptor((method, metadata) =>
+ {
+ metadata.Add(initialKey, "ABC");
+ });
+
+ var headers = new Metadata
+ {
+ { trailingKey, new byte[] {0xaa} }
+ };
+ var call = client.UnaryCallAsync(new SimpleRequest(), headers: headers);
+ await call;
+
+ var responseHeaders = await call.ResponseHeadersAsync;
+ Assert.AreEqual("ABC", responseHeaders.First((entry) => entry.Key == initialKey).Value);
+ CollectionAssert.AreEqual(new byte[] {0xaa}, call.GetTrailers().First((entry) => entry.Key == trailingKey).ValueBytes);
+ }
+ }
+}
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
index 5eec11abf7..b0e33e49f7 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs
@@ -34,6 +34,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
@@ -130,8 +131,7 @@ namespace Grpc.IntegrationTesting
};
}
var channel = new Channel(options.ServerHost, options.ServerPort, credentials, channelOptions);
- TestService.TestServiceClient client = new TestService.TestServiceClient(channel);
- await RunTestCaseAsync(client, options);
+ await RunTestCaseAsync(channel, options);
await channel.ShutdownAsync();
}
@@ -159,8 +159,9 @@ namespace Grpc.IntegrationTesting
return credentials;
}
- private async Task RunTestCaseAsync(TestService.TestServiceClient client, ClientOptions options)
+ private async Task RunTestCaseAsync(Channel channel, ClientOptions options)
{
+ var client = new TestService.TestServiceClient(channel);
switch (options.TestCase)
{
case "empty_unary":
@@ -202,8 +203,14 @@ namespace Grpc.IntegrationTesting
case "timeout_on_sleeping_server":
await RunTimeoutOnSleepingServerAsync(client);
break;
- case "benchmark_empty_unary":
- RunBenchmarkEmptyUnary(client);
+ case "custom_metadata":
+ await RunCustomMetadataAsync(client);
+ break;
+ case "status_code_and_message":
+ await RunStatusCodeAndMessageAsync(client);
+ break;
+ case "unimplemented_method":
+ RunUnimplementedMethod(new UnimplementedService.UnimplementedServiceClient(channel));
break;
default:
throw new ArgumentException("Unknown test case " + options.TestCase);
@@ -227,7 +234,6 @@ namespace Grpc.IntegrationTesting
ResponseSize = 314159,
Payload = CreateZerosPayload(271828)
};
-
var response = client.UnaryCall(request);
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type);
@@ -493,11 +499,95 @@ namespace Grpc.IntegrationTesting
Console.WriteLine("Passed!");
}
- // This is not an official interop test, but it's useful.
- public static void RunBenchmarkEmptyUnary(TestService.ITestServiceClient client)
+ public static async Task RunCustomMetadataAsync(TestService.ITestServiceClient client)
{
- BenchmarkUtil.RunBenchmark(10000, 10000,
- () => { client.EmptyCall(new Empty()); });
+ Console.WriteLine("running custom_metadata");
+ {
+ // step 1: test unary call
+ var request = new SimpleRequest
+ {
+ ResponseType = PayloadType.COMPRESSABLE,
+ ResponseSize = 314159,
+ Payload = CreateZerosPayload(271828)
+ };
+
+ var call = client.UnaryCallAsync(request, headers: CreateTestMetadata());
+ await call.ResponseAsync;
+
+ var responseHeaders = await call.ResponseHeadersAsync;
+ var responseTrailers = call.GetTrailers();
+
+ Assert.AreEqual("test_initial_metadata_value", responseHeaders.First((entry) => entry.Key == "x-grpc-test-echo-initial").Value);
+ CollectionAssert.AreEqual(new byte[] { 0xab, 0xab, 0xab }, responseTrailers.First((entry) => entry.Key == "x-grpc-test-echo-trailing-bin").ValueBytes);
+ }
+
+ {
+ // step 2: test full duplex call
+ var request = new StreamingOutputCallRequest
+ {
+ ResponseType = PayloadType.COMPRESSABLE,
+ ResponseParameters = { new ResponseParameters { Size = 31415 } },
+ Payload = CreateZerosPayload(27182)
+ };
+
+ var call = client.FullDuplexCall(headers: CreateTestMetadata());
+ var responseHeaders = await call.ResponseHeadersAsync;
+
+ await call.RequestStream.WriteAsync(request);
+ await call.RequestStream.CompleteAsync();
+ await call.ResponseStream.ToListAsync();
+
+ var responseTrailers = call.GetTrailers();
+
+ Assert.AreEqual("test_initial_metadata_value", responseHeaders.First((entry) => entry.Key == "x-grpc-test-echo-initial").Value);
+ CollectionAssert.AreEqual(new byte[] { 0xab, 0xab, 0xab }, responseTrailers.First((entry) => entry.Key == "x-grpc-test-echo-trailing-bin").ValueBytes);
+ }
+
+ Console.WriteLine("Passed!");
+ }
+
+ public static async Task RunStatusCodeAndMessageAsync(TestService.ITestServiceClient client)
+ {
+ Console.WriteLine("running status_code_and_message");
+ var echoStatus = new EchoStatus
+ {
+ Code = 2,
+ Message = "test status message"
+ };
+
+ {
+ // step 1: test unary call
+ var request = new SimpleRequest { ResponseStatus = echoStatus };
+
+ var e = Assert.Throws<RpcException>(() => client.UnaryCall(request));
+ Assert.AreEqual(StatusCode.Unknown, e.Status.StatusCode);
+ Assert.AreEqual(echoStatus.Message, e.Status.Detail);
+ }
+
+ {
+ // step 2: test full duplex call
+ var request = new StreamingOutputCallRequest { ResponseStatus = echoStatus };
+
+ var call = client.FullDuplexCall();
+ await call.RequestStream.WriteAsync(request);
+ await call.RequestStream.CompleteAsync();
+
+ var e = Assert.Throws<RpcException>(async () => await call.ResponseStream.ToListAsync());
+ Assert.AreEqual(StatusCode.Unknown, e.Status.StatusCode);
+ Assert.AreEqual(echoStatus.Message, e.Status.Detail);
+ }
+
+ Console.WriteLine("Passed!");
+ }
+
+ public static void RunUnimplementedMethod(UnimplementedService.IUnimplementedServiceClient client)
+ {
+ Console.WriteLine("running unimplemented_method");
+ var e = Assert.Throws<RpcException>(() => client.UnimplementedCall(new Empty()));
+
+ Assert.AreEqual(StatusCode.Unimplemented, e.Status.StatusCode);
+ Assert.AreEqual("", e.Status.Detail);
+ Console.WriteLine("Passed!");
}
private static Payload CreateZerosPayload(int size)
@@ -516,5 +606,14 @@ namespace Grpc.IntegrationTesting
Assert.IsTrue(email.Length > 0); // spec requires nonempty client email.
return email;
}
+
+ private static Metadata CreateTestMetadata()
+ {
+ return new Metadata
+ {
+ {"x-grpc-test-echo-initial", "test_initial_metadata_value"},
+ {"x-grpc-test-echo-trailing-bin", new byte[] {0xab, 0xab, 0xab}}
+ };
+ }
}
}
diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
index 837ae74c45..18168f9970 100644
--- a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
+++ b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs
@@ -128,9 +128,29 @@ namespace Grpc.IntegrationTesting
}
[Test]
- public async Task TimeoutOnSleepingServerAsync()
+ public async Task TimeoutOnSleepingServer()
{
await InteropClient.RunTimeoutOnSleepingServerAsync(client);
}
+
+ [Test]
+ public async Task CustomMetadata()
+ {
+ await InteropClient.RunCustomMetadataAsync(client);
+ }
+
+ [Test]
+ [Ignore("TODO: see #4427")]
+ public async Task StatusCodeAndMessage()
+ {
+ await InteropClient.RunStatusCodeAndMessageAsync(client);
+ }
+
+ [Test]
+ [Ignore("TODO: see #4427")]
+ public void UnimplementedMethod()
+ {
+ InteropClient.RunUnimplementedMethod(UnimplementedService.NewClient(channel));
+ }
}
}
diff --git a/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs
index 35230f48c1..1c8bfed1f6 100644
--- a/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs
+++ b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs
@@ -40,6 +40,7 @@ using System.Threading.Tasks;
using Grpc.Core;
using Grpc.Core.Utils;
using Grpc.Testing;
+using Moq;
using NUnit.Framework;
namespace Grpc.IntegrationTesting
@@ -50,37 +51,37 @@ namespace Grpc.IntegrationTesting
Server server;
Channel channel;
TestService.ITestServiceClient client;
+ List<ChannelOption> options;
+ Mock<TestService.ITestService> serviceMock;
+ AsyncAuthInterceptor asyncAuthInterceptor;
- [TestFixtureSetUp]
+ [SetUp]
public void Init()
{
- var serverCredentials = new SslServerCredentials(new[] { new KeyCertificatePair(File.ReadAllText(TestCredentials.ServerCertChainPath), File.ReadAllText(TestCredentials.ServerPrivateKeyPath)) });
+ serviceMock = new Mock<TestService.ITestService>();
+ serviceMock.Setup(m => m.UnaryCall(It.IsAny<SimpleRequest>(), It.IsAny<ServerCallContext>()))
+ .Returns(new Func<SimpleRequest, ServerCallContext, Task<SimpleResponse>>(UnaryCallHandler));
+
server = new Server
{
- Services = { TestService.BindService(new TestServiceImpl()) },
- Ports = { { Host, ServerPort.PickUnused, serverCredentials } }
+ Services = { TestService.BindService(serviceMock.Object) },
+ Ports = { { Host, ServerPort.PickUnused, TestCredentials.CreateSslServerCredentials() } }
};
server.Start();
- var options = new List<ChannelOption>
+ options = new List<ChannelOption>
{
new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride)
};
- var asyncAuthInterceptor = new AsyncAuthInterceptor(async (context, metadata) =>
+ asyncAuthInterceptor = new AsyncAuthInterceptor(async (context, metadata) =>
{
- await Task.Delay(100); // make sure the operation is asynchronous.
+ await Task.Delay(100).ConfigureAwait(false); // make sure the operation is asynchronous.
metadata.Add("authorization", "SECRET_TOKEN");
});
-
- var clientCredentials = ChannelCredentials.Create(
- new SslCredentials(File.ReadAllText(TestCredentials.ClientCertAuthorityPath)),
- CallCredentials.FromInterceptor(asyncAuthInterceptor));
- channel = new Channel(Host, server.Ports.Single().BoundPort, clientCredentials, options);
- client = TestService.NewClient(channel);
}
- [TestFixtureTearDown]
+ [TearDown]
public void Cleanup()
{
channel.ShutdownAsync().Wait();
@@ -90,8 +91,29 @@ namespace Grpc.IntegrationTesting
[Test]
public void MetadataCredentials()
{
- var response = client.UnaryCall(new SimpleRequest { ResponseSize = 10 });
- Assert.AreEqual(10, response.Payload.Body.Length);
+ var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(),
+ CallCredentials.FromInterceptor(asyncAuthInterceptor));
+ channel = new Channel(Host, server.Ports.Single().BoundPort, channelCredentials, options);
+ client = TestService.NewClient(channel);
+
+ client.UnaryCall(new SimpleRequest {});
+ }
+
+ [Test]
+ public void MetadataCredentials_PerCall()
+ {
+ channel = new Channel(Host, server.Ports.Single().BoundPort, TestCredentials.CreateSslCredentials(), options);
+ client = TestService.NewClient(channel);
+
+ var callCredentials = CallCredentials.FromInterceptor(asyncAuthInterceptor);
+ client.UnaryCall(new SimpleRequest { }, new CallOptions(credentials: callCredentials));
+ }
+
+ private Task<SimpleResponse> UnaryCallHandler(SimpleRequest request, ServerCallContext context)
+ {
+ var authToken = context.RequestHeaders.First((entry) => entry.Key == "authorization").Value;
+ Assert.AreEqual("SECRET_TOKEN", authToken);
+ return Task.FromResult(new SimpleResponse());
}
}
}
diff --git a/src/csharp/Grpc.IntegrationTesting/RunnerClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/RunnerClientServerTest.cs
index 2b51526c88..3dd91b7948 100644
--- a/src/csharp/Grpc.IntegrationTesting/RunnerClientServerTest.cs
+++ b/src/csharp/Grpc.IntegrationTesting/RunnerClientServerTest.cs
@@ -75,9 +75,10 @@ namespace Grpc.IntegrationTesting
serverRunner.StopAsync().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 async Task ClientServerRunner()
{
var config = new ClientConfig
diff --git a/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs b/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs
index c5bfcf08c0..5a1b4cf319 100644
--- a/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs
+++ b/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs
@@ -33,6 +33,7 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Google.Protobuf;
@@ -51,14 +52,20 @@ namespace Grpc.Testing
return Task.FromResult(new Empty());
}
- public Task<SimpleResponse> UnaryCall(SimpleRequest request, ServerCallContext context)
+ public async Task<SimpleResponse> UnaryCall(SimpleRequest request, ServerCallContext context)
{
+ await EnsureEchoMetadataAsync(context);
+ EnsureEchoStatus(request.ResponseStatus, context);
+
var response = new SimpleResponse { Payload = CreateZerosPayload(request.ResponseSize) };
- return Task.FromResult(response);
+ return response;
}
public async Task StreamingOutputCall(StreamingOutputCallRequest request, IServerStreamWriter<StreamingOutputCallResponse> responseStream, ServerCallContext context)
{
+ await EnsureEchoMetadataAsync(context);
+ EnsureEchoStatus(request.ResponseStatus, context);
+
foreach (var responseParam in request.ResponseParameters)
{
var response = new StreamingOutputCallResponse { Payload = CreateZerosPayload(responseParam.Size) };
@@ -68,6 +75,8 @@ namespace Grpc.Testing
public async Task<StreamingInputCallResponse> StreamingInputCall(IAsyncStreamReader<StreamingInputCallRequest> requestStream, ServerCallContext context)
{
+ await EnsureEchoMetadataAsync(context);
+
int sum = 0;
await requestStream.ForEachAsync(async request =>
{
@@ -78,8 +87,11 @@ namespace Grpc.Testing
public async Task FullDuplexCall(IAsyncStreamReader<StreamingOutputCallRequest> requestStream, IServerStreamWriter<StreamingOutputCallResponse> responseStream, ServerCallContext context)
{
+ await EnsureEchoMetadataAsync(context);
+
await requestStream.ForEachAsync(async request =>
{
+ EnsureEchoStatus(request.ResponseStatus, context);
foreach (var responseParam in request.ResponseParameters)
{
var response = new StreamingOutputCallResponse { Payload = CreateZerosPayload(responseParam.Size) };
@@ -97,5 +109,28 @@ namespace Grpc.Testing
{
return new Payload { Body = ByteString.CopyFrom(new byte[size]) };
}
+
+ private static async Task EnsureEchoMetadataAsync(ServerCallContext context)
+ {
+ var echoInitialList = context.RequestHeaders.Where((entry) => entry.Key == "x-grpc-test-echo-initial").ToList();
+ if (echoInitialList.Any()) {
+ var entry = echoInitialList.Single();
+ await context.WriteResponseHeadersAsync(new Metadata { entry });
+ }
+
+ var echoTrailingList = context.RequestHeaders.Where((entry) => entry.Key == "x-grpc-test-echo-trailing-bin").ToList();
+ if (echoTrailingList.Any()) {
+ context.ResponseTrailers.Add(echoTrailingList.Single());
+ }
+ }
+
+ private static void EnsureEchoStatus(EchoStatus responseStatus, ServerCallContext context)
+ {
+ if (responseStatus != null)
+ {
+ var statusCode = (StatusCode)responseStatus.Code;
+ context.Status = new Status(statusCode, responseStatus.Message);
+ }
+ }
}
}
diff --git a/src/csharp/Grpc.IntegrationTesting/packages.config b/src/csharp/Grpc.IntegrationTesting/packages.config
index bdb3dadf44..5c59af1b7d 100644
--- a/src/csharp/Grpc.IntegrationTesting/packages.config
+++ b/src/csharp/Grpc.IntegrationTesting/packages.config
@@ -11,6 +11,7 @@
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" />
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
+ <package id="Moq" version="4.2.1510.2205" targetFramework="net45" />
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
<package id="NUnit" version="2.6.4" targetFramework="net45" />
</packages> \ No newline at end of file
diff --git a/src/csharp/tests.json b/src/csharp/tests.json
new file mode 100644
index 0000000000..4aa93668ad
--- /dev/null
+++ b/src/csharp/tests.json
@@ -0,0 +1,45 @@
+{
+ "assemblies": [
+ "Grpc.Core.Tests",
+ "Grpc.Examples.Tests",
+ "Grpc.HealthCheck.Tests",
+ "Grpc.IntegrationTesting"
+ ],
+ "tests": [
+ "Grpc.Core.Internal.Tests.AsyncCallTest",
+ "Grpc.Core.Internal.Tests.ChannelArgsSafeHandleTest",
+ "Grpc.Core.Internal.Tests.CompletionQueueEventTest",
+ "Grpc.Core.Internal.Tests.CompletionQueueSafeHandleTest",
+ "Grpc.Core.Internal.Tests.MetadataArraySafeHandleTest",
+ "Grpc.Core.Internal.Tests.TimespecTest",
+ "Grpc.Core.Tests.CallCredentialsTest",
+ "Grpc.Core.Tests.CallOptionsTest",
+ "Grpc.Core.Tests.ChannelCredentialsTest",
+ "Grpc.Core.Tests.ChannelOptionsTest",
+ "Grpc.Core.Tests.ChannelTest",
+ "Grpc.Core.Tests.ClientServerTest",
+ "Grpc.Core.Tests.CompressionTest",
+ "Grpc.Core.Tests.ContextPropagationTest",
+ "Grpc.Core.Tests.GrpcEnvironmentTest",
+ "Grpc.Core.Tests.MarshallingErrorsTest",
+ "Grpc.Core.Tests.MetadataTest",
+ "Grpc.Core.Tests.NUnitVersionTest",
+ "Grpc.Core.Tests.PerformanceTest",
+ "Grpc.Core.Tests.PInvokeTest",
+ "Grpc.Core.Tests.ResponseHeadersTest",
+ "Grpc.Core.Tests.SanityTest",
+ "Grpc.Core.Tests.ServerTest",
+ "Grpc.Core.Tests.ShutdownTest",
+ "Grpc.Core.Tests.TimeoutsTest",
+ "Grpc.Core.Tests.UserAgentStringTest",
+ "Math.Tests.MathClientServerTest",
+ "Grpc.HealthCheck.Tests.HealthClientServerTest",
+ "Grpc.HealthCheck.Tests.HealthServiceImplTest",
+ "Grpc.IntegrationTesting.HeaderInterceptorTest",
+ "Grpc.IntegrationTesting.HistogramTest",
+ "Grpc.IntegrationTesting.InteropClientServerTest",
+ "Grpc.IntegrationTesting.MetadataCredentialsTest",
+ "Grpc.IntegrationTesting.RunnerClientServerTest",
+ "Grpc.IntegrationTesting.SslCredentialsTest"
+ ]
+} \ No newline at end of file