From c75c57c5af620491d0043047533fa0e2f078b09f Mon Sep 17 00:00:00 2001
From: Jan Tattermusch <jtattermusch@google.com>
Date: Fri, 7 Aug 2015 22:07:40 -0700
Subject: [PATCH] added ResponseHeadersTest, fixed stylecop issues

---
 .../Grpc.Core.Tests/ClientServerTest.cs       |   3 +-
 .../Grpc.Core.Tests/Grpc.Core.Tests.csproj    |   1 +
 .../Grpc.Core.Tests/MockServiceHelper.cs      |   4 +
 .../Grpc.Core.Tests/ResponseHeadersTest.cs    | 139 ++++++++++++++++++
 src/csharp/Grpc.Core.Tests/TimeoutsTest.cs    |  15 +-
 src/csharp/Grpc.Core/Internal/AsyncCall.cs    |   2 +-
 .../Grpc.Core/Internal/AsyncCallServer.cs     |   3 +-
 src/csharp/Grpc.Core/WriteOptions.cs          |   1 -
 8 files changed, 159 insertions(+), 9 deletions(-)
 create mode 100644 src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs

diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs
index 08c80bbe53..f56fb744a6 100644
--- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs
+++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs
@@ -217,7 +217,8 @@ namespace Grpc.Core.Tests
         [Test]
         public void UnaryCallPerformance()
         {
-            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => {
+            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
+            {
                 return request;
             });
 
diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
index 55d0c98d44..4692d958a0 100644
--- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
+++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj
@@ -78,6 +78,7 @@
     <Compile Include="NUnitVersionTest.cs" />
     <Compile Include="ChannelTest.cs" />
     <Compile Include="MockServiceHelper.cs" />
+    <Compile Include="ResponseHeadersTest.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup>
diff --git a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs
index 25afa30bba..b642286b11 100644
--- a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs
+++ b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs
@@ -199,6 +199,7 @@ namespace Grpc.Core.Tests
             {
                 return this.unaryHandler;
             }
+
             set
             {
                 unaryHandler = value;
@@ -211,6 +212,7 @@ namespace Grpc.Core.Tests
             {
                 return this.clientStreamingHandler;
             }
+
             set
             {
                 clientStreamingHandler = value;
@@ -223,6 +225,7 @@ namespace Grpc.Core.Tests
             {
                 return this.serverStreamingHandler;
             }
+
             set
             {
                 serverStreamingHandler = value;
@@ -235,6 +238,7 @@ namespace Grpc.Core.Tests
             {
                 return this.duplexStreamingHandler;
             }
+
             set
             {
                 duplexStreamingHandler = value;
diff --git a/src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs b/src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs
new file mode 100644
index 0000000000..b024488549
--- /dev/null
+++ b/src/csharp/Grpc.Core.Tests/ResponseHeadersTest.cs
@@ -0,0 +1,139 @@
+#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.Diagnostics;
+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
+{
+    /// <summary>
+    /// Tests for response headers support.
+    /// </summary>
+    public class ResponseHeadersTest
+    {
+        MockServiceHelper helper;
+        Server server;
+        Channel channel;
+
+        Metadata headers;
+
+        [SetUp]
+        public void Init()
+        {
+            helper = new MockServiceHelper();
+
+            server = helper.GetServer();
+            server.Start();
+            channel = helper.GetChannel();
+
+            headers = new Metadata
+            {
+                new Metadata.Entry("ascii-header", "abcdefg"),
+            };
+        }
+
+        [TearDown]
+        public void Cleanup()
+        {
+            channel.Dispose();
+            server.ShutdownAsync().Wait();
+        }
+
+        [TestFixtureTearDown]
+        public void CleanupClass()
+        {
+            GrpcEnvironment.Shutdown();
+        }
+
+        [Test]
+        public void WriteResponseHeaders_NullNotAllowed()
+        {
+            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
+            {
+                Assert.Throws(typeof(NullReferenceException), async () => await context.WriteResponseHeadersAsync(null));
+                return "PASS";
+            });
+
+            Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), ""));
+        }
+
+        [Test]
+        public void WriteResponseHeaders_AllowedOnlyOnce()
+        {
+            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
+            {
+                await context.WriteResponseHeadersAsync(headers);
+                try
+                {
+                    await context.WriteResponseHeadersAsync(headers);
+                    Assert.Fail();
+                }
+                catch (InvalidOperationException expected)
+                {
+                }
+                return "PASS";
+            });
+                
+            Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), ""));
+        }
+
+        [Test]
+        public async Task WriteResponseHeaders_NotAllowedAfterWrite()
+        {
+            helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>(async (request, responseStream, context) =>
+            {
+                await responseStream.WriteAsync("A");
+                try
+                {
+                    await context.WriteResponseHeadersAsync(headers);
+                    Assert.Fail();
+                }
+                catch (InvalidOperationException expected)
+                {
+                }
+                await responseStream.WriteAsync("B");
+            });
+
+            var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "");
+            var responses = await call.ResponseStream.ToList();
+            CollectionAssert.AreEqual(new[] { "A", "B" }, responses);
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs
index ead0b1854b..d875d601b9 100644
--- a/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs
+++ b/src/csharp/Grpc.Core.Tests/TimeoutsTest.cs
@@ -78,7 +78,8 @@ namespace Grpc.Core.Tests
         [Test]
         public void InfiniteDeadline()
         {
-            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => {
+            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
+            {
                 Assert.AreEqual(DateTime.MaxValue, context.Deadline);
                 return "PASS";
             });
@@ -95,7 +96,8 @@ namespace Grpc.Core.Tests
         {
             var clientDeadline = DateTime.UtcNow + TimeSpan.FromDays(7);
 
-            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => {
+            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
+            {
                 // A fairly relaxed check that the deadline set by client and deadline seen by server
                 // are in agreement. C core takes care of the work with transferring deadline over the wire,
                 // so we don't need an exact check here.
@@ -108,7 +110,8 @@ namespace Grpc.Core.Tests
         [Test]
         public void DeadlineInThePast()
         {
-            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => {
+            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
+            {
                 await Task.Delay(60000);
                 return "FAIL";
             });
@@ -121,7 +124,8 @@ namespace Grpc.Core.Tests
         [Test]
         public void DeadlineExceededStatusOnTimeout()
         {
-            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => {
+            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) =>
+            {
                 await Task.Delay(60000);
                 return "FAIL";
             });
@@ -136,7 +140,8 @@ namespace Grpc.Core.Tests
         {
             var serverReceivedCancellationTcs = new TaskCompletionSource<bool>();
 
-            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => {
+            helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => 
+            {
                 // wait until cancellation token is fired.
                 var tcs = new TaskCompletionSource<object>();
                 context.CancellationToken.Register(() => { tcs.SetResult(null); });
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
index c8c2449ee6..df5c07e4c4 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs
@@ -279,7 +279,7 @@ namespace Grpc.Core.Internal
             }
         }
 
-        public CallInvocationDetails<TRequest, TResponse>  Details
+        public CallInvocationDetails<TRequest, TResponse> Details
         {
             get
             {
diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
index 9eac7f7b61..1704b9afbf 100644
--- a/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
+++ b/src/csharp/Grpc.Core/Internal/AsyncCallServer.cs
@@ -107,10 +107,11 @@ namespace Grpc.Core.Internal
         {
             lock (myLock)
             {
+                Preconditions.CheckNotNull(headers, "metadata");
                 Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null");
 
                 Preconditions.CheckState(!initialMetadataSent, "Response headers can only be sent once per call.");
-                Preconditions.CheckState(streamingWritesCounter > 0, "Response headers can only be sent before the first write starts.");
+                Preconditions.CheckState(streamingWritesCounter == 0, "Response headers can only be sent before the first write starts.");
                 CheckSendingAllowed();
 
                 Preconditions.CheckNotNull(completionDelegate, "Completion delegate cannot be null");
diff --git a/src/csharp/Grpc.Core/WriteOptions.cs b/src/csharp/Grpc.Core/WriteOptions.cs
index ec4a7dd8cd..7ef3189d76 100644
--- a/src/csharp/Grpc.Core/WriteOptions.cs
+++ b/src/csharp/Grpc.Core/WriteOptions.cs
@@ -54,7 +54,6 @@ namespace Grpc.Core
         NoCompress = 0x2
     }
 
-
     /// <summary>
     /// Options for write operations.
     /// </summary>
-- 
GitLab