@ -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 ( 1 0 0 ) ;
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.
}
}
}