Add more Grpc.Tools integration tests (#32311)

Further Grpc.Tools tests
- TestCharactersInName - proto files with dots and numbers in their
names
- TestExtraOptions - test setting AdditionalProtocArguments,
OutputOptions and GrpcOutputOptions
- TestGrpcServices - test setting GrpcServices to "none", "client",
"server" and "both"
- TestSetOutputDirs - test setting OutputDir and GrpcOutputDir
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestCharactersInName/Program.cs b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestCharactersInName/Program.cs
new file mode 100644
index 0000000..15e3506
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestCharactersInName/Program.cs
@@ -0,0 +1,30 @@
+#region Copyright notice and license
+
+// Copyright 2023 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;
+
+namespace TestApp
+{
+    public class Program
+    {
+        public static void Main(string[] args)
+        {
+            Console.WriteLine("test app");
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestCharactersInName/expected.json b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestCharactersInName/expected.json
new file mode 100644
index 0000000..50dc354
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestCharactersInName/expected.json
@@ -0,0 +1,55 @@
+{
+    "Files": {
+        "protos/hello.world.proto": {
+            "--csharp_out": [
+                "${TEST_OUT_DIR}/obj/Debug/netstandard2.0/protos"
+            ],
+            "--plugin": [
+                "protoc-gen-grpc=dummy-plugin-not-used"
+            ],
+            "--grpc_out": [
+                "${TEST_OUT_DIR}/obj/Debug/netstandard2.0/protos"
+            ],
+            "--proto_path": [
+                "../../../Grpc.Tools/build/native/include",
+                "."
+            ],
+            "--dependency_out": [
+                "REGEX:${TEST_OUT_DIR}/obj/Debug/netstandard2.0/.*_hello.world.protodep"
+            ],
+            "--error_format": [
+                "msvs"
+            ],
+            "protofile": [
+                "protos/hello.world.proto"
+            ]
+        },
+        "protos/m_double_2d.proto": {
+            "--csharp_out": [
+                "${TEST_OUT_DIR}/obj/Debug/netstandard2.0/protos"
+            ],
+            "--plugin": [
+                "protoc-gen-grpc=dummy-plugin-not-used"
+            ],
+            "--grpc_out": [
+                "${TEST_OUT_DIR}/obj/Debug/netstandard2.0/protos"
+            ],
+            "--proto_path": [
+                "../../../Grpc.Tools/build/native/include",
+                "."
+            ],
+            "--dependency_out": [
+                "REGEX:${TEST_OUT_DIR}/obj/Debug/netstandard2.0/.*_m_double_2d.protodep"
+            ],
+            "--error_format": [
+                "msvs"
+            ],
+            "protofile": [
+                "protos/m_double_2d.proto"
+            ]
+        }
+    },
+    "Metadata": {
+        "timestamp": "IGNORE:"
+    }
+}
\ No newline at end of file
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestCharactersInName/msbuildtest.csproj b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestCharactersInName/msbuildtest.csproj
new file mode 100644
index 0000000..4cb726a
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestCharactersInName/msbuildtest.csproj
@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <!--
+     Note: common properties and targets for tests are imported from the
+     Directory.Build.props and Directory.Build.targets files in the parent directoty
+  -->
+
+  <!-- The protobuf compiler settings to test -->
+  <ItemGroup>
+    <Protobuf Include="**/*.proto" />
+  </ItemGroup>
+  
+</Project>
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestCharactersInName/protos/hello.world.proto b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestCharactersInName/protos/hello.world.proto
new file mode 100644
index 0000000..104d370
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestCharactersInName/protos/hello.world.proto
@@ -0,0 +1,35 @@
+// Copyright 2022 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.
+
+syntax = "proto3";
+
+package grpc_tools_tests.integration_tests.test_characters_in_name_dot;
+
+// The greeting service definition.
+service Greeter {
+  // Sends a greeting
+  rpc SayHello (HelloRequest) returns (HelloReply) {}
+  rpc SayHelloSlowly (HelloRequest) returns (HelloReply) {}
+}
+
+// The request message containing the user's name.
+message HelloRequest {
+  string name = 1;
+  int32 delay = 2;
+}
+
+// The response message containing the greetings
+message HelloReply {
+  string message = 1;
+}
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestCharactersInName/protos/m_double_2d.proto b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestCharactersInName/protos/m_double_2d.proto
new file mode 100644
index 0000000..e07d30c
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestCharactersInName/protos/m_double_2d.proto
@@ -0,0 +1,27 @@
+// Copyright 2023 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.
+
+syntax = "proto3";
+
+package grpc_tools_tests.integration_tests.test_characters_in_name_numbers;
+
+message MDouble2D
+{
+  double x = 1;
+  double y = 2;
+}
+
+service MyService {
+  rpc MyFunction (MDouble2D) returns (MDouble2D) {}
+}
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestExtraOptions/Program.cs b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestExtraOptions/Program.cs
new file mode 100644
index 0000000..f1d9726
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestExtraOptions/Program.cs
@@ -0,0 +1,30 @@
+#region Copyright notice and license
+
+// Copyright 2022 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;
+
+namespace TestApp
+{
+    public class Program
+    {
+        public static void Main(string[] args)
+        {
+            Console.WriteLine("test app");
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestExtraOptions/expected.json b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestExtraOptions/expected.json
new file mode 100644
index 0000000..323368c
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestExtraOptions/expected.json
@@ -0,0 +1,41 @@
+{
+    "Files": {
+        "file.proto": {
+            "--csharp_out": [
+                "${TEST_OUT_DIR}/obj/Debug/netstandard2.0"
+            ],
+            "--csharp_opt": [
+                "--outOpt1=foo,--outOpt2=bar"
+            ],
+            "--plugin": [
+                "protoc-gen-grpc=dummy-plugin-not-used",
+                "REGEX:protoc-gen-myplugin=.*myplugin.exe"
+            ],
+            "--grpc_out": [
+                "${TEST_OUT_DIR}/obj/Debug/netstandard2.0"
+            ],
+            "--grpc_opt": [
+                "--grpcOpt1=one,--grpcOpt2=two,--grpcOpt3=three"
+            ],
+            "--proto_path": [
+                "../../../Grpc.Tools/build/native/include",
+                "."
+            ],
+            "--dependency_out": [
+                "REGEX:${TEST_OUT_DIR}/obj/Debug/netstandard2.0/.*_file.protodep"
+            ],
+            "--error_format": [
+                "msvs"
+            ],
+            "--myplugin_out": [
+                "."
+            ],
+            "protofile": [
+                "file.proto"
+            ]
+        }
+    },
+    "Metadata": {
+        "timestamp": "IGNORE:"
+    }
+}
\ No newline at end of file
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestExtraOptions/file.proto b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestExtraOptions/file.proto
new file mode 100644
index 0000000..650ee04
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestExtraOptions/file.proto
@@ -0,0 +1,35 @@
+// Copyright 202 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.
+
+syntax = "proto3";
+
+package grpc_tools_tests.integration_tests.test_extra_options;
+
+// The greeting service definition.
+service Greeter {
+  // Sends a greeting
+  rpc SayHello (HelloRequest) returns (HelloReply) {}
+  rpc SayHelloSlowly (HelloRequest) returns (HelloReply) {}
+}
+
+// The request message containing the user's name.
+message HelloRequest {
+  string name = 1;
+  int32 delay = 2;
+}
+
+// The response message containing the greetings
+message HelloReply {
+  string message = 1;
+}
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestExtraOptions/msbuildtest.csproj b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestExtraOptions/msbuildtest.csproj
new file mode 100644
index 0000000..7d3ac23
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestExtraOptions/msbuildtest.csproj
@@ -0,0 +1,18 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <!--
+     Note: common properties and targets for tests are imported from the
+     Directory.Build.props and Directory.Build.targets files in the parent directoty
+  -->
+
+  <!-- The protobuf compiler settings to test -->
+  <!-- Test various optional options are passed to protoc and plugin -->
+  <ItemGroup>
+    <Protobuf Include="file.proto" 
+      AdditionalProtocArguments="--plugin=protoc-gen-myplugin=D:\myplugin.exe;--myplugin_out=."
+      OutputOptions="--outOpt1=foo;--outOpt2=bar"
+      GrpcOutputOptions="--grpcOpt1=one;--grpcOpt2=two;--grpcOpt3=three"
+    />
+  </ItemGroup>
+  
+</Project>
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/Program.cs b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/Program.cs
new file mode 100644
index 0000000..f1d9726
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/Program.cs
@@ -0,0 +1,30 @@
+#region Copyright notice and license
+
+// Copyright 2022 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;
+
+namespace TestApp
+{
+    public class Program
+    {
+        public static void Main(string[] args)
+        {
+            Console.WriteLine("test app");
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/clientandserver.proto b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/clientandserver.proto
new file mode 100644
index 0000000..457d7cf
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/clientandserver.proto
@@ -0,0 +1,26 @@
+// Copyright 2023 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.
+
+syntax = "proto3";
+
+package grpc_tools_tests.integration_tests.test_grpc_services;
+
+import "messages.proto";
+
+// For testing client and server code generation.
+service ClientAndServer {
+  // Sends a greeting
+  rpc SayHello (HelloRequest) returns (HelloReply) {}
+  rpc SayHelloSlowly (HelloRequest) returns (HelloReply) {}
+}
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/clientonly.proto b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/clientonly.proto
new file mode 100644
index 0000000..b056805
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/clientonly.proto
@@ -0,0 +1,26 @@
+// Copyright 2023 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.
+
+syntax = "proto3";
+
+package grpc_tools_tests.integration_tests.test_grpc_services;
+
+import "messages.proto";
+
+// For testing client only code generation.
+service ClientOnly {
+  // Sends a greeting
+  rpc SayHello (HelloRequest) returns (HelloReply) {}
+  rpc SayHelloSlowly (HelloRequest) returns (HelloReply) {}
+}
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/expected.json b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/expected.json
new file mode 100644
index 0000000..25d2e0b
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/expected.json
@@ -0,0 +1,103 @@
+{
+    "Files": {
+        "messages.proto": {
+            "--csharp_out": [
+                "${TEST_OUT_DIR}/obj/Debug/netstandard2.0"
+            ],
+            "--proto_path": [
+                "../../../Grpc.Tools/build/native/include",
+                "."
+            ],
+            "--dependency_out": [
+                "REGEX:${TEST_OUT_DIR}/obj/Debug/netstandard2.0/.*_messages.protodep"
+            ],
+            "--error_format": [
+                "msvs"
+            ],
+            "protofile": [
+                "messages.proto"
+            ]
+        },
+        "serveronly.proto": {
+            "--csharp_out": [
+                "${TEST_OUT_DIR}/obj/Debug/netstandard2.0"
+            ],
+            "--plugin": [
+                "protoc-gen-grpc=dummy-plugin-not-used"
+            ],
+            "--grpc_out": [
+                "${TEST_OUT_DIR}/obj/Debug/netstandard2.0"
+            ],
+            "--grpc_opt": [
+                "no_client"
+            ],
+            "--proto_path": [
+                "../../../Grpc.Tools/build/native/include",
+                "."
+            ],
+            "--dependency_out": [
+                "REGEX:${TEST_OUT_DIR}/obj/Debug/netstandard2.0/.*_serveronly.protodep"
+            ],
+            "--error_format": [
+                "msvs"
+            ],
+            "protofile": [
+                "serveronly.proto"
+            ]
+        },
+        "clientonly.proto": {
+            "--csharp_out": [
+                "${TEST_OUT_DIR}/obj/Debug/netstandard2.0"
+            ],
+            "--plugin": [
+                "protoc-gen-grpc=dummy-plugin-not-used"
+            ],
+            "--grpc_out": [
+                "${TEST_OUT_DIR}/obj/Debug/netstandard2.0"
+            ],
+            "--grpc_opt": [
+                "no_server"
+            ],
+            "--proto_path": [
+                "../../../Grpc.Tools/build/native/include",
+                "."
+            ],
+            "--dependency_out": [
+                "REGEX:${TEST_OUT_DIR}/obj/Debug/netstandard2.0/.*_clientonly.protodep"
+            ],
+            "--error_format": [
+                "msvs"
+            ],
+            "protofile": [
+                "clientonly.proto"
+            ]
+        },
+        "clientandserver.proto": {
+            "--csharp_out": [
+                "${TEST_OUT_DIR}/obj/Debug/netstandard2.0"
+            ],
+            "--plugin": [
+                "protoc-gen-grpc=dummy-plugin-not-used"
+            ],
+            "--grpc_out": [
+                "${TEST_OUT_DIR}/obj/Debug/netstandard2.0"
+            ],
+            "--proto_path": [
+                "../../../Grpc.Tools/build/native/include",
+                "."
+            ],
+            "--dependency_out": [
+                "REGEX:${TEST_OUT_DIR}/obj/Debug/netstandard2.0/.*_clientandserver.protodep"
+            ],
+            "--error_format": [
+                "msvs"
+            ],
+            "protofile": [
+                "clientandserver.proto"
+            ]
+        }
+    },
+    "Metadata": {
+        "timestamp": "IGNORE:"
+    }
+}
\ No newline at end of file
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/messages.proto b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/messages.proto
new file mode 100644
index 0000000..a8f23f3
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/messages.proto
@@ -0,0 +1,30 @@
+// Copyright 2023 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.
+
+syntax = "proto3";
+
+package grpc_tools_tests.integration_tests.test_grpc_services;
+
+// No service defintions in this file
+
+// The request message containing the user's name.
+message HelloRequest {
+  string name = 1;
+  int32 delay = 2;
+}
+
+// The response message containing the greetings
+message HelloReply {
+  string message = 1;
+}
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/msbuildtest.csproj b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/msbuildtest.csproj
new file mode 100644
index 0000000..370e5e8
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/msbuildtest.csproj
@@ -0,0 +1,16 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <!--
+     Note: common properties and targets for tests are imported from the
+     Directory.Build.props and Directory.Build.targets files in the parent directoty
+  -->
+
+  <!-- The protobuf compiler settings to test -->
+  <ItemGroup>
+    <Protobuf Include="messages.proto" GrpcServices="none" />
+    <Protobuf Include="serveronly.proto" GrpcServices="server" />
+    <Protobuf Include="clientonly.proto" GrpcServices="client" />
+    <Protobuf Include="clientandserver.proto" GrpcServices="both" />
+  </ItemGroup>
+  
+</Project>
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/serveronly.proto b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/serveronly.proto
new file mode 100644
index 0000000..d7ada7d
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestGrpcServicesMetadata/serveronly.proto
@@ -0,0 +1,26 @@
+// Copyright 2023 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.
+
+syntax = "proto3";
+
+package grpc_tools_tests.integration_tests.test_grpc_services;
+
+import "messages.proto";
+
+// For testing server only code generation.
+service ServerOnly {
+  // Sends a greeting
+  rpc SayHello (HelloRequest) returns (HelloReply) {}
+  rpc SayHelloSlowly (HelloRequest) returns (HelloReply) {}
+}
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestSetOutputDirs/Program.cs b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestSetOutputDirs/Program.cs
new file mode 100644
index 0000000..f1d9726
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestSetOutputDirs/Program.cs
@@ -0,0 +1,30 @@
+#region Copyright notice and license
+
+// Copyright 2022 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;
+
+namespace TestApp
+{
+    public class Program
+    {
+        public static void Main(string[] args)
+        {
+            Console.WriteLine("test app");
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestSetOutputDirs/expected.json b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestSetOutputDirs/expected.json
new file mode 100644
index 0000000..b732a6a
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestSetOutputDirs/expected.json
@@ -0,0 +1,31 @@
+{
+    "Files": {
+        "file.proto": {
+            "--csharp_out": [
+                "${TEST_OUT_DIR}/myOutput"
+            ],
+            "--plugin": [
+                "protoc-gen-grpc=dummy-plugin-not-used"
+            ],
+            "--grpc_out": [
+                "${TEST_OUT_DIR}/myGrpcOutput"
+            ],
+            "--proto_path": [
+                "../../../Grpc.Tools/build/native/include",
+                "."
+            ],
+            "--dependency_out": [
+                "REGEX:${TEST_OUT_DIR}/obj/Debug/netstandard2.0/.*_file.protodep"
+            ],
+            "--error_format": [
+                "msvs"
+            ],
+            "protofile": [
+                "file.proto"
+            ]
+        }
+    },
+    "Metadata": {
+        "timestamp": "IGNORE:"
+    }
+}
\ No newline at end of file
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestSetOutputDirs/file.proto b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestSetOutputDirs/file.proto
new file mode 100644
index 0000000..4d4cfa2
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestSetOutputDirs/file.proto
@@ -0,0 +1,35 @@
+// Copyright 2023 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.
+
+syntax = "proto3";
+
+package grpc_tools_tests.integration_tests.test_set_output_dirs;
+
+// The greeting service definition.
+service Greeter {
+  // Sends a greeting
+  rpc SayHello (HelloRequest) returns (HelloReply) {}
+  rpc SayHelloSlowly (HelloRequest) returns (HelloReply) {}
+}
+
+// The request message containing the user's name.
+message HelloRequest {
+  string name = 1;
+  int32 delay = 2;
+}
+
+// The response message containing the greetings
+message HelloReply {
+  string message = 1;
+}
diff --git a/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestSetOutputDirs/msbuildtest.csproj b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestSetOutputDirs/msbuildtest.csproj
new file mode 100644
index 0000000..c1fdebc
--- /dev/null
+++ b/src/csharp/Grpc.Tools.Tests/IntegrationTests/TestSetOutputDirs/msbuildtest.csproj
@@ -0,0 +1,15 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <!--
+     Note: common properties and targets for tests are imported from the
+     Directory.Build.props and Directory.Build.targets files in the parent directoty
+  -->
+
+  <!-- The protobuf compiler settings to test -->
+  <ItemGroup>
+    <Protobuf Include="**/*.proto" 
+      OutputDir="$(TestOutDir)\myOutput"
+      GrpcOutputDir="$(TestOutDir)\myGrpcOutput" />
+  </ItemGroup>
+  
+</Project>
diff --git a/src/csharp/Grpc.Tools.Tests/MsBuildIntegrationTest.cs b/src/csharp/Grpc.Tools.Tests/MsBuildIntegrationTest.cs
index bca480e..465a2a9 100644
--- a/src/csharp/Grpc.Tools.Tests/MsBuildIntegrationTest.cs
+++ b/src/csharp/Grpc.Tools.Tests/MsBuildIntegrationTest.cs
@@ -126,6 +126,61 @@
             TryRunMsBuild("TestProtoOutsideProject/project", expectedFiles.ToString());
         }
 
+        [Test]
+        public void TestCharactersInName()
+        {
+            // see https://github.com/grpc/grpc/issues/17661 - dot in name
+            // and https://github.com/grpc/grpc/issues/18698 - numbers in name
+            SetUpForTest(nameof(TestCharactersInName));
+
+            var expectedFiles = new ExpectedFilesBuilder();
+            expectedFiles.Add("protos/hello.world.proto", "HelloWorld.cs", "Hello.worldGrpc.cs");
+            expectedFiles.Add("protos/m_double_2d.proto", "MDouble2D.cs", "MDouble2dGrpc.cs");
+
+            TryRunMsBuild("TestCharactersInName", expectedFiles.ToString());
+        }
+
+        [Test]
+        public void TestExtraOptions()
+        {
+            // Test various extra options passed to protoc and plugin
+            // See https://github.com/grpc/grpc/issues/25950
+            // Tests setting AdditionalProtocArguments, OutputOptions and GrpcOutputOptions
+            SetUpForTest(nameof(TestExtraOptions));
+
+            var expectedFiles = new ExpectedFilesBuilder();
+            expectedFiles.Add("file.proto", "File.cs", "FileGrpc.cs");
+
+            TryRunMsBuild("TestExtraOptions", expectedFiles.ToString());
+        }
+
+        [Test]
+        public void TestGrpcServicesMetadata()
+        {
+            // Test different values for GrpcServices item metadata
+            SetUpForTest(nameof(TestGrpcServicesMetadata));
+
+            var expectedFiles = new ExpectedFilesBuilder();
+            expectedFiles.Add("messages.proto", "Messages.cs");
+            expectedFiles.Add("serveronly.proto", "Serveronly.cs", "ServeronlyGrpc.cs");
+            expectedFiles.Add("clientonly.proto", "Clientonly.cs", "ClientonlyGrpc.cs");
+            expectedFiles.Add("clientandserver.proto", "Clientandserver.cs", "ClientandserverGrpc.cs");
+
+            TryRunMsBuild("TestGrpcServicesMetadata", expectedFiles.ToString());
+        }
+
+        [Test]
+        public void TestSetOutputDirs()
+        {
+            // Test setting different GrpcOutputDir and OutputDir
+            SetUpForTest(nameof(TestSetOutputDirs));
+
+            var expectedFiles = new ExpectedFilesBuilder();
+            expectedFiles.Add("file.proto", "File.cs", "FileGrpc.cs");
+
+            TryRunMsBuild("TestSetOutputDirs", expectedFiles.ToString());
+        }
+
         /// <summary>
         /// Set up common paths for all the tests
         /// </summary>
diff --git a/src/csharp/Grpc.Tools.Tests/scripts/fakeprotoc.py b/src/csharp/Grpc.Tools.Tests/scripts/fakeprotoc.py
index 78c42d3..29fb9f0 100755
--- a/src/csharp/Grpc.Tools.Tests/scripts/fakeprotoc.py
+++ b/src/csharp/Grpc.Tools.Tests/scripts/fakeprotoc.py
@@ -220,7 +220,15 @@
     return cs_files_to_generate
 
 
-def _generate_cs_files(protofile, cs_files_to_generate, out_dir, projectdir):
+def _is_grpc_out_file(csfile):
+    """Return true if the file is one that would be generated by gRPC plugin"""
+    # This is using the heuristics of checking that the name of the file
+    # matches *Grpc.cs which is the name that the gRPC plugin would produce.
+    return csfile.endswith("Grpc.cs")
+
+
+def _generate_cs_files(protofile, cs_files_to_generate, grpc_out_dir,
+                       csharp_out_dir, projectdir):
     """Create expected cs files."""
     _write_debug("\ngenerate_cs_files")
 
@@ -228,24 +236,34 @@
         _write_debug("No .cs files matching proto file name %s" % protofile)
         return
 
-    if not os.path.isabs(out_dir):
+    if not os.path.isabs(grpc_out_dir):
         # if not absolute, it is relative to project directory
-        out_dir = os.path.abspath("%s/%s" % (projectdir, out_dir))
+        grpc_out_dir = os.path.abspath("%s/%s" % (projectdir, grpc_out_dir))
 
-    # Ensure out_dir exists
-    if not os.path.isdir(out_dir):
-        os.makedirs(out_dir)
+    if not os.path.isabs(csharp_out_dir):
+        # if not absolute, it is relative to project directory
+        csharp_out_dir = os.path.abspath("%s/%s" % (projectdir, csharp_out_dir))
+
+    # Ensure directories exist
+    if not os.path.isdir(grpc_out_dir):
+        os.makedirs(grpc_out_dir)
+
+    if not os.path.isdir(csharp_out_dir):
+        os.makedirs(csharp_out_dir)
 
     timestamp = str(datetime.datetime.now())
     for csfile in cs_files_to_generate:
-        csfile_fullpath = "%s/%s" % (out_dir, csfile)
+        if csfile.endswith("Grpc.cs"):
+            csfile_fullpath = "%s/%s" % (grpc_out_dir, csfile)
+        else:
+            csfile_fullpath = "%s/%s" % (csharp_out_dir, csfile)
         _write_debug("Creating: %s" % csfile_fullpath)
         with open(csfile_fullpath, "w") as fout:
             print("// Generated by fake protoc: %s" % timestamp, file=fout)
 
 
 def _create_dependency_file(protofile, cs_files_to_generate, dependencyfile,
-                            grpcout):
+                            grpc_out_dir, csharp_out_dir):
     """Create the expected dependency file."""
     _write_debug("\ncreate_dependency_file")
 
@@ -261,7 +279,11 @@
     with open(dependencyfile, "w") as out:
         nfiles = len(cs_files_to_generate)
         for i in range(0, nfiles):
-            cs_filename = os.path.join(grpcout, cs_files_to_generate[i])
+            csfile = cs_files_to_generate[i]
+            if csfile.endswith("Grpc.cs"):
+                cs_filename = os.path.join(grpc_out_dir, csfile)
+            else:
+                cs_filename = os.path.join(csharp_out_dir, csfile)
             if i == nfiles - 1:
                 print("%s: %s" % (cs_filename, protofile), file=out)
             else:
@@ -278,6 +300,15 @@
     return value
 
 
+def _get_argument_last_occurrence_or_none(protoc_arg_dict, name):
+    # If argument was passed multiple times, take the last occurrence.
+    # If the value does not exist then return None
+    values = protoc_arg_dict.get(name)
+    if values is not None:
+        return values[-1]
+    return None
+
+
 def main():
     # Check environment variables for the additional arguments used in the tests.
 
@@ -317,8 +348,16 @@
 
     # If argument was passed multiple times, take the last occurrence of it.
     # TODO(jtattermusch): handle multiple occurrences of the same argument
-    dependencyfile = protoc_arg_dict.get('--dependency_out')[-1]
-    grpcout = protoc_arg_dict.get('--grpc_out')[-1]
+    dependencyfile = _get_argument_last_occurrence_or_none(
+        protoc_arg_dict, '--dependency_out')
+    grpcout = _get_argument_last_occurrence_or_none(protoc_arg_dict,
+                                                    '--grpc_out')
+    csharpout = _get_argument_last_occurrence_or_none(protoc_arg_dict,
+                                                      '--csharp_out')
+
+    # --grpc_out might not be set in which case use --csharp_out
+    if grpcout is None:
+        grpcout = csharpout
 
     if len(protoc_arg_dict.get('protofile')) != 1:
         # regular protoc can process multiple .proto files passed at once, but we know
@@ -335,11 +374,13 @@
     _create_dependency_file(protofile=protofile,
                             cs_files_to_generate=cs_files_to_generate,
                             dependencyfile=dependencyfile,
-                            grpcout=grpcout)
+                            grpc_out_dir=grpcout,
+                            csharp_out_dir=csharpout)
 
     _generate_cs_files(protofile=protofile,
                        cs_files_to_generate=cs_files_to_generate,
-                       out_dir=grpcout,
+                       grpc_out_dir=grpcout,
+                       csharp_out_dir=csharpout,
                        projectdir=projectdir)
 
     _write_or_update_results_json(log_dir=log_dir,