minimal reproduction

pull/29182/head
Jan Tattermusch 3 years ago
parent f4693018c2
commit 4d02d2e730
  1. 356
      src/csharp/Grpc.Core.Tests/ClientServerTest.cs
  2. 3
      tools/run_tests/run_tests.py

@ -50,361 +50,43 @@ namespace Grpc.Core.Tests
[TearDown]
public void Cleanup()
{
channel.ShutdownAsync().Wait();
server.ShutdownAsync().Wait();
var task1 = channel.ShutdownAsync();
Console.Error.WriteLine("shutting down client");
task1.Wait();
Console.Error.WriteLine("client shutdown finished");
var task2 = server.ShutdownAsync();
Console.Error.WriteLine("shutting down server");
task2.Wait();
Console.Error.WriteLine("server shutdown finished");
}
[Test]
public async Task UnaryCall()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
return Task.FromResult(request);
});
Assert.AreEqual("ABC", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "ABC"));
Assert.AreEqual("ABC", await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "ABC"));
Assert.AreEqual("ABC", await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "ABC").ConfigureAwait(false));
}
[Test]
public void UnaryCall_ServerHandlerThrows()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
throw new Exception("This was thrown on purpose by a test");
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unknown, ex.Status.StatusCode);
var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unknown, ex2.Status.StatusCode);
}
[Test]
public void UnaryCall_ServerHandlerThrowsRpcException()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
throw new RpcException(new Status(StatusCode.Unauthenticated, ""));
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode);
Assert.AreEqual(0, ex.Trailers.Count);
var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode);
Assert.AreEqual(0, ex.Trailers.Count);
}
[Test]
public void UnaryCall_ServerHandlerThrowsRpcExceptionWithTrailers()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
var trailers = new Metadata { {"xyz", "xyz-value"} };
throw new RpcException(new Status(StatusCode.Unauthenticated, ""), trailers);
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode);
Assert.AreEqual(1, ex.Trailers.Count);
Assert.AreEqual("xyz", ex.Trailers[0].Key);
Assert.AreEqual("xyz-value", ex.Trailers[0].Value);
var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode);
Assert.AreEqual(1, ex2.Trailers.Count);
Assert.AreEqual("xyz", ex2.Trailers[0].Key);
Assert.AreEqual("xyz-value", ex2.Trailers[0].Value);
}
[Test]
public void UnaryCall_ServerHandlerSetsStatus()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
context.Status = new Status(StatusCode.Unauthenticated, "");
return Task.FromResult("");
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode);
Assert.AreEqual(0, ex.Trailers.Count);
var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode);
Assert.AreEqual(0, ex2.Trailers.Count);
}
[Test]
public void UnaryCall_StatusDebugErrorStringNotTransmittedFromServer()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
context.Status = new Status(StatusCode.Unauthenticated, "", new CoreErrorDetailException("this DebugErrorString value should not be transmitted to the client"));
return Task.FromResult("");
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode);
StringAssert.Contains("Error received from peer", ex.Status.DebugException.Message, "Is \"Error received from peer\" still a valid substring to search for in the client-generated error message from C-core?");
Assert.AreEqual(0, ex.Trailers.Count);
var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode);
StringAssert.Contains("Error received from peer", ex2.Status.DebugException.Message, "Is \"Error received from peer\" still a valid substring to search for in the client-generated error message from C-core?");
Assert.AreEqual(0, ex2.Trailers.Count);
}
[Test]
public void UnaryCall_ServerHandlerSetsStatusAndTrailers()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
context.Status = new Status(StatusCode.Unauthenticated, "");
context.ResponseTrailers.Add("xyz", "xyz-value");
return Task.FromResult("");
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex.Status.StatusCode);
Assert.AreEqual(1, ex.Trailers.Count);
Assert.AreEqual("xyz", ex.Trailers[0].Key);
Assert.AreEqual("xyz-value", ex.Trailers[0].Value);
var ex2 = Assert.ThrowsAsync<RpcException>(async () => await Calls.AsyncUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unauthenticated, ex2.Status.StatusCode);
Assert.AreEqual(1, ex2.Trailers.Count);
Assert.AreEqual("xyz", ex2.Trailers[0].Key);
Assert.AreEqual("xyz-value", ex2.Trailers[0].Value);
}
[Test]
public async Task ClientStreamingCall()
{
helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) =>
{
string result = "";
await requestStream.ForEachAsync((request) =>
{
result += request;
return TaskUtils.CompletedTask;
});
await Task.Delay(100);
return result;
});
{
var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall());
await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" });
Assert.AreEqual("ABC", await call);
Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode);
Assert.IsNotNull(call.GetTrailers());
}
{
var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall());
await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" });
Assert.AreEqual("ABC", await call.ConfigureAwait(false));
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.AreEqual("xyz", call.GetTrailers()[0].Key);
}
[Test]
public async Task ServerStreamingCall_EndOfStreamIsIdempotent()
{
helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>((request, responseStream, context) => TaskUtils.CompletedTask);
var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "");
Assert.IsFalse(await call.ResponseStream.MoveNext());
Assert.IsFalse(await call.ResponseStream.MoveNext());
}
[Test]
public void ServerStreamingCall_ErrorCanBeAwaitedTwice()
public async Task ServerStreamingCall_TrailersFromMultipleSourcesGetConcatenated()
{
helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>((request, responseStream, context) =>
{
context.Status = new Status(StatusCode.InvalidArgument, "");
return TaskUtils.CompletedTask;
// server streaming call hander simply ends the call by sending "InvalidArgument" status.
throw new RpcException(new Status(StatusCode.InvalidArgument, ""));
});
var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "");
// the invalid argument status code is received
var ex = Assert.ThrowsAsync<RpcException>(async () => await call.ResponseStream.MoveNext());
Assert.AreEqual(StatusCode.InvalidArgument, ex.Status.StatusCode);
// attempting MoveNext again should result in throwing the same exception.
var ex2 = Assert.ThrowsAsync<RpcException>(async () => await call.ResponseStream.MoveNext());
Assert.AreEqual(StatusCode.InvalidArgument, ex2.Status.StatusCode);
// sometimes, the empty response headers are nnot delivered to the client and the tests times out.
var responseHeaders = await call.ResponseHeadersAsync;
Console.Error.WriteLine("TEST DONE");
}
[Test]
public void ServerStreamingCall_TrailersFromMultipleSourcesGetConcatenated()
public void DuplexStreamingCall()
{
helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>((request, responseStream, context) =>
{
context.ResponseTrailers.Add("xyz", "xyz-value");
throw new RpcException(new Status(StatusCode.InvalidArgument, ""), new Metadata { {"abc", "abc-value"} });
});
var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "");
var ex = Assert.ThrowsAsync<RpcException>(async () => await call.ResponseStream.MoveNext());
Assert.AreEqual(StatusCode.InvalidArgument, ex.Status.StatusCode);
Assert.AreEqual(2, call.GetTrailers().Count);
Assert.AreEqual(2, ex.Trailers.Count);
Assert.AreEqual("xyz", ex.Trailers[0].Key);
Assert.AreEqual("xyz-value", ex.Trailers[0].Value);
Assert.AreEqual("abc", ex.Trailers[1].Key);
Assert.AreEqual("abc-value", ex.Trailers[1].Value);
}
[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.AreEqual("xyz-value", call.GetTrailers()[0].Value);
}
[Test]
public async Task AsyncUnaryCall_EchoMetadata()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
foreach (Metadata.Entry metadataEntry in context.RequestHeaders)
{
if (metadataEntry.Key != "user-agent")
{
context.ResponseTrailers.Add(metadataEntry);
}
}
return Task.FromResult("");
});
var headers = new Metadata
{
{ "ascii-header", "abcdefg" },
{ "binary-header-bin", new byte[] { 1, 2, 3, 0, 0xff } }
};
var call = Calls.AsyncUnaryCall(helper.CreateUnaryCall(new CallOptions(headers: headers)), "ABC");
await call;
// DUMMY TEST -> it only creates a client and a server via the Init() and Cleanup() methods.
// under the hood, that does grpc_init() and grpc_shutdown(), which may or may not be important.
Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode);
var trailers = call.GetTrailers();
Assert.AreEqual(2, trailers.Count);
Assert.AreEqual(headers[0].Key, trailers[0].Key);
Assert.AreEqual(headers[0].Value, trailers[0].Value);
Assert.AreEqual(headers[1].Key, trailers[1].Key);
CollectionAssert.AreEqual(headers[1].ValueBytes, trailers[1].ValueBytes);
}
[Test]
public void UnknownMethodHandler()
{
var nonexistentMethod = new Method<string, string>(
MethodType.Unary,
MockServiceHelper.ServiceName,
"NonExistentMethod",
Marshallers.StringMarshaller,
Marshallers.StringMarshaller);
var callDetails = new CallInvocationDetails<string, string>(channel, nonexistentMethod, new CallOptions());
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(callDetails, "abc"));
Assert.AreEqual(StatusCode.Unimplemented, ex.Status.StatusCode);
}
[Test]
public void StatusDetailIsUtf8()
{
// some japanese and chinese characters
var nonAsciiString = "\u30a1\u30a2\u30a3 \u62b5\u6297\u662f\u5f92\u52b3\u7684";
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
context.Status = new Status(StatusCode.Unknown, nonAsciiString);
return Task.FromResult("");
});
var ex = Assert.Throws<RpcException>(() => Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
Assert.AreEqual(StatusCode.Unknown, ex.Status.StatusCode);
Assert.AreEqual(nonAsciiString, ex.Status.Detail);
}
[Test]
public void ServerCallContext_PeerInfoPresent()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
return Task.FromResult(context.Peer);
});
string peer = Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc");
Assert.IsTrue(peer.Contains(Host));
}
[Test]
public void ServerCallContext_HostAndMethodPresent()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
Assert.IsTrue(context.Host.Contains(Host));
Assert.AreEqual("/tests.Test/Unary", context.Method);
return Task.FromResult("PASS");
});
Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
}
[Test]
public void ServerCallContext_AuthContextNotPopulated()
{
helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) =>
{
Assert.IsFalse(context.AuthContext.IsPeerAuthenticated);
// 1) security_level: TSI_SECURITY_NONE
// 2) transport_security_type: 'insecure'
Assert.AreEqual(2, context.AuthContext.Properties.Count());
return Task.FromResult("PASS");
});
Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"));
// if this method is commented out, can no longer reproduce.
}
}
}

@ -60,6 +60,7 @@ os.chdir(_ROOT)
_FORCE_ENVIRON_FOR_WRAPPERS = {
'GRPC_VERBOSITY': 'DEBUG',
'GRPC_TRACE': 'api',
}
_POLLING_STRATEGIES = {
@ -72,7 +73,7 @@ def platform_string():
return jobset.platform_string()
_DEFAULT_TIMEOUT_SECONDS = 5 * 60
_DEFAULT_TIMEOUT_SECONDS = 60
_PRE_BUILD_STEP_TIMEOUT_SECONDS = 10 * 60

Loading…
Cancel
Save