aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/csharp/Grpc.Core.Tests
diff options
context:
space:
mode:
authorGravatar kkm <kkm@smartaction.com>2018-10-07 00:07:30 -0700
committerGravatar kkm <kkm@smartaction.com>2018-10-07 00:07:30 -0700
commit51e41ae7d5f513d948a4b38fdb0914f8f1bf4773 (patch)
treefec64dff9787b8c549c8af009e9a73549ac41ec5 /src/csharp/Grpc.Core.Tests
parente3e7e32a7e1698c7937e5bf0bde4914e13131362 (diff)
parent3b26fe7262a9db90dfb69f84ad582d9f71871a5c (diff)
Merge remote-tracking branch 'golden/master' into package-grpc-tools
Diffstat (limited to 'src/csharp/Grpc.Core.Tests')
-rw-r--r--src/csharp/Grpc.Core.Tests/ChannelConnectivityTest.cs20
-rw-r--r--src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs37
-rw-r--r--src/csharp/Grpc.Core.Tests/ContextualMarshallerTest.cs119
-rw-r--r--src/csharp/Grpc.Core.Tests/FakeCredentials.cs10
-rw-r--r--src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs75
-rw-r--r--src/csharp/Grpc.Core.Tests/Internal/FakeNativeCall.cs23
-rw-r--r--src/csharp/Grpc.Core.Tests/MarshallerTest.cs105
-rw-r--r--src/csharp/Grpc.Core.Tests/MetadataTest.cs19
8 files changed, 384 insertions, 24 deletions
diff --git a/src/csharp/Grpc.Core.Tests/ChannelConnectivityTest.cs b/src/csharp/Grpc.Core.Tests/ChannelConnectivityTest.cs
index a43040f01a..0834ddadda 100644
--- a/src/csharp/Grpc.Core.Tests/ChannelConnectivityTest.cs
+++ b/src/csharp/Grpc.Core.Tests/ChannelConnectivityTest.cs
@@ -57,19 +57,23 @@ namespace Grpc.Core.Tests
[Test]
public async Task Channel_WaitForStateChangedAsync()
{
- helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
- {
- return Task.FromResult(request);
- });
-
Assert.ThrowsAsync(typeof(TaskCanceledException),
- async () => await channel.WaitForStateChangedAsync(channel.State, DateTime.UtcNow.AddMilliseconds(10)));
+ async () => await channel.WaitForStateChangedAsync(channel.State, DateTime.UtcNow.AddMilliseconds(0)));
var stateChangedTask = channel.WaitForStateChangedAsync(channel.State);
+ await channel.ConnectAsync(DateTime.UtcNow.AddMilliseconds(5000));
+ await stateChangedTask;
+ Assert.AreEqual(ChannelState.Ready, channel.State);
+ }
- await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc");
+ [Test]
+ public async Task Channel_TryWaitForStateChangedAsync()
+ {
+ Assert.IsFalse(await channel.TryWaitForStateChangedAsync(channel.State, DateTime.UtcNow.AddMilliseconds(0)));
- await stateChangedTask;
+ var stateChangedTask = channel.TryWaitForStateChangedAsync(channel.State);
+ await channel.ConnectAsync(DateTime.UtcNow.AddMilliseconds(5000));
+ Assert.IsTrue(await stateChangedTask);
Assert.AreEqual(ChannelState.Ready, channel.State);
}
diff --git a/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs b/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs
index 62cc904a61..843d88bfb6 100644
--- a/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs
+++ b/src/csharp/Grpc.Core.Tests/ChannelCredentialsTest.cs
@@ -17,13 +17,7 @@
#endregion
using System;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-using Grpc.Core;
using Grpc.Core.Internal;
-using Grpc.Core.Utils;
using NUnit.Framework;
namespace Grpc.Core.Tests
@@ -44,9 +38,38 @@ namespace Grpc.Core.Tests
Assert.Throws(typeof(ArgumentNullException), () => ChannelCredentials.Create(null, new FakeCallCredentials()));
Assert.Throws(typeof(ArgumentNullException), () => ChannelCredentials.Create(new FakeChannelCredentials(true), null));
-
+
// forbid composing non-composable
Assert.Throws(typeof(ArgumentException), () => ChannelCredentials.Create(new FakeChannelCredentials(false), new FakeCallCredentials()));
}
+
+ [Test]
+ public void ChannelCredentials_NativeCredentialsAreReused()
+ {
+ // always returning the same native object is critical for subchannel sharing to work with secure channels
+ var creds = new SslCredentials();
+ var nativeCreds1 = creds.GetNativeCredentials();
+ var nativeCreds2 = creds.GetNativeCredentials();
+ Assert.AreSame(nativeCreds1, nativeCreds2);
+ }
+
+ [Test]
+ public void ChannelCredentials_CreateExceptionIsCached()
+ {
+ var creds = new ChannelCredentialsWithCreateNativeThrows();
+ var ex1 = Assert.Throws(typeof(Exception), () => creds.GetNativeCredentials());
+ var ex2 = Assert.Throws(typeof(Exception), () => creds.GetNativeCredentials());
+ Assert.AreSame(ex1, ex2);
+ }
+
+ internal class ChannelCredentialsWithCreateNativeThrows : ChannelCredentials
+ {
+ internal override bool IsComposable => false;
+
+ internal override ChannelCredentialsSafeHandle CreateNativeCredentials()
+ {
+ throw new Exception("Creation of native credentials has failed on purpose.");
+ }
+ }
}
}
diff --git a/src/csharp/Grpc.Core.Tests/ContextualMarshallerTest.cs b/src/csharp/Grpc.Core.Tests/ContextualMarshallerTest.cs
new file mode 100644
index 0000000000..c3aee726f2
--- /dev/null
+++ b/src/csharp/Grpc.Core.Tests/ContextualMarshallerTest.cs
@@ -0,0 +1,119 @@
+#region Copyright notice and license
+
+// Copyright 2018 The gRPC Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Grpc.Core;
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+using NUnit.Framework;
+
+namespace Grpc.Core.Tests
+{
+ public class ContextualMarshallerTest
+ {
+ const string Host = "127.0.0.1";
+
+ MockServiceHelper helper;
+ Server server;
+ Channel channel;
+
+ [SetUp]
+ public void Init()
+ {
+ var contextualMarshaller = new Marshaller<string>(
+ (str, serializationContext) =>
+ {
+ if (str == "UNSERIALIZABLE_VALUE")
+ {
+ // Google.Protobuf throws exception inherited from IOException
+ throw new IOException("Error serializing the message.");
+ }
+ if (str == "SERIALIZE_TO_NULL")
+ {
+ return;
+ }
+ var bytes = System.Text.Encoding.UTF8.GetBytes(str);
+ serializationContext.Complete(bytes);
+ },
+ (deserializationContext) =>
+ {
+ var buffer = deserializationContext.PayloadAsNewBuffer();
+ Assert.AreEqual(buffer.Length, deserializationContext.PayloadLength);
+ var s = System.Text.Encoding.UTF8.GetString(buffer);
+ if (s == "UNPARSEABLE_VALUE")
+ {
+ // Google.Protobuf throws exception inherited from IOException
+ throw new IOException("Error parsing the message.");
+ }
+ return s;
+ });
+ helper = new MockServiceHelper(Host, contextualMarshaller);
+ server = helper.GetServer();
+ server.Start();
+ channel = helper.GetChannel();
+ }
+
+ [TearDown]
+ public void Cleanup()
+ {
+ channel.ShutdownAsync().Wait();
+ server.ShutdownAsync().Wait();
+ }
+
+ [Test]
+ public void UnaryCall()
+ {
+ helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
+ {
+ return Task.FromResult(request);
+ });
+ Assert.AreEqual("ABC", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "ABC"));
+ }
+
+ [Test]
+ public void ResponseParsingError_UnaryResponse()
+ {
+ helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
+ {
+ return Task.FromResult("UNPARSEABLE_VALUE");
+ });
+
+ var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "REQUEST"));
+ Assert.AreEqual(StatusCode.Internal, ex.Status.StatusCode);
+ }
+
+ [Test]
+ public void RequestSerializationError_BlockingUnary()
+ {
+ Assert.Throws<IOException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "UNSERIALIZABLE_VALUE"));
+ }
+
+ [Test]
+ public void SerializationResultIsNull_BlockingUnary()
+ {
+ Assert.Throws<NullReferenceException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "SERIALIZE_TO_NULL"));
+ }
+ }
+}
diff --git a/src/csharp/Grpc.Core.Tests/FakeCredentials.cs b/src/csharp/Grpc.Core.Tests/FakeCredentials.cs
index 7d658576e5..f23c9e9757 100644
--- a/src/csharp/Grpc.Core.Tests/FakeCredentials.cs
+++ b/src/csharp/Grpc.Core.Tests/FakeCredentials.cs
@@ -16,15 +16,7 @@
#endregion
-using System;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-using Grpc.Core;
using Grpc.Core.Internal;
-using Grpc.Core.Utils;
-using NUnit.Framework;
namespace Grpc.Core.Tests
{
@@ -42,7 +34,7 @@ namespace Grpc.Core.Tests
get { return composable; }
}
- internal override ChannelCredentialsSafeHandle ToNativeCredentials()
+ internal override ChannelCredentialsSafeHandle CreateNativeCredentials()
{
return null;
}
diff --git a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs
index 9aab54d2d0..775849d89b 100644
--- a/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs
+++ b/src/csharp/Grpc.Core.Tests/Internal/AsyncCallTest.cs
@@ -107,6 +107,42 @@ namespace Grpc.Core.Internal.Tests
}
[Test]
+ public void AsyncUnary_RequestSerializationExceptionDoesntLeakResources()
+ {
+ string nullRequest = null; // will throw when serializing
+ Assert.Throws(typeof(ArgumentNullException), () => asyncCall.UnaryCallAsync(nullRequest));
+ Assert.AreEqual(0, channel.GetCallReferenceCount());
+ Assert.IsTrue(fakeCall.IsDisposed);
+ }
+
+ [Test]
+ public void AsyncUnary_StartCallFailureDoesntLeakResources()
+ {
+ fakeCall.MakeStartCallFail();
+ Assert.Throws(typeof(InvalidOperationException), () => asyncCall.UnaryCallAsync("request1"));
+ Assert.AreEqual(0, channel.GetCallReferenceCount());
+ Assert.IsTrue(fakeCall.IsDisposed);
+ }
+
+ [Test]
+ public void SyncUnary_RequestSerializationExceptionDoesntLeakResources()
+ {
+ string nullRequest = null; // will throw when serializing
+ Assert.Throws(typeof(ArgumentNullException), () => asyncCall.UnaryCall(nullRequest));
+ Assert.AreEqual(0, channel.GetCallReferenceCount());
+ Assert.IsTrue(fakeCall.IsDisposed);
+ }
+
+ [Test]
+ public void SyncUnary_StartCallFailureDoesntLeakResources()
+ {
+ fakeCall.MakeStartCallFail();
+ Assert.Throws(typeof(InvalidOperationException), () => asyncCall.UnaryCall("request1"));
+ Assert.AreEqual(0, channel.GetCallReferenceCount());
+ Assert.IsTrue(fakeCall.IsDisposed);
+ }
+
+ [Test]
public void ClientStreaming_StreamingReadNotAllowed()
{
asyncCall.ClientStreamingCallAsync();
@@ -328,6 +364,15 @@ namespace Grpc.Core.Internal.Tests
}
[Test]
+ public void ClientStreaming_StartCallFailureDoesntLeakResources()
+ {
+ fakeCall.MakeStartCallFail();
+ Assert.Throws(typeof(InvalidOperationException), () => asyncCall.ClientStreamingCallAsync());
+ Assert.AreEqual(0, channel.GetCallReferenceCount());
+ Assert.IsTrue(fakeCall.IsDisposed);
+ }
+
+ [Test]
public void ServerStreaming_StreamingSendNotAllowed()
{
asyncCall.StartServerStreamingCall("request1");
@@ -402,6 +447,27 @@ namespace Grpc.Core.Internal.Tests
}
[Test]
+ public void ServerStreaming_RequestSerializationExceptionDoesntLeakResources()
+ {
+ string nullRequest = null; // will throw when serializing
+ Assert.Throws(typeof(ArgumentNullException), () => asyncCall.StartServerStreamingCall(nullRequest));
+ Assert.AreEqual(0, channel.GetCallReferenceCount());
+ Assert.IsTrue(fakeCall.IsDisposed);
+
+ var responseStream = new ClientResponseStream<string, string>(asyncCall);
+ var readTask = responseStream.MoveNext();
+ }
+
+ [Test]
+ public void ServerStreaming_StartCallFailureDoesntLeakResources()
+ {
+ fakeCall.MakeStartCallFail();
+ Assert.Throws(typeof(InvalidOperationException), () => asyncCall.StartServerStreamingCall("request1"));
+ Assert.AreEqual(0, channel.GetCallReferenceCount());
+ Assert.IsTrue(fakeCall.IsDisposed);
+ }
+
+ [Test]
public void DuplexStreaming_NoRequestNoResponse_Success()
{
asyncCall.StartDuplexStreamingCall();
@@ -558,6 +624,15 @@ namespace Grpc.Core.Internal.Tests
AssertStreamingResponseError(asyncCall, fakeCall, readTask2, StatusCode.Cancelled);
}
+ [Test]
+ public void DuplexStreaming_StartCallFailureDoesntLeakResources()
+ {
+ fakeCall.MakeStartCallFail();
+ Assert.Throws(typeof(InvalidOperationException), () => asyncCall.StartDuplexStreamingCall());
+ Assert.AreEqual(0, channel.GetCallReferenceCount());
+ Assert.IsTrue(fakeCall.IsDisposed);
+ }
+
ClientSideStatus CreateClientSideStatus(StatusCode statusCode)
{
return new ClientSideStatus(new Status(statusCode, ""), new Metadata());
diff --git a/src/csharp/Grpc.Core.Tests/Internal/FakeNativeCall.cs b/src/csharp/Grpc.Core.Tests/Internal/FakeNativeCall.cs
index 581ac3384b..ef67918dab 100644
--- a/src/csharp/Grpc.Core.Tests/Internal/FakeNativeCall.cs
+++ b/src/csharp/Grpc.Core.Tests/Internal/FakeNativeCall.cs
@@ -31,6 +31,7 @@ namespace Grpc.Core.Internal.Tests
/// </summary>
internal class FakeNativeCall : INativeCall
{
+ private bool shouldStartCallFail;
public IUnaryResponseClientCallback UnaryResponseClientCallback
{
get;
@@ -102,26 +103,31 @@ namespace Grpc.Core.Internal.Tests
public void StartUnary(IUnaryResponseClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
{
+ StartCallMaybeFail();
UnaryResponseClientCallback = callback;
}
public void StartUnary(BatchContextSafeHandle ctx, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
{
+ StartCallMaybeFail();
throw new NotImplementedException();
}
public void StartClientStreaming(IUnaryResponseClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
{
+ StartCallMaybeFail();
UnaryResponseClientCallback = callback;
}
public void StartServerStreaming(IReceivedStatusOnClientCallback callback, byte[] payload, WriteFlags writeFlags, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
{
+ StartCallMaybeFail();
ReceivedStatusOnClientCallback = callback;
}
public void StartDuplexStreaming(IReceivedStatusOnClientCallback callback, MetadataArraySafeHandle metadataArray, CallFlags callFlags)
{
+ StartCallMaybeFail();
ReceivedStatusOnClientCallback = callback;
}
@@ -165,5 +171,22 @@ namespace Grpc.Core.Internal.Tests
{
IsDisposed = true;
}
+
+ /// <summary>
+ /// Emulate CallSafeHandle.CheckOk() failure for all future attempts
+ /// to start a call.
+ /// </summary>
+ public void MakeStartCallFail()
+ {
+ shouldStartCallFail = true;
+ }
+
+ private void StartCallMaybeFail()
+ {
+ if (shouldStartCallFail)
+ {
+ throw new InvalidOperationException("Start call has failed.");
+ }
+ }
}
}
diff --git a/src/csharp/Grpc.Core.Tests/MarshallerTest.cs b/src/csharp/Grpc.Core.Tests/MarshallerTest.cs
new file mode 100644
index 0000000000..97f64a0575
--- /dev/null
+++ b/src/csharp/Grpc.Core.Tests/MarshallerTest.cs
@@ -0,0 +1,105 @@
+#region Copyright notice and license
+
+// Copyright 2018 The gRPC Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Grpc.Core;
+using Grpc.Core.Internal;
+using Grpc.Core.Utils;
+using NUnit.Framework;
+
+namespace Grpc.Core.Tests
+{
+ public class MarshallerTest
+ {
+ [Test]
+ public void ContextualSerializerEmulation()
+ {
+ Func<string, byte[]> simpleSerializer = System.Text.Encoding.UTF8.GetBytes;
+ Func<byte[], string> simpleDeserializer = System.Text.Encoding.UTF8.GetString;
+ var marshaller = new Marshaller<string>(simpleSerializer,
+ simpleDeserializer);
+
+ Assert.AreSame(simpleSerializer, marshaller.Serializer);
+ Assert.AreSame(simpleDeserializer, marshaller.Deserializer);
+
+ // test that emulated contextual serializer and deserializer work
+ string origMsg = "abc";
+ var serializationContext = new FakeSerializationContext();
+ marshaller.ContextualSerializer(origMsg, serializationContext);
+
+ var deserializationContext = new FakeDeserializationContext(serializationContext.Payload);
+ Assert.AreEqual(origMsg, marshaller.ContextualDeserializer(deserializationContext));
+ }
+
+ [Test]
+ public void SimpleSerializerEmulation()
+ {
+ Action<string, SerializationContext> contextualSerializer = (str, context) =>
+ {
+ var bytes = System.Text.Encoding.UTF8.GetBytes(str);
+ context.Complete(bytes);
+ };
+ Func<DeserializationContext, string> contextualDeserializer = (context) =>
+ {
+ return System.Text.Encoding.UTF8.GetString(context.PayloadAsNewBuffer());
+ };
+ var marshaller = new Marshaller<string>(contextualSerializer, contextualDeserializer);
+
+ Assert.AreSame(contextualSerializer, marshaller.ContextualSerializer);
+ Assert.AreSame(contextualDeserializer, marshaller.ContextualDeserializer);
+
+ // test that emulated serializer and deserializer work
+ var origMsg = "abc";
+ var serialized = marshaller.Serializer(origMsg);
+ Assert.AreEqual(origMsg, marshaller.Deserializer(serialized));
+ }
+
+ class FakeSerializationContext : SerializationContext
+ {
+ public byte[] Payload;
+ public override void Complete(byte[] payload)
+ {
+ this.Payload = payload;
+ }
+ }
+
+ class FakeDeserializationContext : DeserializationContext
+ {
+ public byte[] payload;
+
+ public FakeDeserializationContext(byte[] payload)
+ {
+ this.payload = payload;
+ }
+
+ public override int PayloadLength => payload.Length;
+
+ public override byte[] PayloadAsNewBuffer()
+ {
+ return payload;
+ }
+ }
+ }
+}
diff --git a/src/csharp/Grpc.Core.Tests/MetadataTest.cs b/src/csharp/Grpc.Core.Tests/MetadataTest.cs
index 8916731757..d85d7572a6 100644
--- a/src/csharp/Grpc.Core.Tests/MetadataTest.cs
+++ b/src/csharp/Grpc.Core.Tests/MetadataTest.cs
@@ -66,11 +66,30 @@ namespace Grpc.Core.Tests
new Metadata.Entry("0123456789abc", "XYZ");
new Metadata.Entry("-abc", "XYZ");
new Metadata.Entry("a_bc_", "XYZ");
+ new Metadata.Entry("abc.xyz", "XYZ");
+ new Metadata.Entry("abc.xyz-bin", new byte[] {1, 2, 3});
Assert.Throws(typeof(ArgumentException), () => new Metadata.Entry("abc[", "xyz"));
Assert.Throws(typeof(ArgumentException), () => new Metadata.Entry("abc/", "xyz"));
}
[Test]
+ public void KeysAreNormalized_UppercaseKey()
+ {
+ var uppercaseKey = "ABC";
+ var entry = new Metadata.Entry(uppercaseKey, "XYZ");
+ Assert.AreEqual("abc", entry.Key);
+ }
+
+ [Test]
+ public void KeysAreNormalized_LowercaseKey()
+ {
+ var lowercaseKey = "abc";
+ var entry = new Metadata.Entry(lowercaseKey, "XYZ");
+ // no allocation if key already lowercase
+ Assert.AreSame(lowercaseKey, entry.Key);
+ }
+
+ [Test]
public void Entry_ConstructionPreconditions()
{
Assert.Throws(typeof(ArgumentNullException), () => new Metadata.Entry(null, "xyz"));